summaryrefslogtreecommitdiff
path: root/arch/arm
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm')
-rw-r--r--arch/arm/Kconfig33
-rw-r--r--arch/arm/boot/compressed/head.S3
-rw-r--r--arch/arm/common/Kconfig50
-rw-r--r--arch/arm/common/Makefile2
-rw-r--r--arch/arm/common/fiq_debugger.c1196
-rw-r--r--arch/arm/common/fiq_debugger_ringbuf.h94
-rw-r--r--arch/arm/common/fiq_glue.S111
-rw-r--r--arch/arm/common/fiq_glue_setup.c155
-rw-r--r--arch/arm/common/gic.c192
-rw-r--r--arch/arm/configs/tegra3_android_defconfig412
-rw-r--r--arch/arm/configs/tegra3_defconfig277
-rw-r--r--arch/arm/configs/tegra_android_defconfig372
-rw-r--r--arch/arm/configs/tegra_aruba2_android_defconfig311
-rw-r--r--arch/arm/configs/tegra_defconfig173
-rw-r--r--arch/arm/configs/tegra_p852_gnu_linux_defconfig303
-rw-r--r--arch/arm/include/asm/cacheflush.h51
-rw-r--r--arch/arm/include/asm/cpu_pm.h123
-rw-r--r--arch/arm/include/asm/delay.h4
-rw-r--r--arch/arm/include/asm/elf.h1
-rw-r--r--arch/arm/include/asm/fiq_debugger.h64
-rw-r--r--arch/arm/include/asm/fiq_glue.h30
-rw-r--r--arch/arm/include/asm/hardirq.h2
-rw-r--r--arch/arm/include/asm/hardware/cache-l2x0.h6
-rw-r--r--arch/arm/include/asm/hardware/coresight.h26
-rw-r--r--arch/arm/include/asm/hardware/gic.h9
-rw-r--r--arch/arm/include/asm/irq.h3
-rw-r--r--arch/arm/include/asm/mach/mmc.h28
-rw-r--r--arch/arm/include/asm/page.h2
-rw-r--r--arch/arm/include/asm/pgalloc.h4
-rw-r--r--arch/arm/include/asm/pgtable.h25
-rw-r--r--arch/arm/include/asm/smp.h2
-rw-r--r--arch/arm/kernel/Makefile1
-rw-r--r--arch/arm/kernel/asm-offsets.c3
-rw-r--r--arch/arm/kernel/cpu_pm.c116
-rw-r--r--arch/arm/kernel/debug.S10
-rw-r--r--arch/arm/kernel/elf.c17
-rw-r--r--arch/arm/kernel/entry-armv.S2
-rw-r--r--arch/arm/kernel/etm.c473
-rw-r--r--arch/arm/kernel/leds.c27
-rw-r--r--arch/arm/kernel/module.c1
-rw-r--r--arch/arm/kernel/process.c120
-rw-r--r--arch/arm/kernel/setup.c4
-rw-r--r--arch/arm/kernel/sleep.S1
-rw-r--r--arch/arm/kernel/smp.c81
-rw-r--r--arch/arm/kernel/smp_twd.c86
-rw-r--r--arch/arm/kernel/traps.c4
-rw-r--r--arch/arm/lib/Makefile6
-rw-r--r--arch/arm/mach-pxa/pm.c1
-rw-r--r--arch/arm/mach-sa1100/pm.c2
-rw-r--r--arch/arm/mach-tegra/Kconfig344
-rw-r--r--arch/arm/mach-tegra/Makefile160
-rw-r--r--arch/arm/mach-tegra/Makefile.boot4
-rw-r--r--arch/arm/mach-tegra/ahb.c218
-rw-r--r--arch/arm/mach-tegra/apbio.c158
-rw-r--r--arch/arm/mach-tegra/apbio.h19
-rw-r--r--arch/arm/mach-tegra/arb_sema.c243
-rw-r--r--arch/arm/mach-tegra/asm_macros.h72
-rw-r--r--arch/arm/mach-tegra/baseband-xmm-power.c895
-rw-r--r--arch/arm/mach-tegra/baseband-xmm-power.h109
-rw-r--r--arch/arm/mach-tegra/baseband-xmm-power2.c707
-rw-r--r--arch/arm/mach-tegra/board-aruba-panel.c247
-rw-r--r--arch/arm/mach-tegra/board-aruba-pinmux.c307
-rw-r--r--arch/arm/mach-tegra/board-aruba-power.c76
-rw-r--r--arch/arm/mach-tegra/board-aruba-sdhci.c248
-rw-r--r--arch/arm/mach-tegra/board-aruba-sensors.c109
-rw-r--r--arch/arm/mach-tegra/board-aruba.c544
-rw-r--r--arch/arm/mach-tegra/board-aruba.h26
-rw-r--r--arch/arm/mach-tegra/board-cardhu-kbc.c275
-rw-r--r--arch/arm/mach-tegra/board-cardhu-memory.c2816
-rw-r--r--arch/arm/mach-tegra/board-cardhu-panel.c1190
-rw-r--r--arch/arm/mach-tegra/board-cardhu-pinmux.c781
-rw-r--r--arch/arm/mach-tegra/board-cardhu-pm298-power-rails.c758
-rw-r--r--arch/arm/mach-tegra/board-cardhu-pm299-power-rails.c728
-rw-r--r--arch/arm/mach-tegra/board-cardhu-power.c1205
-rw-r--r--arch/arm/mach-tegra/board-cardhu-powermon.c256
-rw-r--r--arch/arm/mach-tegra/board-cardhu-sdhci.c299
-rw-r--r--arch/arm/mach-tegra/board-cardhu-sensors.c922
-rw-r--r--arch/arm/mach-tegra/board-cardhu.c1010
-rw-r--r--arch/arm/mach-tegra/board-cardhu.h248
-rw-r--r--arch/arm/mach-tegra/board-enterprise-baseband.c235
-rw-r--r--arch/arm/mach-tegra/board-enterprise-kbc.c104
-rw-r--r--arch/arm/mach-tegra/board-enterprise-memory.c629
-rw-r--r--arch/arm/mach-tegra/board-enterprise-panel.c826
-rw-r--r--arch/arm/mach-tegra/board-enterprise-pinmux.c526
-rw-r--r--arch/arm/mach-tegra/board-enterprise-power.c575
-rw-r--r--arch/arm/mach-tegra/board-enterprise-sdhci.c269
-rw-r--r--arch/arm/mach-tegra/board-enterprise-sensors.c613
-rw-r--r--arch/arm/mach-tegra/board-enterprise.c891
-rw-r--r--arch/arm/mach-tegra/board-enterprise.h131
-rw-r--r--arch/arm/mach-tegra/board-harmony-kbc.c372
-rw-r--r--arch/arm/mach-tegra/board-harmony-panel.c205
-rw-r--r--arch/arm/mach-tegra/board-harmony-pinmux.c33
-rw-r--r--arch/arm/mach-tegra/board-harmony-power.c225
-rw-r--r--arch/arm/mach-tegra/board-harmony.c273
-rw-r--r--arch/arm/mach-tegra/board-harmony.h11
-rw-r--r--arch/arm/mach-tegra/board-seaboard-pinmux.c1
-rw-r--r--arch/arm/mach-tegra/board-ventana-memory.c592
-rw-r--r--arch/arm/mach-tegra/board-ventana-panel.c438
-rw-r--r--arch/arm/mach-tegra/board-ventana-pinmux.c194
-rw-r--r--arch/arm/mach-tegra/board-ventana-power.c320
-rw-r--r--arch/arm/mach-tegra/board-ventana-sdhci.c266
-rw-r--r--arch/arm/mach-tegra/board-ventana-sensors.c573
-rw-r--r--arch/arm/mach-tegra/board-ventana.c641
-rw-r--r--arch/arm/mach-tegra/board-ventana.h89
-rw-r--r--arch/arm/mach-tegra/board-whistler-baseband.c226
-rw-r--r--arch/arm/mach-tegra/board-whistler-baseband.h76
-rw-r--r--arch/arm/mach-tegra/board-whistler-kbc.c135
-rw-r--r--arch/arm/mach-tegra/board-whistler-memory.c569
-rw-r--r--arch/arm/mach-tegra/board-whistler-panel.c383
-rw-r--r--arch/arm/mach-tegra/board-whistler-pinmux.c177
-rw-r--r--arch/arm/mach-tegra/board-whistler-power.c276
-rw-r--r--arch/arm/mach-tegra/board-whistler-sdhci.c248
-rw-r--r--arch/arm/mach-tegra/board-whistler-sensors.c406
-rw-r--r--arch/arm/mach-tegra/board-whistler.c599
-rw-r--r--arch/arm/mach-tegra/board-whistler.h39
-rw-r--r--arch/arm/mach-tegra/board.h81
-rw-r--r--arch/arm/mach-tegra/clock.c833
-rw-r--r--arch/arm/mach-tegra/clock.h147
-rw-r--r--arch/arm/mach-tegra/common-t2.c192
-rw-r--r--arch/arm/mach-tegra/common-t3.c268
-rw-r--r--arch/arm/mach-tegra/common.c922
-rw-r--r--arch/arm/mach-tegra/cpu-tegra.c566
-rw-r--r--arch/arm/mach-tegra/cpu-tegra.h79
-rw-r--r--arch/arm/mach-tegra/cpu-tegra3.c459
-rw-r--r--arch/arm/mach-tegra/cpuidle-t2.c403
-rw-r--r--arch/arm/mach-tegra/cpuidle-t3.c439
-rw-r--r--arch/arm/mach-tegra/cpuidle.c315
-rw-r--r--arch/arm/mach-tegra/cpuidle.h120
-rw-r--r--arch/arm/mach-tegra/csi.c84
-rw-r--r--arch/arm/mach-tegra/delay.S52
-rw-r--r--arch/arm/mach-tegra/devices.c1068
-rw-r--r--arch/arm/mach-tegra/devices.h74
-rw-r--r--arch/arm/mach-tegra/dma.c527
-rw-r--r--arch/arm/mach-tegra/dvfs.c679
-rw-r--r--arch/arm/mach-tegra/dvfs.h144
-rw-r--r--arch/arm/mach-tegra/edp.c387
-rw-r--r--arch/arm/mach-tegra/fiq.c99
-rw-r--r--arch/arm/mach-tegra/fuse.c413
-rw-r--r--arch/arm/mach-tegra/fuse.h43
-rw-r--r--arch/arm/mach-tegra/gic.c129
-rw-r--r--arch/arm/mach-tegra/gic.h49
-rw-r--r--arch/arm/mach-tegra/gpio-names.h24
-rw-r--r--arch/arm/mach-tegra/headsmp.S358
-rw-r--r--arch/arm/mach-tegra/hotplug.c144
-rw-r--r--arch/arm/mach-tegra/i2c_error_recovery.c103
-rw-r--r--arch/arm/mach-tegra/include/mach/arb_sema.h35
-rw-r--r--arch/arm/mach-tegra/include/mach/audio.h57
-rw-r--r--arch/arm/mach-tegra/include/mach/clk.h38
-rw-r--r--arch/arm/mach-tegra/include/mach/csi.h38
-rw-r--r--arch/arm/mach-tegra/include/mach/dc.h558
-rw-r--r--arch/arm/mach-tegra/include/mach/delay.h41
-rw-r--r--arch/arm/mach-tegra/include/mach/dma.h31
-rw-r--r--arch/arm/mach-tegra/include/mach/edp.h80
-rw-r--r--arch/arm/mach-tegra/include/mach/entry-macro.S23
-rw-r--r--arch/arm/mach-tegra/include/mach/fb.h61
-rw-r--r--arch/arm/mach-tegra/include/mach/fiq.h25
-rw-r--r--arch/arm/mach-tegra/include/mach/gpio.h26
-rw-r--r--arch/arm/mach-tegra/include/mach/gpufuse.h19
-rw-r--r--arch/arm/mach-tegra/include/mach/hardware.h (renamed from arch/arm/mach-tegra/include/mach/suspend.h)38
-rw-r--r--arch/arm/mach-tegra/include/mach/hdmi-audio.h46
-rw-r--r--arch/arm/mach-tegra/include/mach/i2s.h316
-rw-r--r--arch/arm/mach-tegra/include/mach/io.h33
-rw-r--r--arch/arm/mach-tegra/include/mach/iomap.h256
-rw-r--r--arch/arm/mach-tegra/include/mach/iovmm.h323
-rw-r--r--arch/arm/mach-tegra/include/mach/irqs.h208
-rw-r--r--arch/arm/mach-tegra/include/mach/kbc.h35
-rw-r--r--arch/arm/mach-tegra/include/mach/kfuse.h20
-rw-r--r--arch/arm/mach-tegra/include/mach/latency_allowance.h121
-rw-r--r--arch/arm/mach-tegra/include/mach/legacy_irq.h23
-rw-r--r--arch/arm/mach-tegra/include/mach/mc.h109
-rw-r--r--arch/arm/mach-tegra/include/mach/memory.h15
-rw-r--r--arch/arm/mach-tegra/include/mach/nand.h55
-rw-r--r--arch/arm/mach-tegra/include/mach/nvmap.h134
-rw-r--r--arch/arm/mach-tegra/include/mach/pinmux-t3.h321
-rw-r--r--arch/arm/mach-tegra/include/mach/pinmux.h130
-rw-r--r--arch/arm/mach-tegra/include/mach/powergate.h65
-rw-r--r--arch/arm/mach-tegra/include/mach/sdhci.h3
-rw-r--r--arch/arm/mach-tegra/include/mach/spdif.h392
-rw-r--r--arch/arm/mach-tegra/include/mach/spi.h42
-rw-r--r--arch/arm/mach-tegra/include/mach/system.h2
-rw-r--r--arch/arm/mach-tegra/include/mach/tegra-bb-power.h56
-rw-r--r--arch/arm/mach-tegra/include/mach/tegra_aic326x_pdata.h39
-rw-r--r--arch/arm/mach-tegra/include/mach/tegra_dc_ext.h77
-rw-r--r--arch/arm/mach-tegra/include/mach/tegra_fb.h27
-rw-r--r--arch/arm/mach-tegra/include/mach/tegra_fiq_debugger.h30
-rw-r--r--arch/arm/mach-tegra/include/mach/tegra_fuse.h27
-rw-r--r--arch/arm/mach-tegra/include/mach/tegra_max98088_pdata.h35
-rw-r--r--arch/arm/mach-tegra/include/mach/tegra_odm_fuses.h107
-rw-r--r--arch/arm/mach-tegra/include/mach/tegra_usb_modem_power.h47
-rw-r--r--arch/arm/mach-tegra/include/mach/tegra_wm8753_pdata.h24
-rw-r--r--arch/arm/mach-tegra/include/mach/thermal.h60
-rw-r--r--arch/arm/mach-tegra/include/mach/tsensor.h53
-rw-r--r--arch/arm/mach-tegra/include/mach/uncompress.h76
-rw-r--r--arch/arm/mach-tegra/include/mach/usb_phy.h86
-rw-r--r--arch/arm/mach-tegra/include/mach/vmalloc.h2
-rw-r--r--arch/arm/mach-tegra/include/mach/w1.h84
-rw-r--r--arch/arm/mach-tegra/io.c42
-rw-r--r--arch/arm/mach-tegra/iovmm-gart.c352
-rw-r--r--arch/arm/mach-tegra/iovmm-smmu.c1359
-rw-r--r--arch/arm/mach-tegra/iovmm.c950
-rw-r--r--arch/arm/mach-tegra/irq.c95
-rw-r--r--arch/arm/mach-tegra/kfuse.c105
-rw-r--r--arch/arm/mach-tegra/latency_allowance.c593
-rw-r--r--arch/arm/mach-tegra/mc.c73
-rw-r--r--arch/arm/mach-tegra/p852/Kconfig110
-rw-r--r--arch/arm/mach-tegra/p852/Makefile39
-rw-r--r--arch/arm/mach-tegra/p852/board-p852-gpio.c158
-rw-r--r--arch/arm/mach-tegra/p852/board-p852-i2c.c180
-rw-r--r--arch/arm/mach-tegra/p852/board-p852-panel.c191
-rw-r--r--arch/arm/mach-tegra/p852/board-p852-pinmux.c439
-rw-r--r--arch/arm/mach-tegra/p852/board-p852-power.c225
-rw-r--r--arch/arm/mach-tegra/p852/board-p852-sdhci.c199
-rw-r--r--arch/arm/mach-tegra/p852/board-p852-sku1-b00.c98
-rw-r--r--arch/arm/mach-tegra/p852/board-p852-sku1-c0x.c98
-rw-r--r--arch/arm/mach-tegra/p852/board-p852-sku1.c89
-rw-r--r--arch/arm/mach-tegra/p852/board-p852-sku13-b00.c114
-rw-r--r--arch/arm/mach-tegra/p852/board-p852-sku13.c112
-rw-r--r--arch/arm/mach-tegra/p852/board-p852-sku23-b00.c115
-rw-r--r--arch/arm/mach-tegra/p852/board-p852-sku23-c01.c87
-rw-r--r--arch/arm/mach-tegra/p852/board-p852-sku23.c113
-rw-r--r--arch/arm/mach-tegra/p852/board-p852-sku3.c103
-rw-r--r--arch/arm/mach-tegra/p852/board-p852-sku5-b00.c115
-rw-r--r--arch/arm/mach-tegra/p852/board-p852-sku5-c01.c93
-rw-r--r--arch/arm/mach-tegra/p852/board-p852-sku8-b00.c88
-rw-r--r--arch/arm/mach-tegra/p852/board-p852-sku8-c01.c87
-rw-r--r--arch/arm/mach-tegra/p852/board-p852-sku9-b00.c93
-rw-r--r--arch/arm/mach-tegra/p852/board-p852-sku9-c01.c92
-rw-r--r--arch/arm/mach-tegra/p852/board-p852.c810
-rw-r--r--arch/arm/mach-tegra/p852/board-p852.h300
-rw-r--r--arch/arm/mach-tegra/pcie.c5
-rw-r--r--arch/arm/mach-tegra/pinmux-t2-tables.c392
-rw-r--r--arch/arm/mach-tegra/pinmux-t3-tables.c478
-rw-r--r--arch/arm/mach-tegra/pinmux.c360
-rw-r--r--arch/arm/mach-tegra/platsmp.c274
-rw-r--r--arch/arm/mach-tegra/pm-irq.c362
-rw-r--r--arch/arm/mach-tegra/pm-irq.h33
-rw-r--r--arch/arm/mach-tegra/pm-t2.c355
-rw-r--r--arch/arm/mach-tegra/pm-t3.c398
-rw-r--r--arch/arm/mach-tegra/pm.c1287
-rw-r--r--arch/arm/mach-tegra/pm.h208
-rw-r--r--arch/arm/mach-tegra/powerdetect.c351
-rw-r--r--arch/arm/mach-tegra/powergate.c720
-rw-r--r--arch/arm/mach-tegra/pwm.c296
-rw-r--r--arch/arm/mach-tegra/reset.c116
-rw-r--r--arch/arm/mach-tegra/reset.h70
-rw-r--r--arch/arm/mach-tegra/sleep-t2.S570
-rw-r--r--arch/arm/mach-tegra/sleep-t3.S694
-rw-r--r--arch/arm/mach-tegra/sleep.S491
-rw-r--r--arch/arm/mach-tegra/sleep.h246
-rw-r--r--arch/arm/mach-tegra/syncpt.c100
-rw-r--r--arch/arm/mach-tegra/sysfs-cluster.c461
-rw-r--r--arch/arm/mach-tegra/sysfs-dcc.c249
-rw-r--r--arch/arm/mach-tegra/tegra2_clocks.c801
-rw-r--r--arch/arm/mach-tegra/tegra2_dvfs.c357
-rw-r--r--arch/arm/mach-tegra/tegra2_emc.c106
-rw-r--r--arch/arm/mach-tegra/tegra2_emc.h15
-rw-r--r--arch/arm/mach-tegra/tegra2_mc.c1017
-rw-r--r--arch/arm/mach-tegra/tegra2_mc.h250
-rw-r--r--arch/arm/mach-tegra/tegra2_speedo.c140
-rw-r--r--arch/arm/mach-tegra/tegra2_statmon.c440
-rw-r--r--arch/arm/mach-tegra/tegra2_statmon.h33
-rw-r--r--arch/arm/mach-tegra/tegra2_throttle.c180
-rw-r--r--arch/arm/mach-tegra/tegra3_actmon.c848
-rw-r--r--arch/arm/mach-tegra/tegra3_clocks.c4703
-rw-r--r--arch/arm/mach-tegra/tegra3_dvfs.c864
-rw-r--r--arch/arm/mach-tegra/tegra3_emc.c1069
-rw-r--r--arch/arm/mach-tegra/tegra3_emc.h279
-rw-r--r--arch/arm/mach-tegra/tegra3_speedo.c373
-rw-r--r--arch/arm/mach-tegra/tegra3_thermal.c350
-rw-r--r--arch/arm/mach-tegra/tegra3_throttle.c367
-rw-r--r--arch/arm/mach-tegra/tegra3_tsensor.c133
-rw-r--r--arch/arm/mach-tegra/tegra_fiq_debugger.c206
-rw-r--r--arch/arm/mach-tegra/tegra_i2s_audio.c1965
-rw-r--r--arch/arm/mach-tegra/tegra_odm_fuses.c951
-rw-r--r--arch/arm/mach-tegra/tegra_smmu.h24
-rw-r--r--arch/arm/mach-tegra/tegra_spdif_audio.c1187
-rw-r--r--arch/arm/mach-tegra/tegra_usb_modem_power.c290
-rw-r--r--arch/arm/mach-tegra/timer-t2.c128
-rw-r--r--arch/arm/mach-tegra/timer-t3.c288
-rw-r--r--arch/arm/mach-tegra/timer.c138
-rw-r--r--arch/arm/mach-tegra/timer.h51
-rw-r--r--arch/arm/mach-tegra/usb_phy.c2365
-rw-r--r--arch/arm/mach-tegra/wakeups-t2.c111
-rw-r--r--arch/arm/mach-tegra/wakeups-t2.h65
-rw-r--r--arch/arm/mach-tegra/wakeups-t3.c122
-rw-r--r--arch/arm/mach-tegra/wakeups-t3.h71
-rw-r--r--arch/arm/mm/Makefile2
-rw-r--r--arch/arm/mm/cache-l2x0.c84
-rw-r--r--arch/arm/mm/cache-v6.S17
-rw-r--r--arch/arm/mm/cache-v7.S55
-rw-r--r--arch/arm/mm/dma-mapping.c7
-rw-r--r--arch/arm/mm/mmu.c11
-rw-r--r--arch/arm/mm/pageattr.c997
-rw-r--r--arch/arm/mm/pgd.c32
-rw-r--r--arch/arm/mm/proc-macros.S2
-rw-r--r--arch/arm/mm/proc-v7.S208
-rw-r--r--arch/arm/mm/proc-xsc3.S2
-rw-r--r--arch/arm/mm/proc-xscale.S2
-rw-r--r--arch/arm/plat-samsung/pm.c4
-rw-r--r--arch/arm/tools/mach-types2
-rw-r--r--arch/arm/vfp/entry.S3
-rw-r--r--arch/arm/vfp/vfphw.S43
-rw-r--r--arch/arm/vfp/vfpmodule.c156
303 files changed, 82454 insertions, 1602 deletions
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index dfe01400cd14..0fc94de7c426 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -186,6 +186,9 @@ config FIQ
config ARCH_MTD_XIP
bool
+config ARCH_PROVIDES_UDELAY
+ bool
+
config VECTORS_BASE
hex
default 0xffff0000 if MMU || CPU_HIGH_VECTOR
@@ -604,6 +607,8 @@ config ARCH_TEGRA
select HAVE_CLK
select HAVE_SCHED_CLOCK
select ARCH_HAS_CPUFREQ
+ select ARCH_PROVIDES_UDELAY
+ select FIQ
help
This enables support for NVIDIA Tegra based systems (Tegra APX,
Tegra 6xx and Tegra 2 series).
@@ -1297,6 +1302,25 @@ config ARM_ERRATA_764369
relevant cache maintenance functions and sets a specific bit
in the diagnostic control register of the SCU.
+config ARM_ERRATA_720791
+ bool "ARM errata: Dynamic high-level clock gating corrupts the Jazelle instruction stream"
+ depends on CPU_V7
+ help
+ This option enables the workaround for the 720791 Cortex-A9
+ (r1p0..r1p2) erratum. The Jazelle instruction stream may be
+ corrupted when dynamic high-level clock gating is enabled.
+ This workaround disables gating the Core clock when the Instruction
+ side is waiting for a Page Table Walk answer or linefill completion.
+
+config ARM_ERRATA_752520
+ bool "ARM errata: Faulty arbitration between PLD and Cacheable TLB requests may create a system deadlock"
+ depends on CPU_V7
+ help
+ Under rare circumstances, PLDs may interfere with a Cacheable page table walk,
+ creating a processor deadlock. The erratum can only happen when the Data Cache
+ and MMU are enabled, with the TLB descriptors marked as L1 cacheable,
+ so that Page Table Walks are performed as cache linefills.
+
config PL310_ERRATA_769419
bool "PL310 errata: no automatic Store Buffer drain"
depends on CACHE_L2X0
@@ -1745,6 +1769,15 @@ config DEPRECATED_PARAM_STRUCT
This was deprecated in 2001 and announced to live on for 5 years.
Some old boot loaders still use this way.
+config ARM_FLUSH_CONSOLE_ON_RESTART
+ bool "Force flush the console on restart"
+ help
+ If the console is locked while the system is rebooted, the messages
+ in the temporary logbuffer would not have propogated to all the
+ console drivers. This option forces the console lock to be
+ released if it failed to be acquired, which will cause all the
+ pending messages to be flushed.
+
endmenu
menu "Boot options"
diff --git a/arch/arm/boot/compressed/head.S b/arch/arm/boot/compressed/head.S
index e95a5989602a..24701d6f72bc 100644
--- a/arch/arm/boot/compressed/head.S
+++ b/arch/arm/boot/compressed/head.S
@@ -657,6 +657,8 @@ proc_types:
@ b __arm6_mmu_cache_off
@ b __armv3_mmu_cache_flush
+#if !defined(CONFIG_CPU_V7)
+ /* This collides with some V7 IDs, preventing correct detection */
.word 0x00000000 @ old ARM ID
.word 0x0000f000
mov pc, lr
@@ -665,6 +667,7 @@ proc_types:
THUMB( nop )
mov pc, lr
THUMB( nop )
+#endif
.word 0x41007000 @ ARM7/710
.word 0xfff8fe00
diff --git a/arch/arm/common/Kconfig b/arch/arm/common/Kconfig
index 4b71766fb21d..638256600ffe 100644
--- a/arch/arm/common/Kconfig
+++ b/arch/arm/common/Kconfig
@@ -39,3 +39,53 @@ config SHARP_PARAM
config SHARP_SCOOP
bool
+
+config FIQ_GLUE
+ bool
+ select FIQ
+
+config FIQ_DEBUGGER
+ bool "FIQ Mode Serial Debugger"
+ select FIQ
+ select FIQ_GLUE
+ default n
+ help
+ The FIQ serial debugger can accept commands even when the
+ kernel is unresponsive due to being stuck with interrupts
+ disabled.
+
+
+config FIQ_DEBUGGER_NO_SLEEP
+ bool "Keep serial debugger active"
+ depends on FIQ_DEBUGGER
+ default n
+ help
+ Enables the serial debugger at boot. Passing
+ fiq_debugger.no_sleep on the kernel commandline will
+ override this config option.
+
+config FIQ_DEBUGGER_WAKEUP_IRQ_ALWAYS_ON
+ bool "Don't disable wakeup IRQ when debugger is active"
+ depends on FIQ_DEBUGGER
+ default n
+ help
+ Don't disable the wakeup irq when enabling the uart clock. This will
+ cause extra interrupts, but it makes the serial debugger usable with
+ on some MSM radio builds that ignore the uart clock request in power
+ collapse.
+
+config FIQ_DEBUGGER_CONSOLE
+ bool "Console on FIQ Serial Debugger port"
+ depends on FIQ_DEBUGGER
+ default n
+ help
+ Enables a console so that printk messages are displayed on
+ the debugger serial port as the occur.
+
+config FIQ_DEBUGGER_CONSOLE_DEFAULT_ENABLE
+ bool "Put the FIQ debugger into console mode by default"
+ depends on FIQ_DEBUGGER_CONSOLE
+ default n
+ help
+ If enabled, this puts the fiq debugger into console mode by default.
+ Otherwise, the fiq debugger will start out in debug mode.
diff --git a/arch/arm/common/Makefile b/arch/arm/common/Makefile
index 6ea9b6f3607a..3ab5d765fedd 100644
--- a/arch/arm/common/Makefile
+++ b/arch/arm/common/Makefile
@@ -17,3 +17,5 @@ obj-$(CONFIG_ARCH_IXP2000) += uengine.o
obj-$(CONFIG_ARCH_IXP23XX) += uengine.o
obj-$(CONFIG_PCI_HOST_ITE8152) += it8152.o
obj-$(CONFIG_ARM_TIMER_SP804) += timer-sp.o
+obj-$(CONFIG_FIQ_GLUE) += fiq_glue.o fiq_glue_setup.o
+obj-$(CONFIG_FIQ_DEBUGGER) += fiq_debugger.o
diff --git a/arch/arm/common/fiq_debugger.c b/arch/arm/common/fiq_debugger.c
new file mode 100644
index 000000000000..3ed18ae2ed80
--- /dev/null
+++ b/arch/arm/common/fiq_debugger.c
@@ -0,0 +1,1196 @@
+/*
+ * arch/arm/common/fiq_debugger.c
+ *
+ * Serial Debugger Interface accessed through an FIQ interrupt.
+ *
+ * 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 <stdarg.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/console.h>
+#include <linux/interrupt.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/kernel_stat.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp.h>
+#include <linux/timer.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/wakelock.h>
+
+#include <asm/fiq_debugger.h>
+#include <asm/fiq_glue.h>
+#include <asm/stacktrace.h>
+
+#include <mach/system.h>
+
+#include <linux/uaccess.h>
+
+#include "fiq_debugger_ringbuf.h"
+
+#define DEBUG_MAX 64
+#define MAX_UNHANDLED_FIQ_COUNT 1000000
+
+#define THREAD_INFO(sp) ((struct thread_info *) \
+ ((unsigned long)(sp) & ~(THREAD_SIZE - 1)))
+
+struct fiq_debugger_state {
+ struct fiq_glue_handler handler;
+
+ int fiq;
+ int uart_irq;
+ int signal_irq;
+ int wakeup_irq;
+ bool wakeup_irq_no_set_wake;
+ struct clk *clk;
+ struct fiq_debugger_pdata *pdata;
+ struct platform_device *pdev;
+
+ char debug_cmd[DEBUG_MAX];
+ int debug_busy;
+ int debug_abort;
+
+ char debug_buf[DEBUG_MAX];
+ int debug_count;
+
+ bool no_sleep;
+ bool debug_enable;
+ bool ignore_next_wakeup_irq;
+ struct timer_list sleep_timer;
+ spinlock_t sleep_timer_lock;
+ bool uart_enabled;
+ struct wake_lock debugger_wake_lock;
+ bool console_enable;
+ int current_cpu;
+ atomic_t unhandled_fiq_count;
+ bool in_fiq;
+
+#ifdef CONFIG_FIQ_DEBUGGER_CONSOLE
+ struct console console;
+ struct tty_driver *tty_driver;
+ struct tty_struct *tty;
+ int tty_open_count;
+ struct fiq_debugger_ringbuf *tty_rbuf;
+ bool syslog_dumping;
+#endif
+
+ unsigned int last_irqs[NR_IRQS];
+ unsigned int last_local_timer_irqs[NR_CPUS];
+};
+
+#ifdef CONFIG_FIQ_DEBUGGER_NO_SLEEP
+static bool initial_no_sleep = true;
+#else
+static bool initial_no_sleep;
+#endif
+
+#ifdef CONFIG_FIQ_DEBUGGER_CONSOLE_DEFAULT_ENABLE
+static bool initial_debug_enable = true;
+static bool initial_console_enable = true;
+#else
+static bool initial_debug_enable;
+static bool initial_console_enable;
+#endif
+
+module_param_named(no_sleep, initial_no_sleep, bool, 0644);
+module_param_named(debug_enable, initial_debug_enable, bool, 0644);
+module_param_named(console_enable, initial_console_enable, bool, 0644);
+
+#ifdef CONFIG_FIQ_DEBUGGER_WAKEUP_IRQ_ALWAYS_ON
+static inline void enable_wakeup_irq(struct fiq_debugger_state *state) {}
+static inline void disable_wakeup_irq(struct fiq_debugger_state *state) {}
+#else
+static inline void enable_wakeup_irq(struct fiq_debugger_state *state)
+{
+ if (state->wakeup_irq < 0)
+ return;
+ enable_irq(state->wakeup_irq);
+ if (!state->wakeup_irq_no_set_wake)
+ enable_irq_wake(state->wakeup_irq);
+}
+static inline void disable_wakeup_irq(struct fiq_debugger_state *state)
+{
+ if (state->wakeup_irq < 0)
+ return;
+ disable_irq_nosync(state->wakeup_irq);
+ if (!state->wakeup_irq_no_set_wake)
+ disable_irq_wake(state->wakeup_irq);
+}
+#endif
+
+static bool inline debug_have_fiq(struct fiq_debugger_state *state)
+{
+ return (state->fiq >= 0);
+}
+
+static void debug_force_irq(struct fiq_debugger_state *state)
+{
+ unsigned int irq = state->signal_irq;
+
+ if (WARN_ON(!debug_have_fiq(state)))
+ return;
+ if (state->pdata->force_irq) {
+ state->pdata->force_irq(state->pdev, irq);
+ } else {
+ struct irq_chip *chip = irq_get_chip(irq);
+ if (chip && chip->irq_retrigger)
+ chip->irq_retrigger(irq_get_irq_data(irq));
+ }
+}
+
+static void debug_uart_enable(struct fiq_debugger_state *state)
+{
+ if (state->clk)
+ clk_enable(state->clk);
+ if (state->pdata->uart_enable)
+ state->pdata->uart_enable(state->pdev);
+}
+
+static void debug_uart_disable(struct fiq_debugger_state *state)
+{
+ if (state->pdata->uart_disable)
+ state->pdata->uart_disable(state->pdev);
+ if (state->clk)
+ clk_disable(state->clk);
+}
+
+static void debug_uart_flush(struct fiq_debugger_state *state)
+{
+ if (state->pdata->uart_flush)
+ state->pdata->uart_flush(state->pdev);
+}
+
+static void debug_puts(struct fiq_debugger_state *state, char *s)
+{
+ unsigned c;
+ while ((c = *s++)) {
+ if (c == '\n')
+ state->pdata->uart_putc(state->pdev, '\r');
+ state->pdata->uart_putc(state->pdev, c);
+ }
+}
+
+static void debug_prompt(struct fiq_debugger_state *state)
+{
+ debug_puts(state, "debug> ");
+}
+
+int log_buf_copy(char *dest, int idx, int len);
+static void dump_kernel_log(struct fiq_debugger_state *state)
+{
+ char buf[1024];
+ int idx = 0;
+ int ret;
+ int saved_oip;
+
+ /* setting oops_in_progress prevents log_buf_copy()
+ * from trying to take a spinlock which will make it
+ * very unhappy in some cases...
+ */
+ saved_oip = oops_in_progress;
+ oops_in_progress = 1;
+ for (;;) {
+ ret = log_buf_copy(buf, idx, 1023);
+ if (ret <= 0)
+ break;
+ buf[ret] = 0;
+ debug_puts(state, buf);
+ idx += ret;
+ }
+ oops_in_progress = saved_oip;
+}
+
+static char *mode_name(unsigned cpsr)
+{
+ switch (cpsr & MODE_MASK) {
+ case USR_MODE: return "USR";
+ case FIQ_MODE: return "FIQ";
+ case IRQ_MODE: return "IRQ";
+ case SVC_MODE: return "SVC";
+ case ABT_MODE: return "ABT";
+ case UND_MODE: return "UND";
+ case SYSTEM_MODE: return "SYS";
+ default: return "???";
+ }
+}
+
+static int debug_printf(void *cookie, const char *fmt, ...)
+{
+ struct fiq_debugger_state *state = cookie;
+ char buf[256];
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+
+ debug_puts(state, buf);
+ return state->debug_abort;
+}
+
+/* Safe outside fiq context */
+static int debug_printf_nfiq(void *cookie, const char *fmt, ...)
+{
+ struct fiq_debugger_state *state = cookie;
+ char buf[256];
+ va_list ap;
+ unsigned long irq_flags;
+
+ va_start(ap, fmt);
+ vsnprintf(buf, 128, fmt, ap);
+ va_end(ap);
+
+ local_irq_save(irq_flags);
+ debug_puts(state, buf);
+ debug_uart_flush(state);
+ local_irq_restore(irq_flags);
+ return state->debug_abort;
+}
+
+static void dump_regs(struct fiq_debugger_state *state, unsigned *regs)
+{
+ debug_printf(state, " r0 %08x r1 %08x r2 %08x r3 %08x\n",
+ regs[0], regs[1], regs[2], regs[3]);
+ debug_printf(state, " r4 %08x r5 %08x r6 %08x r7 %08x\n",
+ regs[4], regs[5], regs[6], regs[7]);
+ debug_printf(state, " r8 %08x r9 %08x r10 %08x r11 %08x mode %s\n",
+ regs[8], regs[9], regs[10], regs[11],
+ mode_name(regs[16]));
+ if ((regs[16] & MODE_MASK) == USR_MODE)
+ debug_printf(state, " ip %08x sp %08x lr %08x pc %08x "
+ "cpsr %08x\n", regs[12], regs[13], regs[14],
+ regs[15], regs[16]);
+ else
+ debug_printf(state, " ip %08x sp %08x lr %08x pc %08x "
+ "cpsr %08x spsr %08x\n", regs[12], regs[13],
+ regs[14], regs[15], regs[16], regs[17]);
+}
+
+struct mode_regs {
+ unsigned long sp_svc;
+ unsigned long lr_svc;
+ unsigned long spsr_svc;
+
+ unsigned long sp_abt;
+ unsigned long lr_abt;
+ unsigned long spsr_abt;
+
+ unsigned long sp_und;
+ unsigned long lr_und;
+ unsigned long spsr_und;
+
+ unsigned long sp_irq;
+ unsigned long lr_irq;
+ unsigned long spsr_irq;
+
+ unsigned long r8_fiq;
+ unsigned long r9_fiq;
+ unsigned long r10_fiq;
+ unsigned long r11_fiq;
+ unsigned long r12_fiq;
+ unsigned long sp_fiq;
+ unsigned long lr_fiq;
+ unsigned long spsr_fiq;
+};
+
+void __naked get_mode_regs(struct mode_regs *regs)
+{
+ asm volatile (
+ "mrs r1, cpsr\n"
+ "msr cpsr_c, #0xd3 @(SVC_MODE | PSR_I_BIT | PSR_F_BIT)\n"
+ "stmia r0!, {r13 - r14}\n"
+ "mrs r2, spsr\n"
+ "msr cpsr_c, #0xd7 @(ABT_MODE | PSR_I_BIT | PSR_F_BIT)\n"
+ "stmia r0!, {r2, r13 - r14}\n"
+ "mrs r2, spsr\n"
+ "msr cpsr_c, #0xdb @(UND_MODE | PSR_I_BIT | PSR_F_BIT)\n"
+ "stmia r0!, {r2, r13 - r14}\n"
+ "mrs r2, spsr\n"
+ "msr cpsr_c, #0xd2 @(IRQ_MODE | PSR_I_BIT | PSR_F_BIT)\n"
+ "stmia r0!, {r2, r13 - r14}\n"
+ "mrs r2, spsr\n"
+ "msr cpsr_c, #0xd1 @(FIQ_MODE | PSR_I_BIT | PSR_F_BIT)\n"
+ "stmia r0!, {r2, r8 - r14}\n"
+ "mrs r2, spsr\n"
+ "stmia r0!, {r2}\n"
+ "msr cpsr_c, r1\n"
+ "bx lr\n");
+}
+
+
+static void dump_allregs(struct fiq_debugger_state *state, unsigned *regs)
+{
+ struct mode_regs mode_regs;
+ dump_regs(state, regs);
+ get_mode_regs(&mode_regs);
+ debug_printf(state, " svc: sp %08x lr %08x spsr %08x\n",
+ mode_regs.sp_svc, mode_regs.lr_svc, mode_regs.spsr_svc);
+ debug_printf(state, " abt: sp %08x lr %08x spsr %08x\n",
+ mode_regs.sp_abt, mode_regs.lr_abt, mode_regs.spsr_abt);
+ debug_printf(state, " und: sp %08x lr %08x spsr %08x\n",
+ mode_regs.sp_und, mode_regs.lr_und, mode_regs.spsr_und);
+ debug_printf(state, " irq: sp %08x lr %08x spsr %08x\n",
+ mode_regs.sp_irq, mode_regs.lr_irq, mode_regs.spsr_irq);
+ debug_printf(state, " fiq: r8 %08x r9 %08x r10 %08x r11 %08x "
+ "r12 %08x\n",
+ mode_regs.r8_fiq, mode_regs.r9_fiq, mode_regs.r10_fiq,
+ mode_regs.r11_fiq, mode_regs.r12_fiq);
+ debug_printf(state, " fiq: sp %08x lr %08x spsr %08x\n",
+ mode_regs.sp_fiq, mode_regs.lr_fiq, mode_regs.spsr_fiq);
+}
+
+static void dump_irqs(struct fiq_debugger_state *state)
+{
+ int n;
+ unsigned int cpu;
+
+ debug_printf(state, "irqnr total since-last status name\n");
+ for (n = 0; n < NR_IRQS; n++) {
+ struct irqaction *act = irq_desc[n].action;
+ if (!act && !kstat_irqs(n))
+ continue;
+ debug_printf(state, "%5d: %10u %11u %8x %s\n", n,
+ kstat_irqs(n),
+ kstat_irqs(n) - state->last_irqs[n],
+ irq_desc[n].status_use_accessors,
+ (act && act->name) ? act->name : "???");
+ state->last_irqs[n] = kstat_irqs(n);
+ }
+
+ for (cpu = 0; cpu < NR_CPUS; cpu++) {
+
+ debug_printf(state, "LOC %d: %10u %11u\n", cpu,
+ __IRQ_STAT(cpu, local_timer_irqs),
+ __IRQ_STAT(cpu, local_timer_irqs) -
+ state->last_local_timer_irqs[cpu]);
+ state->last_local_timer_irqs[cpu] =
+ __IRQ_STAT(cpu, local_timer_irqs);
+ }
+}
+
+struct stacktrace_state {
+ struct fiq_debugger_state *state;
+ unsigned int depth;
+};
+
+static int report_trace(struct stackframe *frame, void *d)
+{
+ struct stacktrace_state *sts = d;
+
+ if (sts->depth) {
+ debug_printf(sts->state,
+ " pc: %p (%pF), lr %p (%pF), sp %p, fp %p\n",
+ frame->pc, frame->pc, frame->lr, frame->lr,
+ frame->sp, frame->fp);
+ sts->depth--;
+ return 0;
+ }
+ debug_printf(sts->state, " ...\n");
+
+ return sts->depth == 0;
+}
+
+struct frame_tail {
+ struct frame_tail *fp;
+ unsigned long sp;
+ unsigned long lr;
+} __attribute__((packed));
+
+static struct frame_tail *user_backtrace(struct fiq_debugger_state *state,
+ struct frame_tail *tail)
+{
+ struct frame_tail buftail[2];
+
+ /* Also check accessibility of one struct frame_tail beyond */
+ if (!access_ok(VERIFY_READ, tail, sizeof(buftail))) {
+ debug_printf(state, " invalid frame pointer %p\n", tail);
+ return NULL;
+ }
+ if (__copy_from_user_inatomic(buftail, tail, sizeof(buftail))) {
+ debug_printf(state,
+ " failed to copy frame pointer %p\n", tail);
+ return NULL;
+ }
+
+ debug_printf(state, " %p\n", buftail[0].lr);
+
+ /* frame pointers should strictly progress back up the stack
+ * (towards higher addresses) */
+ if (tail >= buftail[0].fp)
+ return NULL;
+
+ return buftail[0].fp-1;
+}
+
+void dump_stacktrace(struct fiq_debugger_state *state,
+ struct pt_regs * const regs, unsigned int depth, void *ssp)
+{
+ struct frame_tail *tail;
+ struct thread_info *real_thread_info = THREAD_INFO(ssp);
+ struct stacktrace_state sts;
+
+ sts.depth = depth;
+ sts.state = state;
+ *current_thread_info() = *real_thread_info;
+
+ if (!current)
+ debug_printf(state, "current NULL\n");
+ else
+ debug_printf(state, "pid: %d comm: %s\n",
+ current->pid, current->comm);
+ dump_regs(state, (unsigned *)regs);
+
+ if (!user_mode(regs)) {
+ struct stackframe frame;
+ frame.fp = regs->ARM_fp;
+ frame.sp = regs->ARM_sp;
+ frame.lr = regs->ARM_lr;
+ frame.pc = regs->ARM_pc;
+ debug_printf(state,
+ " pc: %p (%pF), lr %p (%pF), sp %p, fp %p\n",
+ regs->ARM_pc, regs->ARM_pc, regs->ARM_lr, regs->ARM_lr,
+ regs->ARM_sp, regs->ARM_fp);
+ walk_stackframe(&frame, report_trace, &sts);
+ return;
+ }
+
+ tail = ((struct frame_tail *) regs->ARM_fp) - 1;
+ while (depth-- && tail && !((unsigned long) tail & 3))
+ tail = user_backtrace(state, tail);
+}
+
+static void do_ps(struct fiq_debugger_state *state)
+{
+ struct task_struct *g;
+ struct task_struct *p;
+ unsigned task_state;
+ static const char stat_nam[] = "RSDTtZX";
+
+ debug_printf(state, "pid ppid prio task pc\n");
+ read_lock(&tasklist_lock);
+ do_each_thread(g, p) {
+ task_state = p->state ? __ffs(p->state) + 1 : 0;
+ debug_printf(state,
+ "%5d %5d %4d ", p->pid, p->parent->pid, p->prio);
+ debug_printf(state, "%-13.13s %c", p->comm,
+ task_state >= sizeof(stat_nam) ? '?' : stat_nam[task_state]);
+ if (task_state == TASK_RUNNING)
+ debug_printf(state, " running\n");
+ else
+ debug_printf(state, " %08lx\n", thread_saved_pc(p));
+ } while_each_thread(g, p);
+ read_unlock(&tasklist_lock);
+}
+
+#ifdef CONFIG_FIQ_DEBUGGER_CONSOLE
+static void begin_syslog_dump(struct fiq_debugger_state *state)
+{
+ state->syslog_dumping = true;
+}
+
+static void end_syslog_dump(struct fiq_debugger_state *state)
+{
+ state->syslog_dumping = false;
+}
+#else
+extern int do_syslog(int type, char __user *bug, int count);
+static void begin_syslog_dump(struct fiq_debugger_state *state)
+{
+ do_syslog(5 /* clear */, NULL, 0);
+}
+
+static void end_syslog_dump(struct fiq_debugger_state *state)
+{
+ char buf[128];
+ int ret;
+ int idx = 0;
+
+ while (1) {
+ ret = log_buf_copy(buf, idx, sizeof(buf) - 1);
+ if (ret <= 0)
+ break;
+ buf[ret] = 0;
+ debug_printf(state, "%s", buf);
+ idx += ret;
+ }
+}
+#endif
+
+static void do_sysrq(struct fiq_debugger_state *state, char rq)
+{
+ begin_syslog_dump(state);
+ handle_sysrq(rq);
+ end_syslog_dump(state);
+}
+
+/* This function CANNOT be called in FIQ context */
+static void debug_irq_exec(struct fiq_debugger_state *state, char *cmd)
+{
+ if (!strcmp(cmd, "ps"))
+ do_ps(state);
+ if (!strcmp(cmd, "sysrq"))
+ do_sysrq(state, 'h');
+ if (!strncmp(cmd, "sysrq ", 6))
+ do_sysrq(state, cmd[6]);
+}
+
+static void debug_help(struct fiq_debugger_state *state)
+{
+ debug_printf(state, "FIQ Debugger commands:\n"
+ " pc PC status\n"
+ " regs Register dump\n"
+ " allregs Extended Register dump\n"
+ " bt Stack trace\n"
+ " reboot Reboot\n"
+ " irqs Interupt status\n"
+ " kmsg Kernel log\n"
+ " version Kernel version\n");
+ debug_printf(state, " sleep Allow sleep while in FIQ\n"
+ " nosleep Disable sleep while in FIQ\n"
+ " console Switch terminal to console\n"
+ " cpu Current CPU\n"
+ " cpu <number> Switch to CPU<number>\n");
+ debug_printf(state, " ps Process list\n"
+ " sysrq sysrq options\n"
+ " sysrq <param> Execute sysrq with <param>\n");
+}
+
+static void take_affinity(void *info)
+{
+ struct fiq_debugger_state *state = info;
+ struct cpumask cpumask;
+
+ cpumask_clear(&cpumask);
+ cpumask_set_cpu(get_cpu(), &cpumask);
+
+ irq_set_affinity(state->uart_irq, &cpumask);
+}
+
+static void switch_cpu(struct fiq_debugger_state *state, int cpu)
+{
+ if (!debug_have_fiq(state))
+ smp_call_function_single(cpu, take_affinity, state, false);
+ state->current_cpu = cpu;
+}
+
+static bool debug_fiq_exec(struct fiq_debugger_state *state,
+ const char *cmd, unsigned *regs, void *svc_sp)
+{
+ bool signal_helper = false;
+
+ if (!strcmp(cmd, "help") || !strcmp(cmd, "?")) {
+ debug_help(state);
+ } else if (!strcmp(cmd, "pc")) {
+ debug_printf(state, " pc %08x cpsr %08x mode %s\n",
+ regs[15], regs[16], mode_name(regs[16]));
+ } else if (!strcmp(cmd, "regs")) {
+ dump_regs(state, regs);
+ } else if (!strcmp(cmd, "allregs")) {
+ dump_allregs(state, regs);
+ } else if (!strcmp(cmd, "bt")) {
+ dump_stacktrace(state, (struct pt_regs *)regs, 100, svc_sp);
+ } else if (!strcmp(cmd, "reboot")) {
+ arch_reset(0, 0);
+ } else if (!strcmp(cmd, "irqs")) {
+ dump_irqs(state);
+ } else if (!strcmp(cmd, "kmsg")) {
+ dump_kernel_log(state);
+ } else if (!strcmp(cmd, "version")) {
+ debug_printf(state, "%s\n", linux_banner);
+ } else if (!strcmp(cmd, "sleep")) {
+ state->no_sleep = false;
+ debug_printf(state, "enabling sleep\n");
+ } else if (!strcmp(cmd, "nosleep")) {
+ state->no_sleep = true;
+ debug_printf(state, "disabling sleep\n");
+ } else if (!strcmp(cmd, "console")) {
+ state->console_enable = true;
+ debug_printf(state, "console mode\n");
+ } else if (!strcmp(cmd, "cpu")) {
+ debug_printf(state, "cpu %d\n", state->current_cpu);
+ } else if (!strncmp(cmd, "cpu ", 4)) {
+ unsigned long cpu = 0;
+ if (strict_strtoul(cmd + 4, 10, &cpu) == 0)
+ switch_cpu(state, cpu);
+ else
+ debug_printf(state, "invalid cpu\n");
+ debug_printf(state, "cpu %d\n", state->current_cpu);
+ } else {
+ if (state->debug_busy) {
+ debug_printf(state,
+ "command processor busy. trying to abort.\n");
+ state->debug_abort = -1;
+ } else {
+ strcpy(state->debug_cmd, cmd);
+ state->debug_busy = 1;
+ }
+
+ return true;
+ }
+ if (!state->console_enable)
+ debug_prompt(state);
+
+ return signal_helper;
+}
+
+static void sleep_timer_expired(unsigned long data)
+{
+ struct fiq_debugger_state *state = (struct fiq_debugger_state *)data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&state->sleep_timer_lock, flags);
+ if (state->uart_enabled && !state->no_sleep) {
+ if (state->debug_enable && !state->console_enable) {
+ state->debug_enable = false;
+ debug_printf_nfiq(state, "suspending fiq debugger\n");
+ }
+ state->ignore_next_wakeup_irq = true;
+ debug_uart_disable(state);
+ state->uart_enabled = false;
+ enable_wakeup_irq(state);
+ }
+ wake_unlock(&state->debugger_wake_lock);
+ spin_unlock_irqrestore(&state->sleep_timer_lock, flags);
+}
+
+static void handle_wakeup(struct fiq_debugger_state *state)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&state->sleep_timer_lock, flags);
+ if (state->wakeup_irq >= 0 && state->ignore_next_wakeup_irq) {
+ state->ignore_next_wakeup_irq = false;
+ } else if (!state->uart_enabled) {
+ wake_lock(&state->debugger_wake_lock);
+ debug_uart_enable(state);
+ state->uart_enabled = true;
+ disable_wakeup_irq(state);
+ mod_timer(&state->sleep_timer, jiffies + HZ / 2);
+ }
+ spin_unlock_irqrestore(&state->sleep_timer_lock, flags);
+}
+
+static irqreturn_t wakeup_irq_handler(int irq, void *dev)
+{
+ struct fiq_debugger_state *state = dev;
+
+ if (!state->no_sleep)
+ debug_puts(state, "WAKEUP\n");
+ handle_wakeup(state);
+
+ return IRQ_HANDLED;
+}
+
+
+static void debug_handle_irq_context(struct fiq_debugger_state *state)
+{
+ if (!state->no_sleep) {
+ unsigned long flags;
+
+ spin_lock_irqsave(&state->sleep_timer_lock, flags);
+ wake_lock(&state->debugger_wake_lock);
+ mod_timer(&state->sleep_timer, jiffies + HZ * 5);
+ spin_unlock_irqrestore(&state->sleep_timer_lock, flags);
+ }
+#if defined(CONFIG_FIQ_DEBUGGER_CONSOLE)
+ if (state->tty) {
+ int i;
+ int count = fiq_debugger_ringbuf_level(state->tty_rbuf);
+ for (i = 0; i < count; i++) {
+ int c = fiq_debugger_ringbuf_peek(state->tty_rbuf, 0);
+ tty_insert_flip_char(state->tty, c, TTY_NORMAL);
+ if (!fiq_debugger_ringbuf_consume(state->tty_rbuf, 1))
+ pr_warn("fiq tty failed to consume byte\n");
+ }
+ tty_flip_buffer_push(state->tty);
+ }
+#endif
+ if (state->debug_busy) {
+ debug_irq_exec(state, state->debug_cmd);
+ debug_prompt(state);
+ state->debug_busy = 0;
+ }
+}
+
+static int debug_getc(struct fiq_debugger_state *state)
+{
+ return state->pdata->uart_getc(state->pdev);
+}
+
+static bool debug_handle_uart_interrupt(struct fiq_debugger_state *state,
+ int this_cpu, void *regs, void *svc_sp)
+{
+ int c;
+ static int last_c;
+ int count = 0;
+ bool signal_helper = false;
+
+ if (this_cpu != state->current_cpu) {
+ if (state->in_fiq)
+ return false;
+
+ if (atomic_inc_return(&state->unhandled_fiq_count) !=
+ MAX_UNHANDLED_FIQ_COUNT)
+ return false;
+
+ debug_printf(state, "fiq_debugger: cpu %d not responding, "
+ "reverting to cpu %d\n", state->current_cpu,
+ this_cpu);
+
+ atomic_set(&state->unhandled_fiq_count, 0);
+ switch_cpu(state, this_cpu);
+ return false;
+ }
+
+ state->in_fiq = true;
+
+ while ((c = debug_getc(state)) != FIQ_DEBUGGER_NO_CHAR) {
+ count++;
+ if (!state->debug_enable) {
+ if ((c == 13) || (c == 10)) {
+ state->debug_enable = true;
+ state->debug_count = 0;
+ debug_prompt(state);
+ }
+ } else if (c == FIQ_DEBUGGER_BREAK) {
+ state->console_enable = false;
+ debug_puts(state, "fiq debugger mode\n");
+ state->debug_count = 0;
+ debug_prompt(state);
+#ifdef CONFIG_FIQ_DEBUGGER_CONSOLE
+ } else if (state->console_enable && state->tty_rbuf) {
+ fiq_debugger_ringbuf_push(state->tty_rbuf, c);
+ signal_helper = true;
+#endif
+ } else if ((c >= ' ') && (c < 127)) {
+ if (state->debug_count < (DEBUG_MAX - 1)) {
+ state->debug_buf[state->debug_count++] = c;
+ state->pdata->uart_putc(state->pdev, c);
+ }
+ } else if ((c == 8) || (c == 127)) {
+ if (state->debug_count > 0) {
+ state->debug_count--;
+ state->pdata->uart_putc(state->pdev, 8);
+ state->pdata->uart_putc(state->pdev, ' ');
+ state->pdata->uart_putc(state->pdev, 8);
+ }
+ } else if ((c == 13) || (c == 10)) {
+ if (c == '\r' || (c == '\n' && last_c != '\r')) {
+ state->pdata->uart_putc(state->pdev, '\r');
+ state->pdata->uart_putc(state->pdev, '\n');
+ }
+ if (state->debug_count) {
+ state->debug_buf[state->debug_count] = 0;
+ state->debug_count = 0;
+ signal_helper |=
+ debug_fiq_exec(state, state->debug_buf,
+ regs, svc_sp);
+ } else {
+ debug_prompt(state);
+ }
+ }
+ last_c = c;
+ }
+ debug_uart_flush(state);
+ if (state->pdata->fiq_ack)
+ state->pdata->fiq_ack(state->pdev, state->fiq);
+
+ /* poke sleep timer if necessary */
+ if (state->debug_enable && !state->no_sleep)
+ signal_helper = true;
+
+ atomic_set(&state->unhandled_fiq_count, 0);
+ state->in_fiq = false;
+
+ return signal_helper;
+}
+
+static void debug_fiq(struct fiq_glue_handler *h, void *regs, void *svc_sp)
+{
+ struct fiq_debugger_state *state =
+ container_of(h, struct fiq_debugger_state, handler);
+ unsigned int this_cpu = THREAD_INFO(svc_sp)->cpu;
+ bool need_irq;
+
+ need_irq = debug_handle_uart_interrupt(state, this_cpu, regs, svc_sp);
+ if (need_irq)
+ debug_force_irq(state);
+}
+
+/*
+ * When not using FIQs, we only use this single interrupt as an entry point.
+ * This just effectively takes over the UART interrupt and does all the work
+ * in this context.
+ */
+static irqreturn_t debug_uart_irq(int irq, void *dev)
+{
+ struct fiq_debugger_state *state = dev;
+ bool not_done;
+
+ handle_wakeup(state);
+
+ /* handle the debugger irq in regular context */
+ not_done = debug_handle_uart_interrupt(state, smp_processor_id(),
+ get_irq_regs(),
+ current_thread_info());
+ if (not_done)
+ debug_handle_irq_context(state);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * If FIQs are used, not everything can happen in fiq context.
+ * FIQ handler does what it can and then signals this interrupt to finish the
+ * job in irq context.
+ */
+static irqreturn_t debug_signal_irq(int irq, void *dev)
+{
+ struct fiq_debugger_state *state = dev;
+
+ if (state->pdata->force_irq_ack)
+ state->pdata->force_irq_ack(state->pdev, state->signal_irq);
+
+ debug_handle_irq_context(state);
+
+ return IRQ_HANDLED;
+}
+
+static void debug_resume(struct fiq_glue_handler *h)
+{
+ struct fiq_debugger_state *state =
+ container_of(h, struct fiq_debugger_state, handler);
+ if (state->pdata->uart_resume)
+ state->pdata->uart_resume(state->pdev);
+}
+
+#if defined(CONFIG_FIQ_DEBUGGER_CONSOLE)
+struct tty_driver *debug_console_device(struct console *co, int *index)
+{
+ struct fiq_debugger_state *state;
+ state = container_of(co, struct fiq_debugger_state, console);
+ *index = 0;
+ return state->tty_driver;
+}
+
+static void debug_console_write(struct console *co,
+ const char *s, unsigned int count)
+{
+ struct fiq_debugger_state *state;
+
+ state = container_of(co, struct fiq_debugger_state, console);
+
+ if (!state->console_enable && !state->syslog_dumping)
+ return;
+
+ debug_uart_enable(state);
+ while (count--) {
+ if (*s == '\n')
+ state->pdata->uart_putc(state->pdev, '\r');
+ state->pdata->uart_putc(state->pdev, *s++);
+ }
+ debug_uart_flush(state);
+ debug_uart_disable(state);
+}
+
+static struct console fiq_debugger_console = {
+ .name = "ttyFIQ",
+ .device = debug_console_device,
+ .write = debug_console_write,
+ .flags = CON_PRINTBUFFER | CON_ANYTIME | CON_ENABLED,
+};
+
+int fiq_tty_open(struct tty_struct *tty, struct file *filp)
+{
+ struct fiq_debugger_state *state = tty->driver->driver_state;
+ if (state->tty_open_count++)
+ return 0;
+
+ tty->driver_data = state;
+ state->tty = tty;
+ return 0;
+}
+
+void fiq_tty_close(struct tty_struct *tty, struct file *filp)
+{
+ struct fiq_debugger_state *state = tty->driver_data;
+ if (--state->tty_open_count)
+ return;
+ state->tty = NULL;
+}
+
+int fiq_tty_write(struct tty_struct *tty, const unsigned char *buf, int count)
+{
+ int i;
+ struct fiq_debugger_state *state = tty->driver_data;
+
+ if (!state->console_enable)
+ return count;
+
+ debug_uart_enable(state);
+ for (i = 0; i < count; i++)
+ state->pdata->uart_putc(state->pdev, *buf++);
+ debug_uart_disable(state);
+
+ return count;
+}
+
+int fiq_tty_write_room(struct tty_struct *tty)
+{
+ return 1024;
+}
+
+static const struct tty_operations fiq_tty_driver_ops = {
+ .write = fiq_tty_write,
+ .write_room = fiq_tty_write_room,
+ .open = fiq_tty_open,
+ .close = fiq_tty_close,
+};
+
+static int fiq_debugger_tty_init(struct fiq_debugger_state *state)
+{
+ int ret = -EINVAL;
+
+ state->tty_driver = alloc_tty_driver(1);
+ if (!state->tty_driver) {
+ pr_err("Failed to allocate fiq debugger tty\n");
+ return -ENOMEM;
+ }
+
+ state->tty_driver->owner = THIS_MODULE;
+ state->tty_driver->driver_name = "fiq-debugger";
+ state->tty_driver->name = "ttyFIQ";
+ state->tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
+ state->tty_driver->subtype = SERIAL_TYPE_NORMAL;
+ state->tty_driver->init_termios = tty_std_termios;
+ state->tty_driver->init_termios.c_cflag =
+ B115200 | CS8 | CREAD | HUPCL | CLOCAL;
+ state->tty_driver->init_termios.c_ispeed =
+ state->tty_driver->init_termios.c_ospeed = 115200;
+ state->tty_driver->flags = TTY_DRIVER_REAL_RAW;
+ tty_set_operations(state->tty_driver, &fiq_tty_driver_ops);
+ state->tty_driver->driver_state = state;
+
+ ret = tty_register_driver(state->tty_driver);
+ if (ret) {
+ pr_err("Failed to register fiq tty: %d\n", ret);
+ goto err;
+ }
+
+ state->tty_rbuf = fiq_debugger_ringbuf_alloc(1024);
+ if (!state->tty_rbuf) {
+ pr_err("Failed to allocate fiq debugger ringbuf\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ pr_info("Registered FIQ tty driver %p\n", state->tty_driver);
+ return 0;
+
+err:
+ fiq_debugger_ringbuf_free(state->tty_rbuf);
+ state->tty_rbuf = NULL;
+ put_tty_driver(state->tty_driver);
+ return ret;
+}
+#endif
+
+static int fiq_debugger_dev_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct fiq_debugger_state *state = platform_get_drvdata(pdev);
+
+ if (state->pdata->uart_dev_suspend)
+ return state->pdata->uart_dev_suspend(pdev);
+ return 0;
+}
+
+static int fiq_debugger_dev_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct fiq_debugger_state *state = platform_get_drvdata(pdev);
+
+ if (state->pdata->uart_dev_resume)
+ return state->pdata->uart_dev_resume(pdev);
+ return 0;
+}
+
+static int fiq_debugger_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct fiq_debugger_pdata *pdata = dev_get_platdata(&pdev->dev);
+ struct fiq_debugger_state *state;
+ int fiq;
+ int uart_irq;
+
+ if (!pdata->uart_getc || !pdata->uart_putc)
+ return -EINVAL;
+ if ((pdata->uart_enable && !pdata->uart_disable) ||
+ (!pdata->uart_enable && pdata->uart_disable))
+ return -EINVAL;
+
+ fiq = platform_get_irq_byname(pdev, "fiq");
+ uart_irq = platform_get_irq_byname(pdev, "uart_irq");
+
+ /* uart_irq mode and fiq mode are mutually exclusive, but one of them
+ * is required */
+ if ((uart_irq < 0 && fiq < 0) || (uart_irq >= 0 && fiq >= 0))
+ return -EINVAL;
+ if (fiq >= 0 && !pdata->fiq_enable)
+ return -EINVAL;
+
+ state = kzalloc(sizeof(*state), GFP_KERNEL);
+ setup_timer(&state->sleep_timer, sleep_timer_expired,
+ (unsigned long)state);
+ state->pdata = pdata;
+ state->pdev = pdev;
+ state->no_sleep = initial_no_sleep;
+ state->debug_enable = initial_debug_enable;
+ state->console_enable = initial_console_enable;
+
+ state->fiq = fiq;
+ state->uart_irq = uart_irq;
+ state->signal_irq = platform_get_irq_byname(pdev, "signal");
+ state->wakeup_irq = platform_get_irq_byname(pdev, "wakeup");
+
+ platform_set_drvdata(pdev, state);
+
+ spin_lock_init(&state->sleep_timer_lock);
+
+ if (state->wakeup_irq < 0 && debug_have_fiq(state))
+ state->no_sleep = true;
+ state->ignore_next_wakeup_irq = !state->no_sleep;
+
+ wake_lock_init(&state->debugger_wake_lock,
+ WAKE_LOCK_SUSPEND, "serial-debug");
+
+ state->clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(state->clk))
+ state->clk = NULL;
+
+ /* do not call pdata->uart_enable here since uart_init may still
+ * need to do some initialization before uart_enable can work.
+ * So, only try to manage the clock during init.
+ */
+ if (state->clk)
+ clk_enable(state->clk);
+
+ if (pdata->uart_init) {
+ ret = pdata->uart_init(pdev);
+ if (ret)
+ goto err_uart_init;
+ }
+
+ debug_printf_nfiq(state, "<hit enter %sto activate fiq debugger>\n",
+ state->no_sleep ? "" : "twice ");
+
+ if (debug_have_fiq(state)) {
+ state->handler.fiq = debug_fiq;
+ state->handler.resume = debug_resume;
+ ret = fiq_glue_register_handler(&state->handler);
+ if (ret) {
+ pr_err("%s: could not install fiq handler\n", __func__);
+ goto err_register_fiq;
+ }
+
+ pdata->fiq_enable(pdev, state->fiq, 1);
+ } else {
+ ret = request_irq(state->uart_irq, debug_uart_irq,
+ IRQF_NO_SUSPEND, "debug", state);
+ if (ret) {
+ pr_err("%s: could not install irq handler\n", __func__);
+ goto err_register_irq;
+ }
+
+ /* for irq-only mode, we want this irq to wake us up, if it
+ * can.
+ */
+ enable_irq_wake(state->uart_irq);
+ }
+
+ if (state->clk)
+ clk_disable(state->clk);
+
+ if (state->signal_irq >= 0) {
+ ret = request_irq(state->signal_irq, debug_signal_irq,
+ IRQF_TRIGGER_RISING, "debug-signal", state);
+ if (ret)
+ pr_err("serial_debugger: could not install signal_irq");
+ }
+
+ if (state->wakeup_irq >= 0) {
+ ret = request_irq(state->wakeup_irq, wakeup_irq_handler,
+ IRQF_TRIGGER_FALLING | IRQF_DISABLED,
+ "debug-wakeup", state);
+ if (ret) {
+ pr_err("serial_debugger: "
+ "could not install wakeup irq\n");
+ state->wakeup_irq = -1;
+ } else {
+ ret = enable_irq_wake(state->wakeup_irq);
+ if (ret) {
+ pr_err("serial_debugger: "
+ "could not enable wakeup\n");
+ state->wakeup_irq_no_set_wake = true;
+ }
+ }
+ }
+ if (state->no_sleep)
+ handle_wakeup(state);
+
+#if defined(CONFIG_FIQ_DEBUGGER_CONSOLE)
+ state->console = fiq_debugger_console;
+ register_console(&state->console);
+ fiq_debugger_tty_init(state);
+#endif
+ return 0;
+
+err_register_irq:
+err_register_fiq:
+ if (pdata->uart_free)
+ pdata->uart_free(pdev);
+err_uart_init:
+ if (state->clk)
+ clk_disable(state->clk);
+ if (state->clk)
+ clk_put(state->clk);
+ wake_lock_destroy(&state->debugger_wake_lock);
+ platform_set_drvdata(pdev, NULL);
+ kfree(state);
+ return ret;
+}
+
+static const struct dev_pm_ops fiq_debugger_dev_pm_ops = {
+ .suspend = fiq_debugger_dev_suspend,
+ .resume = fiq_debugger_dev_resume,
+};
+
+static struct platform_driver fiq_debugger_driver = {
+ .probe = fiq_debugger_probe,
+ .driver = {
+ .name = "fiq_debugger",
+ .pm = &fiq_debugger_dev_pm_ops,
+ },
+};
+
+static int __init fiq_debugger_init(void)
+{
+ return platform_driver_register(&fiq_debugger_driver);
+}
+
+postcore_initcall(fiq_debugger_init);
diff --git a/arch/arm/common/fiq_debugger_ringbuf.h b/arch/arm/common/fiq_debugger_ringbuf.h
new file mode 100644
index 000000000000..2649b5581088
--- /dev/null
+++ b/arch/arm/common/fiq_debugger_ringbuf.h
@@ -0,0 +1,94 @@
+/*
+ * arch/arm/common/fiq_debugger_ringbuf.c
+ *
+ * simple lockless ringbuffer
+ *
+ * Copyright (C) 2010 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/slab.h>
+
+struct fiq_debugger_ringbuf {
+ int len;
+ int head;
+ int tail;
+ u8 buf[];
+};
+
+
+static inline struct fiq_debugger_ringbuf *fiq_debugger_ringbuf_alloc(int len)
+{
+ struct fiq_debugger_ringbuf *rbuf;
+
+ rbuf = kzalloc(sizeof(*rbuf) + len, GFP_KERNEL);
+ if (rbuf == NULL)
+ return NULL;
+
+ rbuf->len = len;
+ rbuf->head = 0;
+ rbuf->tail = 0;
+ smp_mb();
+
+ return rbuf;
+}
+
+static inline void fiq_debugger_ringbuf_free(struct fiq_debugger_ringbuf *rbuf)
+{
+ kfree(rbuf);
+}
+
+static inline int fiq_debugger_ringbuf_level(struct fiq_debugger_ringbuf *rbuf)
+{
+ int level = rbuf->head - rbuf->tail;
+
+ if (level < 0)
+ level = rbuf->len + level;
+
+ return level;
+}
+
+static inline int fiq_debugger_ringbuf_room(struct fiq_debugger_ringbuf *rbuf)
+{
+ return rbuf->len - fiq_debugger_ringbuf_level(rbuf) - 1;
+}
+
+static inline u8
+fiq_debugger_ringbuf_peek(struct fiq_debugger_ringbuf *rbuf, int i)
+{
+ return rbuf->buf[(rbuf->tail + i) % rbuf->len];
+}
+
+static inline int
+fiq_debugger_ringbuf_consume(struct fiq_debugger_ringbuf *rbuf, int count)
+{
+ count = min(count, fiq_debugger_ringbuf_level(rbuf));
+
+ rbuf->tail = (rbuf->tail + count) % rbuf->len;
+ smp_mb();
+
+ return count;
+}
+
+static inline int
+fiq_debugger_ringbuf_push(struct fiq_debugger_ringbuf *rbuf, u8 datum)
+{
+ if (fiq_debugger_ringbuf_room(rbuf) == 0)
+ return 0;
+
+ rbuf->buf[rbuf->head] = datum;
+ smp_mb();
+ rbuf->head = (rbuf->head + 1) % rbuf->len;
+ smp_mb();
+
+ return 1;
+}
diff --git a/arch/arm/common/fiq_glue.S b/arch/arm/common/fiq_glue.S
new file mode 100644
index 000000000000..9e3455a09f8f
--- /dev/null
+++ b/arch/arm/common/fiq_glue.S
@@ -0,0 +1,111 @@
+/*
+ * 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/linkage.h>
+#include <asm/assembler.h>
+
+ .text
+
+ .global fiq_glue_end
+
+ /* fiq stack: r0-r15,cpsr,spsr of interrupted mode */
+
+ENTRY(fiq_glue)
+ /* store pc, cpsr from previous mode */
+ mrs r12, spsr
+ sub r11, lr, #4
+ subs r10, #1
+ bne nested_fiq
+
+ stmfd sp!, {r11-r12, lr}
+
+ /* store r8-r14 from previous mode */
+ sub sp, sp, #(7 * 4)
+ stmia sp, {r8-r14}^
+ nop
+
+ /* store r0-r7 from previous mode */
+ stmfd sp!, {r0-r7}
+
+ /* setup func(data,regs) arguments */
+ mov r0, r9
+ mov r1, sp
+ mov r3, r8
+
+ mov r7, sp
+
+ /* Get sp and lr from non-user modes */
+ and r4, r12, #MODE_MASK
+ cmp r4, #USR_MODE
+ beq fiq_from_usr_mode
+
+ mov r7, sp
+ orr r4, r4, #(PSR_I_BIT | PSR_F_BIT)
+ msr cpsr_c, r4
+ str sp, [r7, #(4 * 13)]
+ str lr, [r7, #(4 * 14)]
+ mrs r5, spsr
+ str r5, [r7, #(4 * 17)]
+
+ cmp r4, #(SVC_MODE | PSR_I_BIT | PSR_F_BIT)
+ /* use fiq stack if we reenter this mode */
+ subne sp, r7, #(4 * 3)
+
+fiq_from_usr_mode:
+ msr cpsr_c, #(SVC_MODE | PSR_I_BIT | PSR_F_BIT)
+ mov r2, sp
+ sub sp, r7, #12
+ stmfd sp!, {r2, ip, lr}
+ /* call func(data,regs) */
+ blx r3
+ ldmfd sp, {r2, ip, lr}
+ mov sp, r2
+
+ /* restore/discard saved state */
+ cmp r4, #USR_MODE
+ beq fiq_from_usr_mode_exit
+
+ msr cpsr_c, r4
+ ldr sp, [r7, #(4 * 13)]
+ ldr lr, [r7, #(4 * 14)]
+ msr spsr_cxsf, r5
+
+fiq_from_usr_mode_exit:
+ msr cpsr_c, #(FIQ_MODE | PSR_I_BIT | PSR_F_BIT)
+
+ ldmfd sp!, {r0-r7}
+ add sp, sp, #(7 * 4)
+ ldmfd sp!, {r11-r12, lr}
+exit_fiq:
+ msr spsr_cxsf, r12
+ add r10, #1
+ movs pc, r11
+
+nested_fiq:
+ orr r12, r12, #(PSR_F_BIT)
+ b exit_fiq
+
+fiq_glue_end:
+
+ENTRY(fiq_glue_setup) /* func, data, sp */
+ mrs r3, cpsr
+ msr cpsr_c, #(FIQ_MODE | PSR_I_BIT | PSR_F_BIT)
+ movs r8, r0
+ mov r9, r1
+ mov sp, r2
+ moveq r10, #0
+ movne r10, #1
+ msr cpsr_c, r3
+ bx lr
+
diff --git a/arch/arm/common/fiq_glue_setup.c b/arch/arm/common/fiq_glue_setup.c
new file mode 100644
index 000000000000..59586861a636
--- /dev/null
+++ b/arch/arm/common/fiq_glue_setup.c
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2010 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/percpu.h>
+#include <linux/slab.h>
+#include <linux/syscore_ops.h>
+#include <asm/cpu_pm.h>
+#include <asm/fiq.h>
+#include <asm/fiq_glue.h>
+
+extern unsigned char fiq_glue, fiq_glue_end;
+extern void fiq_glue_setup(void *func, void *data, void *sp);
+
+static struct fiq_handler fiq_debbuger_fiq_handler = {
+ .name = "fiq_glue",
+};
+DEFINE_PER_CPU(void *, fiq_stack);
+static struct fiq_glue_handler *current_handler;
+static DEFINE_MUTEX(fiq_glue_lock);
+
+static void fiq_glue_setup_helper(void *info)
+{
+ struct fiq_glue_handler *handler = info;
+ fiq_glue_setup(handler->fiq, handler,
+ __get_cpu_var(fiq_stack) + THREAD_START_SP);
+}
+
+int fiq_glue_register_handler(struct fiq_glue_handler *handler)
+{
+ int ret;
+ int cpu;
+
+ if (!handler || !handler->fiq)
+ return -EINVAL;
+
+ mutex_lock(&fiq_glue_lock);
+ if (fiq_stack) {
+ ret = -EBUSY;
+ goto err_busy;
+ }
+
+ for_each_possible_cpu(cpu) {
+ void *stack;
+ stack = (void *)__get_free_pages(GFP_KERNEL, THREAD_SIZE_ORDER);
+ if (WARN_ON(!stack)) {
+ ret = -ENOMEM;
+ goto err_alloc_fiq_stack;
+ }
+ per_cpu(fiq_stack, cpu) = stack;
+ }
+
+ ret = claim_fiq(&fiq_debbuger_fiq_handler);
+ if (WARN_ON(ret))
+ goto err_claim_fiq;
+
+ current_handler = handler;
+ on_each_cpu(fiq_glue_setup_helper, handler, true);
+ set_fiq_handler(&fiq_glue, &fiq_glue_end - &fiq_glue);
+
+ mutex_unlock(&fiq_glue_lock);
+ return 0;
+
+err_claim_fiq:
+err_alloc_fiq_stack:
+ for_each_possible_cpu(cpu) {
+ __free_pages(per_cpu(fiq_stack, cpu), THREAD_SIZE_ORDER);
+ per_cpu(fiq_stack, cpu) = NULL;
+ }
+err_busy:
+ mutex_unlock(&fiq_glue_lock);
+ return ret;
+}
+
+/**
+ * fiq_glue_resume - Restore fiqs after suspend or low power idle states
+ *
+ * This must be called before calling local_fiq_enable after returning from a
+ * power state where the fiq mode registers were lost. If a driver provided
+ * a resume hook when it registered the handler it will be called.
+ */
+
+void fiq_glue_resume(void)
+{
+ if (!current_handler)
+ return;
+ fiq_glue_setup(current_handler->fiq, current_handler,
+ __get_cpu_var(fiq_stack) + THREAD_START_SP);
+ if (current_handler->resume)
+ current_handler->resume(current_handler);
+}
+
+static int fiq_glue_cpu_pm_notify(struct notifier_block *self, unsigned long cmd,
+ void *v)
+{
+ switch (cmd) {
+ case CPU_PM_ENTER:
+ //pr_info("cpu pm enter %d\n", smp_processor_id());
+ local_fiq_disable();
+ break;
+ case CPU_PM_ENTER_FAILED:
+ case CPU_PM_EXIT:
+ fiq_glue_resume();
+ local_fiq_enable();
+ //pr_info("cpu pm exit %d\n", smp_processor_id());
+ break;
+ }
+ return NOTIFY_OK;
+}
+
+static struct notifier_block fiq_glue_cpu_pm_notifier = {
+ .notifier_call = fiq_glue_cpu_pm_notify,
+};
+
+static int __init fiq_glue_cpu_pm_init(void)
+{
+ return cpu_pm_register_notifier(&fiq_glue_cpu_pm_notifier);
+}
+core_initcall(fiq_glue_cpu_pm_init);
+
+#ifdef CONFIG_PM
+static int fiq_glue_syscore_suspend(void)
+{
+ local_fiq_disable();
+ return 0;
+}
+
+static void fiq_glue_syscore_resume(void)
+{
+ fiq_glue_resume();
+ local_fiq_enable();
+}
+
+static struct syscore_ops fiq_glue_syscore_ops = {
+ .suspend = fiq_glue_syscore_suspend,
+ .resume = fiq_glue_syscore_resume,
+};
+
+static int __init fiq_glue_syscore_init(void)
+{
+ register_syscore_ops(&fiq_glue_syscore_ops);
+ return 0;
+}
+late_initcall(fiq_glue_syscore_init);
+#endif
diff --git a/arch/arm/common/gic.c b/arch/arm/common/gic.c
index 3227ca952a12..60e6175dc11d 100644
--- a/arch/arm/common/gic.c
+++ b/arch/arm/common/gic.c
@@ -29,6 +29,7 @@
#include <linux/cpumask.h>
#include <linux/io.h>
+#include <asm/cpu_pm.h>
#include <asm/irq.h>
#include <asm/mach/irq.h>
#include <asm/hardware/gic.h>
@@ -276,6 +277,8 @@ static void __init gic_dist_init(struct gic_chip_data *gic,
if (gic_irqs > 1020)
gic_irqs = 1020;
+ gic->gic_irqs = gic_irqs;
+
/*
* Set all global interrupts to be level triggered, active low.
*/
@@ -343,6 +346,180 @@ static void __cpuinit gic_cpu_init(struct gic_chip_data *gic)
writel_relaxed(1, base + GIC_CPU_CTRL);
}
+/*
+ * Saves the GIC distributor registers during suspend or idle. Must be called
+ * with interrupts disabled but before powering down the GIC. After calling
+ * this function, no interrupts will be delivered by the GIC, and another
+ * platform-specific wakeup source must be enabled.
+ */
+static void gic_dist_save(unsigned int gic_nr)
+{
+ unsigned int gic_irqs;
+ void __iomem *dist_base;
+ int i;
+
+ if (gic_nr >= MAX_GIC_NR)
+ BUG();
+
+ gic_irqs = gic_data[gic_nr].gic_irqs;
+ dist_base = gic_data[gic_nr].dist_base;
+
+ if (!dist_base)
+ return;
+
+ for (i = 0; i < DIV_ROUND_UP(gic_irqs, 16); i++)
+ gic_data[gic_nr].saved_spi_conf[i] =
+ readl_relaxed(dist_base + GIC_DIST_CONFIG + i * 4);
+
+ for (i = 0; i < DIV_ROUND_UP(gic_irqs, 4); i++)
+ gic_data[gic_nr].saved_spi_pri[i] =
+ readl_relaxed(dist_base + GIC_DIST_PRI + i * 4);
+
+ for (i = 0; i < DIV_ROUND_UP(gic_irqs, 4); i++)
+ gic_data[gic_nr].saved_spi_target[i] =
+ readl_relaxed(dist_base + GIC_DIST_TARGET + i * 4);
+
+ for (i = 0; i < DIV_ROUND_UP(gic_irqs, 32); i++)
+ gic_data[gic_nr].saved_spi_enable[i] =
+ readl_relaxed(dist_base + GIC_DIST_ENABLE_SET + i * 4);
+
+ writel_relaxed(0, dist_base + GIC_DIST_CTRL);
+}
+
+/*
+ * Restores the GIC distributor registers during resume or when coming out of
+ * idle. Must be called before enabling interrupts. If a level interrupt
+ * that occured while the GIC was suspended is still present, it will be
+ * handled normally, but any edge interrupts that occured will not be seen by
+ * the GIC and need to be handled by the platform-specific wakeup source.
+ */
+static void gic_dist_restore(unsigned int gic_nr)
+{
+ unsigned int gic_irqs;
+ unsigned int i;
+ void __iomem *dist_base;
+
+ if (gic_nr >= MAX_GIC_NR)
+ BUG();
+
+ gic_irqs = gic_data[gic_nr].gic_irqs;
+ dist_base = gic_data[gic_nr].dist_base;
+
+ if (!dist_base)
+ return;
+
+ writel_relaxed(0, dist_base + GIC_DIST_CTRL);
+
+ for (i = 0; i < DIV_ROUND_UP(gic_irqs, 16); i++)
+ writel_relaxed(gic_data[gic_nr].saved_spi_conf[i],
+ dist_base + GIC_DIST_CONFIG + i * 4);
+
+ for (i = 0; i < DIV_ROUND_UP(gic_irqs, 4); i++)
+ writel_relaxed(gic_data[gic_nr].saved_spi_pri[i],
+ dist_base + GIC_DIST_PRI + i * 4);
+
+ for (i = 0; i < DIV_ROUND_UP(gic_irqs, 4); i++)
+ writel_relaxed(gic_data[gic_nr].saved_spi_target[i],
+ dist_base + GIC_DIST_TARGET + i * 4);
+
+ for (i = 0; i < DIV_ROUND_UP(gic_irqs, 32); i++)
+ writel_relaxed(gic_data[gic_nr].saved_spi_enable[i],
+ dist_base + GIC_DIST_ENABLE_SET + i * 4);
+
+ writel_relaxed(1, dist_base + GIC_DIST_CTRL);
+}
+
+static void gic_cpu_save(unsigned int gic_nr)
+{
+ int i;
+ u32 *ptr;
+ void __iomem *dist_base;
+ void __iomem *cpu_base;
+
+ if (gic_nr >= MAX_GIC_NR)
+ BUG();
+
+ dist_base = gic_data[gic_nr].dist_base;
+ cpu_base = gic_data[gic_nr].cpu_base;
+
+ if (!dist_base || !cpu_base)
+ return;
+
+ ptr = __this_cpu_ptr(gic_data[gic_nr].saved_ppi_enable);
+ for (i = 0; i < DIV_ROUND_UP(32, 32); i++)
+ ptr[i] = readl_relaxed(dist_base + GIC_DIST_ENABLE_SET + i * 4);
+
+ ptr = __this_cpu_ptr(gic_data[gic_nr].saved_ppi_conf);
+ for (i = 0; i < DIV_ROUND_UP(32, 16); i++)
+ ptr[i] = readl_relaxed(dist_base + GIC_DIST_CONFIG + i * 4);
+
+ ptr = __this_cpu_ptr(gic_data[gic_nr].saved_ppi_pri);
+ for (i = 0; i < DIV_ROUND_UP(32, 4); i++)
+ ptr[i] = readl_relaxed(dist_base + GIC_DIST_PRI + i * 4);
+}
+
+static void gic_cpu_restore(unsigned int gic_nr)
+{
+ int i;
+ u32 *ptr;
+ void __iomem *dist_base;
+ void __iomem *cpu_base;
+
+ if (gic_nr >= MAX_GIC_NR)
+ BUG();
+
+ dist_base = gic_data[gic_nr].dist_base;
+ cpu_base = gic_data[gic_nr].cpu_base;
+
+ if (!dist_base || !cpu_base)
+ return;
+
+ ptr = __this_cpu_ptr(gic_data[gic_nr].saved_ppi_enable);
+ for (i = 0; i < DIV_ROUND_UP(32, 32); i++)
+ writel_relaxed(ptr[i], dist_base + GIC_DIST_ENABLE_SET + i * 4);
+
+ ptr = __this_cpu_ptr(gic_data[gic_nr].saved_ppi_conf);
+ for (i = 0; i < DIV_ROUND_UP(32, 16); i++)
+ writel_relaxed(ptr[i], dist_base + GIC_DIST_CONFIG + i * 4);
+
+ ptr = __this_cpu_ptr(gic_data[gic_nr].saved_ppi_pri);
+ for (i = 0; i < DIV_ROUND_UP(32, 4); i++)
+ writel_relaxed(ptr[i], dist_base + GIC_DIST_PRI + i * 4);
+
+ writel_relaxed(0xf0, cpu_base + GIC_CPU_PRIMASK);
+ writel_relaxed(1, cpu_base + GIC_CPU_CTRL);
+}
+
+static int gic_notifier(struct notifier_block *self, unsigned long cmd, void *v)
+{
+ int i;
+
+ for (i = 0; i < MAX_GIC_NR; i++) {
+ switch (cmd) {
+ case CPU_PM_ENTER:
+ gic_cpu_save(i);
+ break;
+ case CPU_PM_ENTER_FAILED:
+ case CPU_PM_EXIT:
+ gic_cpu_restore(i);
+ break;
+ case CPU_COMPLEX_PM_ENTER:
+ gic_dist_save(i);
+ break;
+ case CPU_COMPLEX_PM_ENTER_FAILED:
+ case CPU_COMPLEX_PM_EXIT:
+ gic_dist_restore(i);
+ break;
+ }
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block gic_notifier_block = {
+ .notifier_call = gic_notifier,
+};
+
void __init gic_init(unsigned int gic_nr, unsigned int irq_start,
void __iomem *dist_base, void __iomem *cpu_base)
{
@@ -358,8 +535,23 @@ void __init gic_init(unsigned int gic_nr, unsigned int irq_start,
if (gic_nr == 0)
gic_cpu_base_addr = cpu_base;
+ gic_chip.flags |= gic_arch_extn.flags;
gic_dist_init(gic, irq_start);
gic_cpu_init(gic);
+
+ gic->saved_ppi_enable = __alloc_percpu(DIV_ROUND_UP(32, 32) * 4,
+ sizeof(u32));
+ BUG_ON(!gic->saved_ppi_enable);
+
+ gic->saved_ppi_conf = __alloc_percpu(DIV_ROUND_UP(32, 16) * 4,
+ sizeof(u32));
+ BUG_ON(!gic->saved_ppi_conf);
+
+ gic->saved_ppi_pri = __alloc_percpu(DIV_ROUND_UP(32, 4) * 4,
+ sizeof(u32));
+ BUG_ON(!gic->saved_ppi_pri);
+
+ cpu_pm_register_notifier(&gic_notifier_block);
}
void __cpuinit gic_secondary_init(unsigned int gic_nr)
diff --git a/arch/arm/configs/tegra3_android_defconfig b/arch/arm/configs/tegra3_android_defconfig
new file mode 100644
index 000000000000..927c18c4f4dd
--- /dev/null
+++ b/arch/arm/configs/tegra3_android_defconfig
@@ -0,0 +1,412 @@
+CONFIG_EXPERIMENTAL=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_CGROUPS=y
+CONFIG_CGROUP_DEBUG=y
+CONFIG_CGROUP_FREEZER=y
+CONFIG_CGROUP_CPUACCT=y
+CONFIG_RESOURCE_COUNTERS=y
+CONFIG_CGROUP_SCHED=y
+CONFIG_RT_GROUP_SCHED=y
+CONFIG_BLK_DEV_INITRD=y
+# CONFIG_SYSCTL_SYSCALL is not set
+# CONFIG_ELF_CORE is not set
+CONFIG_ASHMEM=y
+CONFIG_EMBEDDED=y
+CONFIG_SLAB=y
+CONFIG_PROFILING=y
+CONFIG_OPROFILE=y
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODULE_FORCE_UNLOAD=y
+# CONFIG_BLK_DEV_BSG is not set
+# CONFIG_IOSCHED_DEADLINE is not set
+# CONFIG_IOSCHED_CFQ is not set
+CONFIG_ARCH_TEGRA=y
+CONFIG_GPIO_PCA953X=y
+CONFIG_ARCH_TEGRA_3x_SOC=y
+CONFIG_TEGRA_PCI=y
+CONFIG_MACH_CARDHU=y
+CONFIG_MACH_TEGRA_ENTERPRISE=y
+CONFIG_TEGRA_PWM=y
+CONFIG_TEGRA_EMC_SCALING_ENABLE=y
+CONFIG_TEGRA_CLOCK_DEBUG_WRITE=y
+CONFIG_TEGRA_CONVSERVATIVE_GOV_ON_EARLYSUPSEND=y
+CONFIG_USB_HOTPLUG=y
+CONFIG_TEGRA_DYNAMIC_PWRDET=y
+CONFIG_TEGRA_USB_MODEM_POWER=y
+CONFIG_TEGRA_BB_XMM_POWER=y
+CONFIG_TEGRA_BB_XMM_POWER2=m
+CONFIG_TEGRA_PLLM_RESTRICTED=y
+CONFIG_ARM_ERRATA_742230=y
+CONFIG_ARM_ERRATA_743622=y
+CONFIG_ARM_ERRATA_751472=y
+CONFIG_ARM_ERRATA_752520=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_SMP=y
+CONFIG_PREEMPT=y
+CONFIG_AEABI=y
+# CONFIG_OABI_COMPAT is not set
+CONFIG_HIGHMEM=y
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZBOOT_ROM_BSS=0x0
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y
+CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
+CONFIG_CPU_FREQ_GOV_POWERSAVE=y
+CONFIG_CPU_FREQ_GOV_ONDEMAND=y
+CONFIG_CPU_FREQ_GOV_INTERACTIVE=y
+CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
+CONFIG_CPU_IDLE=y
+CONFIG_VFP=y
+CONFIG_NEON=y
+CONFIG_WAKELOCK=y
+CONFIG_PM_RUNTIME=y
+CONFIG_SUSPEND_TIME=y
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_NET_KEY=y
+CONFIG_INET=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+CONFIG_IP_PNP_RARP=y
+CONFIG_INET_ESP=y
+# CONFIG_INET_XFRM_MODE_TUNNEL is not set
+# CONFIG_INET_XFRM_MODE_BEET is not set
+# CONFIG_INET_LRO is not set
+# CONFIG_INET_DIAG is not set
+CONFIG_IPV6=y
+CONFIG_IPV6_PRIVACY=y
+CONFIG_IPV6_ROUTER_PREF=y
+CONFIG_IPV6_OPTIMISTIC_DAD=y
+CONFIG_INET6_AH=y
+CONFIG_INET6_ESP=y
+CONFIG_INET6_IPCOMP=y
+CONFIG_IPV6_MIP6=y
+CONFIG_IPV6_TUNNEL=y
+CONFIG_IPV6_MULTIPLE_TABLES=y
+CONFIG_NETFILTER=y
+CONFIG_NETFILTER_NETLINK_LOG=y
+CONFIG_NF_CONNTRACK=y
+CONFIG_NF_CONNTRACK_EVENTS=y
+CONFIG_NF_CT_PROTO_DCCP=y
+CONFIG_NF_CT_PROTO_SCTP=y
+CONFIG_NF_CT_PROTO_UDPLITE=y
+CONFIG_NF_CONNTRACK_AMANDA=y
+CONFIG_NF_CONNTRACK_FTP=y
+CONFIG_NF_CONNTRACK_H323=y
+CONFIG_NF_CONNTRACK_IRC=y
+CONFIG_NF_CONNTRACK_NETBIOS_NS=y
+CONFIG_NF_CONNTRACK_PPTP=y
+CONFIG_NF_CONNTRACK_SANE=y
+CONFIG_NF_CONNTRACK_SIP=y
+CONFIG_NF_CONNTRACK_TFTP=y
+CONFIG_NETFILTER_TPROXY=y
+CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y
+CONFIG_NETFILTER_XT_TARGET_CONNMARK=y
+CONFIG_NETFILTER_XT_TARGET_MARK=y
+CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y
+CONFIG_NETFILTER_XT_MATCH_COMMENT=y
+CONFIG_NETFILTER_XT_MATCH_CONNBYTES=y
+CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y
+CONFIG_NETFILTER_XT_MATCH_CONNMARK=y
+CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y
+CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y
+CONFIG_NETFILTER_XT_MATCH_HELPER=y
+CONFIG_NETFILTER_XT_MATCH_IPRANGE=y
+CONFIG_NETFILTER_XT_MATCH_LENGTH=y
+CONFIG_NETFILTER_XT_MATCH_LIMIT=y
+CONFIG_NETFILTER_XT_MATCH_MAC=y
+CONFIG_NETFILTER_XT_MATCH_MARK=y
+CONFIG_NETFILTER_XT_MATCH_POLICY=y
+CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y
+CONFIG_NETFILTER_XT_MATCH_QTAGUID=y
+CONFIG_NETFILTER_XT_MATCH_QUOTA=y
+CONFIG_NETFILTER_XT_MATCH_SOCKET=y
+CONFIG_NETFILTER_XT_MATCH_STATE=y
+CONFIG_NETFILTER_XT_MATCH_STATISTIC=y
+CONFIG_NETFILTER_XT_MATCH_STRING=y
+CONFIG_NETFILTER_XT_MATCH_TIME=y
+CONFIG_NETFILTER_XT_MATCH_U32=y
+CONFIG_NF_CONNTRACK_IPV4=y
+CONFIG_IP_NF_IPTABLES=y
+CONFIG_IP_NF_MATCH_AH=y
+CONFIG_IP_NF_MATCH_ECN=y
+CONFIG_IP_NF_MATCH_TTL=y
+CONFIG_IP_NF_FILTER=y
+CONFIG_IP_NF_TARGET_REJECT=y
+CONFIG_IP_NF_TARGET_LOG=y
+CONFIG_NF_NAT=y
+CONFIG_IP_NF_TARGET_MASQUERADE=y
+CONFIG_IP_NF_TARGET_NETMAP=y
+CONFIG_IP_NF_TARGET_REDIRECT=y
+CONFIG_IP_NF_MANGLE=y
+CONFIG_IP_NF_ARPTABLES=y
+CONFIG_IP_NF_ARPFILTER=y
+CONFIG_IP_NF_ARP_MANGLE=y
+CONFIG_IP6_NF_IPTABLES=y
+CONFIG_NET_SCHED=y
+CONFIG_NET_SCH_HTB=y
+CONFIG_NET_SCH_INGRESS=y
+CONFIG_NET_CLS_U32=y
+CONFIG_NET_EMATCH=y
+CONFIG_NET_EMATCH_U32=y
+CONFIG_NET_CLS_ACT=y
+CONFIG_NET_ACT_POLICE=y
+CONFIG_NET_ACT_GACT=y
+CONFIG_NET_ACT_MIRRED=y
+CONFIG_BT=y
+CONFIG_BT_L2CAP=y
+CONFIG_BT_SCO=y
+CONFIG_BT_RFCOMM=y
+CONFIG_BT_RFCOMM_TTY=y
+CONFIG_BT_BNEP=y
+CONFIG_BT_HIDP=y
+CONFIG_BT_HCIUART=y
+CONFIG_BT_HCIUART_H4=y
+CONFIG_BT_HCIUART_LL=y
+CONFIG_BT_BLUESLEEP=y
+CONFIG_CFG80211=y
+CONFIG_RFKILL=y
+# CONFIG_FIRMWARE_IN_KERNEL is not set
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_MISC_DEVICES=y
+CONFIG_AD525X_DPOT=y
+CONFIG_AD525X_DPOT_I2C=y
+# CONFIG_ANDROID_PMEM is not set
+CONFIG_ICS932S401=y
+CONFIG_APDS9802ALS=y
+CONFIG_SENSORS_NCT1008=y
+CONFIG_UID_STAT=y
+CONFIG_BCM4329_RFKILL=y
+CONFIG_TEGRA_CRYPTO_DEV=y
+CONFIG_MAX1749_VIBRATOR=y
+CONFIG_MPU_SENSORS_TIMERIRQ=y
+CONFIG_INV_SENSORS=y
+CONFIG_MPU_SENSORS_MPU3050=y
+CONFIG_MPU_SENSORS_MPU3050_GYRO=y
+CONFIG_MPU_SENSORS_KXTF9=y
+CONFIG_MPU_SENSORS_AK8975=y
+CONFIG_MPU_SENSORS_PRESSURE_NONE=y
+CONFIG_SCSI=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_BLK_DEV_SR=y
+CONFIG_BLK_DEV_SR_VENDOR=y
+CONFIG_CHR_DEV_SG=y
+CONFIG_SCSI_MULTI_LUN=y
+CONFIG_MD=y
+CONFIG_BLK_DEV_DM=y
+CONFIG_DM_CRYPT=y
+CONFIG_DM_UEVENT=y
+CONFIG_NETDEVICES=y
+CONFIG_DUMMY=y
+CONFIG_R8169=y
+# CONFIG_NETDEV_10000 is not set
+CONFIG_BCM4329=m
+CONFIG_BCM4329_FIRST_SCAN=y
+CONFIG_BCM4329_FW_PATH="/system/vendor/firmware/fw_bcm4329.bin"
+CONFIG_BCM4329_NVRAM_PATH="/system/etc/nvram.txt"
+CONFIG_BCM4329_WIFI_CONTROL_FUNC=y
+CONFIG_BCMDHD=m
+CONFIG_BCMDHD_WIFI_CONTROL_FUNC=y
+CONFIG_BCMDHD_HW_OOB=y
+CONFIG_USB_USBNET=y
+CONFIG_USB_NET_SMSC95XX=y
+# CONFIG_USB_NET_NET1080 is not set
+# CONFIG_USB_BELKIN is not set
+# CONFIG_USB_ARMLINUX is not set
+# CONFIG_USB_NET_ZAURUS is not set
+CONFIG_USB_NET_RAW_IP=m
+CONFIG_PPP=y
+CONFIG_PPP_FILTER=y
+CONFIG_PPP_ASYNC=y
+CONFIG_PPP_SYNC_TTY=y
+CONFIG_PPP_DEFLATE=y
+CONFIG_PPP_BSDCOMP=y
+CONFIG_PPP_MPPE=y
+CONFIG_PPPOLAC=y
+CONFIG_PPPOPNS=y
+# CONFIG_INPUT_MOUSEDEV is not set
+CONFIG_INPUT_JOYDEV=y
+CONFIG_INPUT_EVDEV=y
+CONFIG_INPUT_KEYRESET=y
+# CONFIG_KEYBOARD_ATKBD is not set
+CONFIG_KEYBOARD_GPIO=y
+CONFIG_KEYBOARD_INTERRUPT=y
+CONFIG_KEYBOARD_TEGRA=y
+# CONFIG_INPUT_MOUSE is not set
+CONFIG_INPUT_JOYSTICK=y
+CONFIG_JOYSTICK_XPAD=y
+CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_TOUCHSCREEN_ATMEL_MXT=y
+CONFIG_INPUT_MISC=y
+CONFIG_INPUT_UINPUT=y
+CONFIG_INPUT_GPIO=y
+CONFIG_SERIO_LIBPS2=y
+# CONFIG_VT is not set
+# CONFIG_LEGACY_PTYS is not set
+# CONFIG_DEVKMEM is not set
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_SERIAL_TEGRA=y
+# CONFIG_HW_RANDOM is not set
+# CONFIG_I2C_COMPAT is not set
+CONFIG_I2C_CHARDEV=y
+CONFIG_I2C_MUX=y
+CONFIG_I2C_MUX_PCA954x=y
+# CONFIG_I2C_HELPER_AUTO is not set
+CONFIG_I2C_TEGRA=y
+CONFIG_SPI=y
+CONFIG_SPI_TEGRA=y
+CONFIG_SPI_SLAVE_TEGRA=y
+CONFIG_DEBUG_GPIO=y
+CONFIG_GPIO_SYSFS=y
+CONFIG_POWER_SUPPLY=y
+CONFIG_BATTERY_BQ20Z75=y
+CONFIG_BATTERY_BQ27x00=y
+CONFIG_CHARGER_TPS8003X=y
+CONFIG_BATTERY_GAUGE_TPS8003X=y
+CONFIG_CHARGER_GPIO=y
+CONFIG_SENSORS_TEGRA_TSENSOR=y
+CONFIG_SENSORS_INA219=y
+CONFIG_THERMAL=y
+CONFIG_MFD_MAX77663=y
+CONFIG_MFD_TPS6586X=y
+CONFIG_MFD_TPS6591X=y
+CONFIG_MFD_TPS80031=y
+CONFIG_GPADC_TPS80031=y
+CONFIG_MFD_RICOH583=y
+CONFIG_REGULATOR=y
+CONFIG_REGULATOR_FIXED_VOLTAGE=y
+CONFIG_REGULATOR_VIRTUAL_CONSUMER=y
+CONFIG_REGULATOR_MAX77663=y
+CONFIG_REGULATOR_TPS6586X=y
+CONFIG_REGULATOR_TPS6591X=y
+CONFIG_REGULATOR_TPS6236X=y
+CONFIG_REGULATOR_TPS80031=y
+CONFIG_REGULATOR_RICOH583=y
+CONFIG_REGULATOR_GPIO_SWITCH=y
+CONFIG_MEDIA_SUPPORT=y
+CONFIG_VIDEO_DEV=y
+# CONFIG_RC_CORE is not set
+# CONFIG_MEDIA_TUNER_CUSTOMISE is not set
+CONFIG_VIDEO_HELPER_CHIPS_AUTO=y
+# CONFIG_TEGRA_AVP is not set
+# CONFIG_TEGRA_MEDIASERVER is not set
+CONFIG_TEGRA_NVAVP=y
+CONFIG_VIDEO_OV5650=y
+CONFIG_VIDEO_OV9726=y
+CONFIG_VIDEO_OV2710=y
+CONFIG_VIDEO_AR0832=y
+CONFIG_TORCH_SSL3250A=y
+CONFIG_TORCH_TPS61050=y
+CONFIG_VIDEO_SH532U=y
+CONFIG_USB_VIDEO_CLASS=y
+# CONFIG_USB_GSPCA is not set
+# CONFIG_RADIO_ADAPTERS is not set
+CONFIG_VIDEO_OUTPUT_CONTROL=y
+CONFIG_FB=y
+CONFIG_TEGRA_GRHOST=y
+CONFIG_TEGRA_DC=y
+CONFIG_TEGRA_DSI=y
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+CONFIG_LCD_CLASS_DEVICE=y
+CONFIG_BACKLIGHT_CLASS_DEVICE=y
+# CONFIG_BACKLIGHT_GENERIC is not set
+CONFIG_BACKLIGHT_PWM=y
+CONFIG_BACKLIGHT_TEGRA_PWM=y
+CONFIG_SOUND=y
+CONFIG_SND=y
+CONFIG_SND_HDA_INTEL=y
+CONFIG_SND_HDA_PLATFORM_DRIVER=y
+CONFIG_SND_HDA_PLATFORM_NVIDIA_TEGRA=y
+CONFIG_SND_HDA_POWER_SAVE=y
+CONFIG_SND_HDA_POWER_SAVE_DEFAULT=10
+CONFIG_SND_SOC=y
+CONFIG_SND_SOC_TEGRA=y
+CONFIG_SND_SOC_TEGRA_WM8903=y
+CONFIG_SND_SOC_TEGRA_MAX98088=y
+CONFIG_SND_SOC_TEGRA_TLV320AIC326X=y
+CONFIG_HID_SONY=y
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
+CONFIG_USB_DEVICEFS=y
+CONFIG_USB_SUSPEND=y
+CONFIG_USB_OTG=y
+# CONFIG_USB_OTG_WHITELIST is not set
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_EHCI_ONOFF_FEATURE=y
+CONFIG_USB_ACM=y
+CONFIG_USB_WDM=y
+CONFIG_USB_STORAGE=y
+CONFIG_USB_LIBUSUAL=y
+CONFIG_USB_SERIAL=y
+CONFIG_USB_SERIAL_PL2303=y
+CONFIG_USB_SERIAL_OPTION=y
+CONFIG_USB_SERIAL_BASEBAND=m
+CONFIG_USB_GADGET=y
+CONFIG_USB_GADGET_VBUS_DRAW=500
+CONFIG_USB_FSL_USB2=y
+CONFIG_USB_G_ANDROID=y
+CONFIG_USB_TEGRA_OTG=y
+CONFIG_MMC=y
+CONFIG_MMC_UNSAFE_RESUME=y
+CONFIG_MMC_EMBEDDED_SDIO=y
+CONFIG_MMC_BLOCK_MINORS=16
+CONFIG_MMC_SDHCI=y
+CONFIG_MMC_SDHCI_PLTFM=y
+CONFIG_MMC_SDHCI_TEGRA=y
+CONFIG_NFC_DEVICES=y
+CONFIG_PN544_NFC=y
+CONFIG_SWITCH=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_MAX77663=y
+CONFIG_RTC_DRV_TPS6586X=y
+CONFIG_RTC_DRV_TPS6591x=y
+CONFIG_RTC_DRV_TPS80031=y
+CONFIG_STAGING=y
+CONFIG_ANDROID=y
+CONFIG_ANDROID_BINDER_IPC=y
+CONFIG_ANDROID_LOGGER=y
+CONFIG_ANDROID_RAM_CONSOLE=y
+CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION=y
+CONFIG_ANDROID_TIMED_GPIO=y
+CONFIG_ANDROID_LOW_MEMORY_KILLER=y
+CONFIG_IIO=y
+CONFIG_SENSORS_ISL29028=y
+CONFIG_EXT2_FS=y
+CONFIG_EXT2_FS_XATTR=y
+CONFIG_EXT2_FS_POSIX_ACL=y
+CONFIG_EXT2_FS_SECURITY=y
+CONFIG_EXT3_FS=y
+# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
+CONFIG_EXT3_FS_POSIX_ACL=y
+CONFIG_EXT3_FS_SECURITY=y
+CONFIG_EXT4_FS=y
+CONFIG_EXT4_FS_POSIX_ACL=y
+# CONFIG_DNOTIFY is not set
+CONFIG_FUSE_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_TMPFS=y
+CONFIG_NFS_FS=y
+CONFIG_ROOT_NFS=y
+CONFIG_PARTITION_ADVANCED=y
+CONFIG_EFI_PARTITION=y
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_PRINTK_TIME=y
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_DEBUG_FS=y
+CONFIG_LOCKUP_DETECTOR=y
+CONFIG_SCHEDSTATS=y
+CONFIG_TIMER_STATS=y
+# CONFIG_DEBUG_PREEMPT is not set
+CONFIG_DEBUG_INFO=y
+CONFIG_DEBUG_VM=y
+CONFIG_CRYPTO_TEST=m
+CONFIG_CRYPTO_SHA256=y
+CONFIG_CRYPTO_TWOFISH=y
+# CONFIG_CRYPTO_ANSI_CPRNG is not set
+CONFIG_CRYPTO_DEV_TEGRA_SE=y
diff --git a/arch/arm/configs/tegra3_defconfig b/arch/arm/configs/tegra3_defconfig
new file mode 100644
index 000000000000..9c07b64e7e30
--- /dev/null
+++ b/arch/arm/configs/tegra3_defconfig
@@ -0,0 +1,277 @@
+CONFIG_EXPERIMENTAL=y
+CONFIG_SYSVIPC=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_CGROUPS=y
+CONFIG_CGROUP_DEBUG=y
+CONFIG_CGROUP_FREEZER=y
+CONFIG_CGROUP_CPUACCT=y
+CONFIG_RESOURCE_COUNTERS=y
+CONFIG_CGROUP_SCHED=y
+CONFIG_RT_GROUP_SCHED=y
+CONFIG_BLK_DEV_INITRD=y
+# CONFIG_SYSCTL_SYSCALL is not set
+CONFIG_KALLSYMS_ALL=y
+CONFIG_KALLSYMS_EXTRA_PASS=y
+# CONFIG_ELF_CORE is not set
+CONFIG_EMBEDDED=y
+CONFIG_SLAB=y
+CONFIG_PROFILING=y
+CONFIG_OPROFILE=y
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODULE_FORCE_UNLOAD=y
+# CONFIG_BLK_DEV_BSG is not set
+# CONFIG_IOSCHED_DEADLINE is not set
+# CONFIG_IOSCHED_CFQ is not set
+CONFIG_ARCH_TEGRA=y
+CONFIG_GPIO_PCA953X=y
+CONFIG_ARCH_TEGRA_3x_SOC=y
+CONFIG_MACH_CARDHU=y
+CONFIG_MACH_TEGRA_ENTERPRISE=y
+CONFIG_TEGRA_EMC_TO_DDR_CLOCK=2
+CONFIG_USB_HOTPLUG=y
+CONFIG_ARM_ERRATA_742230=y
+CONFIG_ARM_ERRATA_743622=y
+CONFIG_ARM_ERRATA_751472=y
+CONFIG_ARM_ERRATA_752520=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_SMP=y
+CONFIG_NR_CPUS=4
+CONFIG_PREEMPT=y
+CONFIG_AEABI=y
+# CONFIG_OABI_COMPAT is not set
+CONFIG_HIGHMEM=y
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZBOOT_ROM_BSS=0x0
+CONFIG_VFP=y
+CONFIG_NEON=y
+CONFIG_PM_RUNTIME=y
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=m
+CONFIG_NET_KEY=m
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+CONFIG_IP_PNP_RARP=y
+CONFIG_INET_AH=m
+CONFIG_INET_ESP=m
+CONFIG_INET_IPCOMP=m
+CONFIG_INET_XFRM_MODE_TRANSPORT=m
+CONFIG_INET_XFRM_MODE_TUNNEL=m
+CONFIG_INET_XFRM_MODE_BEET=m
+# CONFIG_INET_LRO is not set
+# CONFIG_INET_DIAG is not set
+CONFIG_TCP_CONG_ADVANCED=y
+CONFIG_TCP_CONG_CUBIC=m
+# CONFIG_TCP_CONG_HTCP is not set
+CONFIG_TCP_CONG_VEGAS=m
+CONFIG_TCP_CONG_VENO=m
+CONFIG_TCP_CONG_ILLINOIS=m
+# CONFIG_IPV6 is not set
+# CONFIG_ANDROID_PARANOID_NETWORK is not set
+# CONFIG_NET_ACTIVITY_STATS is not set
+CONFIG_NETFILTER=y
+CONFIG_NF_CONNTRACK=m
+CONFIG_NF_CONNTRACK_FTP=m
+CONFIG_NF_CONNTRACK_H323=m
+CONFIG_NF_CONNTRACK_SIP=m
+CONFIG_NF_CT_NETLINK=m
+CONFIG_NETFILTER_XT_MATCH_CONNTRACK=m
+CONFIG_NETFILTER_XT_MATCH_POLICY=m
+CONFIG_NETFILTER_XT_MATCH_STATE=m
+CONFIG_NF_CONNTRACK_IPV4=m
+CONFIG_IP_NF_IPTABLES=m
+CONFIG_IP_NF_FILTER=m
+CONFIG_IP_NF_TARGET_REJECT=m
+CONFIG_IP_NF_TARGET_LOG=m
+CONFIG_IP_NF_TARGET_ULOG=m
+CONFIG_NF_NAT=m
+CONFIG_IP_NF_TARGET_MASQUERADE=m
+CONFIG_NET_SCHED=y
+CONFIG_NET_SCH_CBQ=m
+CONFIG_NET_SCH_SFQ=m
+CONFIG_NET_SCH_MQPRIO=m
+# CONFIG_FIRMWARE_IN_KERNEL is not set
+CONFIG_DEBUG_DRIVER=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_MISC_DEVICES=y
+CONFIG_AD525X_DPOT=y
+CONFIG_AD525X_DPOT_I2C=y
+CONFIG_ICS932S401=y
+CONFIG_APDS9802ALS=y
+CONFIG_ISL29003=y
+CONFIG_SENSORS_AK8975=y
+CONFIG_SCSI=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_NETDEVICES=y
+CONFIG_DUMMY=y
+# CONFIG_NETDEV_10000 is not set
+CONFIG_BCM4329=m
+CONFIG_BCM4329_FW_PATH="/lib/firmware/bcm4329/fw_bcm4329.bin"
+CONFIG_BCM4329_NVRAM_PATH="/lib/firmware/bcm4329/nvram.txt"
+CONFIG_BCM4329_WIFI_CONTROL_FUNC=y
+CONFIG_USB_CATC=y
+CONFIG_USB_KAWETH=y
+CONFIG_USB_PEGASUS=y
+CONFIG_USB_RTL8150=y
+CONFIG_USB_USBNET=y
+# CONFIG_USB_NET_CDCETHER is not set
+# CONFIG_USB_NET_CDC_NCM is not set
+CONFIG_USB_NET_DM9601=y
+CONFIG_USB_NET_SMSC75XX=y
+CONFIG_USB_NET_SMSC95XX=y
+# CONFIG_USB_NET_NET1080 is not set
+CONFIG_USB_NET_MCS7830=y
+# CONFIG_USB_NET_CDC_SUBSET is not set
+# CONFIG_USB_NET_ZAURUS is not set
+CONFIG_INPUT_EVDEV=y
+CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_TOUCHSCREEN_ATMEL_MXT=y
+CONFIG_INPUT_MISC=y
+CONFIG_INPUT_UINPUT=y
+CONFIG_INPUT_GPIO=y
+CONFIG_INPUT_ALPS_GPIO_SCROLLWHEEL=y
+# CONFIG_LEGACY_PTYS is not set
+# CONFIG_DEVKMEM is not set
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
+# CONFIG_HW_RANDOM is not set
+# CONFIG_I2C_COMPAT is not set
+# CONFIG_I2C_HELPER_AUTO is not set
+CONFIG_I2C_TEGRA=y
+CONFIG_SENSORS_LM90=y
+CONFIG_MFD_MAX8907C=y
+CONFIG_MFD_TPS6586X=y
+CONFIG_MFD_TPS6591X=y
+CONFIG_MFD_TPS80031=y
+CONFIG_REGULATOR=y
+CONFIG_REGULATOR_MAX8907C=y
+CONFIG_REGULATOR_TPS6586X=y
+CONFIG_REGULATOR_TPS6591X=y
+CONFIG_REGULATOR_TPS80031=y
+CONFIG_REGULATOR_GPIO_SWITCH=y
+CONFIG_MEDIA_SUPPORT=y
+CONFIG_VIDEO_DEV=y
+# CONFIG_MEDIA_TUNER_SIMPLE is not set
+# CONFIG_MEDIA_TUNER_TDA8290 is not set
+# CONFIG_MEDIA_TUNER_TDA827X is not set
+# CONFIG_MEDIA_TUNER_TDA18271 is not set
+# CONFIG_MEDIA_TUNER_TDA9887 is not set
+# CONFIG_MEDIA_TUNER_TEA5761 is not set
+# CONFIG_MEDIA_TUNER_TEA5767 is not set
+# CONFIG_MEDIA_TUNER_MT20XX is not set
+# CONFIG_MEDIA_TUNER_MT2060 is not set
+# CONFIG_MEDIA_TUNER_MT2266 is not set
+# CONFIG_MEDIA_TUNER_MT2131 is not set
+# CONFIG_MEDIA_TUNER_QT1010 is not set
+# CONFIG_MEDIA_TUNER_XC2028 is not set
+# CONFIG_MEDIA_TUNER_XC5000 is not set
+# CONFIG_MEDIA_TUNER_MXL5005S is not set
+# CONFIG_MEDIA_TUNER_MXL5007T is not set
+# CONFIG_MEDIA_TUNER_MC44S803 is not set
+# CONFIG_MEDIA_TUNER_MAX2165 is not set
+# CONFIG_MEDIA_TUNER_TDA18218 is not set
+# CONFIG_V4L_USB_DRIVERS is not set
+# CONFIG_RADIO_ADAPTERS is not set
+CONFIG_FB=y
+CONFIG_TEGRA_GRHOST=y
+CONFIG_TEGRA_DC=y
+CONFIG_TEGRA_DC_EXTENSIONS=y
+CONFIG_SOUND=y
+CONFIG_SND=y
+CONFIG_SND_SOC=y
+CONFIG_SND_SOC_TEGRA=y
+CONFIG_SND_SOC_TEGRA_WM8903=y
+CONFIG_USB=y
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
+# CONFIG_USB_DEVICE_CLASS is not set
+CONFIG_USB_SUSPEND=y
+CONFIG_USB_OTG=y
+# CONFIG_USB_OTG_WHITELIST is not set
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_STORAGE=y
+CONFIG_USB_LIBUSUAL=y
+CONFIG_USB_TEGRA_OTG=y
+CONFIG_MMC=y
+CONFIG_MMC_EMBEDDED_SDIO=y
+CONFIG_MMC_BLOCK_DEFERRED_RESUME=y
+CONFIG_MMC_SDHCI=y
+CONFIG_MMC_SDHCI_PLTFM=y
+CONFIG_MMC_SDHCI_TEGRA=y
+CONFIG_RTC_CLASS=y
+# CONFIG_RTC_DRV_TEGRA is not set
+CONFIG_RTC_DRV_TPS6591x=y
+CONFIG_RTC_DRV_TPS80031=y
+CONFIG_STAGING=y
+# CONFIG_STAGING_EXCLUDE_BUILD is not set
+CONFIG_IIO=y
+CONFIG_SENSORS_ISL29018=y
+CONFIG_EXT2_FS=y
+CONFIG_EXT2_FS_XATTR=y
+CONFIG_EXT2_FS_POSIX_ACL=y
+CONFIG_EXT2_FS_SECURITY=y
+CONFIG_EXT3_FS=y
+# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
+CONFIG_EXT3_FS_POSIX_ACL=y
+CONFIG_EXT3_FS_SECURITY=y
+CONFIG_EXT4_FS=y
+CONFIG_EXT4_FS_POSIX_ACL=y
+CONFIG_EXT4_FS_SECURITY=y
+# CONFIG_DNOTIFY is not set
+CONFIG_FUSE_FS=m
+CONFIG_ISO9660_FS=m
+CONFIG_JOLIET=y
+CONFIG_ZISOFS=y
+CONFIG_MSDOS_FS=m
+CONFIG_VFAT_FS=m
+CONFIG_NTFS_FS=m
+CONFIG_NTFS_RW=y
+CONFIG_TMPFS=y
+# CONFIG_MISC_FILESYSTEMS is not set
+CONFIG_NFS_FS=y
+CONFIG_NFS_V3=y
+CONFIG_NFS_V3_ACL=y
+CONFIG_NFS_V4=y
+CONFIG_NFS_V4_1=y
+CONFIG_ROOT_NFS=y
+CONFIG_CIFS=m
+CONFIG_CIFS_EXPERIMENTAL=y
+CONFIG_PARTITION_ADVANCED=y
+CONFIG_EFI_PARTITION=y
+CONFIG_NLS_CODEPAGE_437=m
+CONFIG_NLS_ASCII=m
+CONFIG_NLS_ISO8859_1=m
+CONFIG_NLS_UTF8=m
+CONFIG_PRINTK_TIME=y
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_DEBUG_KERNEL=y
+CONFIG_DETECT_HUNG_TASK=y
+CONFIG_SCHEDSTATS=y
+CONFIG_TIMER_STATS=y
+CONFIG_DEBUG_SLAB=y
+# CONFIG_DEBUG_PREEMPT is not set
+CONFIG_DEBUG_MUTEXES=y
+CONFIG_DEBUG_SPINLOCK_SLEEP=y
+CONFIG_DEBUG_INFO=y
+CONFIG_DEBUG_VM=y
+CONFIG_DEBUG_SG=y
+# CONFIG_RCU_CPU_STALL_DETECTOR is not set
+CONFIG_FUNCTION_TRACER=y
+CONFIG_CRYPTO_AUTHENC=y
+CONFIG_CRYPTO_CBC=y
+CONFIG_CRYPTO_ECB=y
+CONFIG_CRYPTO_HMAC=y
+CONFIG_CRYPTO_MD5=y
+CONFIG_CRYPTO_SHA1=y
+CONFIG_CRYPTO_ARC4=y
+CONFIG_CRYPTO_DES=y
+CONFIG_CRYPTO_TWOFISH=y
+CONFIG_CRYPTO_DEFLATE=y
+# CONFIG_CRYPTO_ANSI_CPRNG is not set
+CONFIG_CRYPTO_DEV_TEGRA_SE=y
+CONFIG_CRC_CCITT=y
diff --git a/arch/arm/configs/tegra_android_defconfig b/arch/arm/configs/tegra_android_defconfig
new file mode 100644
index 000000000000..7256673c7f38
--- /dev/null
+++ b/arch/arm/configs/tegra_android_defconfig
@@ -0,0 +1,372 @@
+CONFIG_EXPERIMENTAL=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_CGROUPS=y
+CONFIG_CGROUP_DEBUG=y
+CONFIG_CGROUP_FREEZER=y
+CONFIG_CGROUP_CPUACCT=y
+CONFIG_RESOURCE_COUNTERS=y
+CONFIG_CGROUP_SCHED=y
+CONFIG_RT_GROUP_SCHED=y
+CONFIG_BLK_DEV_INITRD=y
+# CONFIG_SYSCTL_SYSCALL is not set
+# CONFIG_ELF_CORE is not set
+CONFIG_ASHMEM=y
+CONFIG_EMBEDDED=y
+CONFIG_SLAB=y
+CONFIG_PROFILING=y
+CONFIG_OPROFILE=y
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODULE_FORCE_UNLOAD=y
+# CONFIG_BLK_DEV_BSG is not set
+# CONFIG_IOSCHED_DEADLINE is not set
+# CONFIG_IOSCHED_CFQ is not set
+CONFIG_ARCH_TEGRA=y
+CONFIG_GPIO_PCA953X=y
+CONFIG_MACH_HARMONY=y
+CONFIG_MACH_VENTANA=y
+CONFIG_MACH_KAEN=y
+CONFIG_MACH_PAZ00=y
+CONFIG_MACH_TRIMSLICE=y
+CONFIG_MACH_WARIO=y
+CONFIG_MACH_WHISTLER=y
+CONFIG_TEGRA_PWM=y
+CONFIG_TEGRA_EMC_SCALING_ENABLE=y
+CONFIG_TEGRA_CLOCK_DEBUG_WRITE=y
+CONFIG_USB_HOTPLUG=y
+CONFIG_TEGRA_USB_MODEM_POWER=y
+CONFIG_ARM_ERRATA_720789=y
+CONFIG_ARM_ERRATA_751472=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_SMP=y
+CONFIG_NR_CPUS=2
+CONFIG_PREEMPT=y
+CONFIG_AEABI=y
+# CONFIG_OABI_COMPAT is not set
+CONFIG_HIGHMEM=y
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZBOOT_ROM_BSS=0x0
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y
+CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
+CONFIG_CPU_FREQ_GOV_ONDEMAND=y
+CONFIG_CPU_FREQ_GOV_INTERACTIVE=y
+CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
+CONFIG_CPU_IDLE=y
+CONFIG_VFP=y
+CONFIG_WAKELOCK=y
+CONFIG_PM_RUNTIME=y
+CONFIG_SUSPEND_TIME=y
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_NET_KEY=y
+CONFIG_INET=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+CONFIG_IP_PNP_RARP=y
+CONFIG_INET_ESP=y
+# CONFIG_INET_XFRM_MODE_TUNNEL is not set
+# CONFIG_INET_XFRM_MODE_BEET is not set
+# CONFIG_INET_LRO is not set
+# CONFIG_INET_DIAG is not set
+CONFIG_IPV6=y
+CONFIG_IPV6_PRIVACY=y
+CONFIG_IPV6_ROUTER_PREF=y
+CONFIG_IPV6_OPTIMISTIC_DAD=y
+CONFIG_INET6_AH=y
+CONFIG_INET6_ESP=y
+CONFIG_INET6_IPCOMP=y
+CONFIG_IPV6_MIP6=y
+CONFIG_IPV6_TUNNEL=y
+CONFIG_IPV6_MULTIPLE_TABLES=y
+CONFIG_NETFILTER=y
+CONFIG_NETFILTER_NETLINK_LOG=y
+CONFIG_NF_CONNTRACK=y
+CONFIG_NF_CONNTRACK_EVENTS=y
+CONFIG_NF_CT_PROTO_DCCP=y
+CONFIG_NF_CT_PROTO_SCTP=y
+CONFIG_NF_CT_PROTO_UDPLITE=y
+CONFIG_NF_CONNTRACK_AMANDA=y
+CONFIG_NF_CONNTRACK_FTP=y
+CONFIG_NF_CONNTRACK_H323=y
+CONFIG_NF_CONNTRACK_IRC=y
+CONFIG_NF_CONNTRACK_NETBIOS_NS=y
+CONFIG_NF_CONNTRACK_PPTP=y
+CONFIG_NF_CONNTRACK_SANE=y
+CONFIG_NF_CONNTRACK_SIP=y
+CONFIG_NF_CONNTRACK_TFTP=y
+CONFIG_NETFILTER_TPROXY=y
+CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y
+CONFIG_NETFILTER_XT_TARGET_CONNMARK=y
+CONFIG_NETFILTER_XT_TARGET_MARK=y
+CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y
+CONFIG_NETFILTER_XT_MATCH_COMMENT=y
+CONFIG_NETFILTER_XT_MATCH_CONNBYTES=y
+CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y
+CONFIG_NETFILTER_XT_MATCH_CONNMARK=y
+CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y
+CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y
+CONFIG_NETFILTER_XT_MATCH_HELPER=y
+CONFIG_NETFILTER_XT_MATCH_IPRANGE=y
+CONFIG_NETFILTER_XT_MATCH_LENGTH=y
+CONFIG_NETFILTER_XT_MATCH_LIMIT=y
+CONFIG_NETFILTER_XT_MATCH_MAC=y
+CONFIG_NETFILTER_XT_MATCH_MARK=y
+CONFIG_NETFILTER_XT_MATCH_POLICY=y
+CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y
+CONFIG_NETFILTER_XT_MATCH_QTAGUID=y
+CONFIG_NETFILTER_XT_MATCH_QUOTA=y
+CONFIG_NETFILTER_XT_MATCH_SOCKET=y
+CONFIG_NETFILTER_XT_MATCH_STATE=y
+CONFIG_NETFILTER_XT_MATCH_STATISTIC=y
+CONFIG_NETFILTER_XT_MATCH_STRING=y
+CONFIG_NETFILTER_XT_MATCH_TIME=y
+CONFIG_NETFILTER_XT_MATCH_U32=y
+CONFIG_NF_CONNTRACK_IPV4=y
+CONFIG_IP_NF_IPTABLES=y
+CONFIG_IP_NF_MATCH_AH=y
+CONFIG_IP_NF_MATCH_ECN=y
+CONFIG_IP_NF_MATCH_TTL=y
+CONFIG_IP_NF_FILTER=y
+CONFIG_IP_NF_TARGET_REJECT=y
+CONFIG_IP_NF_TARGET_LOG=y
+CONFIG_NF_NAT=y
+CONFIG_IP_NF_TARGET_MASQUERADE=y
+CONFIG_IP_NF_TARGET_NETMAP=y
+CONFIG_IP_NF_TARGET_REDIRECT=y
+CONFIG_IP_NF_MANGLE=y
+CONFIG_IP_NF_ARPTABLES=y
+CONFIG_IP_NF_ARPFILTER=y
+CONFIG_IP_NF_ARP_MANGLE=y
+CONFIG_IP6_NF_IPTABLES=y
+CONFIG_NET_SCHED=y
+CONFIG_NET_SCH_HTB=y
+CONFIG_NET_SCH_INGRESS=y
+CONFIG_NET_CLS_U32=y
+CONFIG_NET_EMATCH=y
+CONFIG_NET_EMATCH_U32=y
+CONFIG_NET_CLS_ACT=y
+CONFIG_NET_ACT_POLICE=y
+CONFIG_NET_ACT_GACT=y
+CONFIG_NET_ACT_MIRRED=y
+CONFIG_BT=y
+CONFIG_BT_L2CAP=y
+CONFIG_BT_SCO=y
+CONFIG_BT_RFCOMM=y
+CONFIG_BT_RFCOMM_TTY=y
+CONFIG_BT_BNEP=y
+CONFIG_BT_HIDP=y
+CONFIG_BT_HCIUART=y
+CONFIG_BT_HCIUART_H4=y
+CONFIG_BT_HCIUART_LL=y
+CONFIG_BT_BLUESLEEP=y
+CONFIG_CFG80211=y
+CONFIG_RFKILL=y
+# CONFIG_FIRMWARE_IN_KERNEL is not set
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_MISC_DEVICES=y
+CONFIG_AD525X_DPOT=y
+CONFIG_AD525X_DPOT_I2C=y
+# CONFIG_ANDROID_PMEM is not set
+CONFIG_APDS9802ALS=y
+CONFIG_UID_STAT=y
+CONFIG_BCM4329_RFKILL=y
+CONFIG_TEGRA_CRYPTO_DEV=y
+CONFIG_MAX1749_VIBRATOR=y
+CONFIG_MPU_SENSORS_TIMERIRQ=y
+CONFIG_INV_SENSORS=y
+CONFIG_MPU_SENSORS_MPU3050=y
+CONFIG_MPU_SENSORS_MPU3050_GYRO=y
+CONFIG_MPU_SENSORS_KXTF9=y
+CONFIG_MPU_SENSORS_AK8975=y
+CONFIG_MPU_SENSORS_PRESSURE_NONE=y
+CONFIG_SCSI=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_BLK_DEV_SR=y
+CONFIG_BLK_DEV_SR_VENDOR=y
+CONFIG_CHR_DEV_SG=y
+CONFIG_SCSI_MULTI_LUN=y
+CONFIG_MD=y
+CONFIG_BLK_DEV_DM=y
+CONFIG_DM_CRYPT=y
+CONFIG_DM_UEVENT=y
+CONFIG_NETDEVICES=y
+CONFIG_DUMMY=y
+CONFIG_R8169=y
+# CONFIG_NETDEV_10000 is not set
+CONFIG_BCM4329=m
+CONFIG_BCM4329_FIRST_SCAN=y
+CONFIG_BCM4329_FW_PATH="/system/vendor/firmware/fw_bcm4329.bin"
+CONFIG_BCM4329_NVRAM_PATH="/system/etc/nvram.txt"
+CONFIG_BCM4329_WIFI_CONTROL_FUNC=y
+CONFIG_BCMDHD=y
+CONFIG_BCMDHD_WIFI_CONTROL_FUNC=y
+CONFIG_BCMDHD_HW_OOB=y
+CONFIG_USB_USBNET=y
+# CONFIG_USB_NET_NET1080 is not set
+# CONFIG_USB_BELKIN is not set
+# CONFIG_USB_ARMLINUX is not set
+# CONFIG_USB_NET_ZAURUS is not set
+CONFIG_PPP=y
+CONFIG_PPP_FILTER=y
+CONFIG_PPP_ASYNC=y
+CONFIG_PPP_SYNC_TTY=y
+CONFIG_PPP_DEFLATE=y
+CONFIG_PPP_BSDCOMP=y
+CONFIG_PPP_MPPE=y
+CONFIG_PPPOLAC=y
+CONFIG_PPPOPNS=y
+# CONFIG_INPUT_MOUSEDEV is not set
+CONFIG_INPUT_JOYDEV=y
+CONFIG_INPUT_EVDEV=y
+CONFIG_INPUT_KEYRESET=y
+# CONFIG_KEYBOARD_ATKBD is not set
+CONFIG_KEYBOARD_GPIO=y
+CONFIG_KEYBOARD_TEGRA=y
+# CONFIG_INPUT_MOUSE is not set
+CONFIG_INPUT_JOYSTICK=y
+CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_TOUCHSCREEN_ATMEL_MXT=y
+CONFIG_TOUCHSCREEN_PANJIT_I2C=y
+CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI=y
+CONFIG_INPUT_MISC=y
+CONFIG_INPUT_UINPUT=y
+CONFIG_INPUT_GPIO=y
+CONFIG_INPUT_ADXL34X=y
+CONFIG_SERIO_LIBPS2=y
+# CONFIG_VT is not set
+# CONFIG_LEGACY_PTYS is not set
+# CONFIG_DEVKMEM is not set
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_SERIAL_TEGRA=y
+# CONFIG_HW_RANDOM is not set
+# CONFIG_I2C_COMPAT is not set
+CONFIG_I2C_MUX=y
+CONFIG_I2C_MUX_PCA954x=y
+# CONFIG_I2C_HELPER_AUTO is not set
+CONFIG_I2C_TEGRA=y
+CONFIG_DEBUG_GPIO=y
+CONFIG_GPIO_SYSFS=y
+CONFIG_POWER_SUPPLY=y
+CONFIG_BATTERY_BQ20Z75=y
+CONFIG_CHARGER_GPIO=y
+CONFIG_SENSORS_ADT7461=y
+CONFIG_MFD_TPS6586X=y
+CONFIG_MFD_MAX8907C=y
+CONFIG_REGULATOR=y
+CONFIG_REGULATOR_FIXED_VOLTAGE=y
+CONFIG_REGULATOR_VIRTUAL_CONSUMER=y
+CONFIG_REGULATOR_MAX8907C=y
+CONFIG_REGULATOR_TPS6586X=y
+CONFIG_MEDIA_SUPPORT=y
+CONFIG_VIDEO_DEV=y
+# CONFIG_RC_CORE is not set
+# CONFIG_MEDIA_TUNER_CUSTOMISE is not set
+CONFIG_VIDEO_HELPER_CHIPS_AUTO=y
+CONFIG_VIDEO_OV5650=y
+CONFIG_VIDEO_OV2710=y
+CONFIG_VIDEO_SOC380=y
+CONFIG_TORCH_SSL3250A=y
+CONFIG_VIDEO_SH532U=y
+CONFIG_VIDEO_AD5820=y
+CONFIG_USB_VIDEO_CLASS=y
+# CONFIG_USB_GSPCA is not set
+# CONFIG_RADIO_ADAPTERS is not set
+CONFIG_VIDEO_OUTPUT_CONTROL=y
+CONFIG_FB=y
+CONFIG_TEGRA_GRHOST=y
+CONFIG_TEGRA_DC=y
+CONFIG_TEGRA_DSI=y
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+CONFIG_LCD_CLASS_DEVICE=y
+CONFIG_BACKLIGHT_CLASS_DEVICE=y
+# CONFIG_BACKLIGHT_GENERIC is not set
+CONFIG_BACKLIGHT_PWM=y
+CONFIG_BACKLIGHT_TEGRA_PWM=y
+CONFIG_SOUND=y
+CONFIG_SND=y
+CONFIG_SND_SOC=y
+CONFIG_SND_SOC_TEGRA=y
+CONFIG_SND_SOC_TEGRA_WM8903=y
+CONFIG_SND_SOC_TEGRA_WM8753=y
+CONFIG_HID_SONY=y
+CONFIG_USB=y
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
+CONFIG_USB_DEVICEFS=y
+CONFIG_USB_SUSPEND=y
+CONFIG_USB_OTG=y
+# CONFIG_USB_OTG_WHITELIST is not set
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_EHCI_TEGRA=y
+CONFIG_USB_ACM=y
+CONFIG_USB_WDM=y
+CONFIG_USB_STORAGE=y
+CONFIG_USB_LIBUSUAL=y
+CONFIG_USB_SERIAL=y
+CONFIG_USB_SERIAL_PL2303=y
+CONFIG_USB_SERIAL_OPTION=y
+CONFIG_USB_GADGET=y
+CONFIG_USB_GADGET_VBUS_DRAW=500
+CONFIG_USB_FSL_USB2=y
+CONFIG_USB_G_ANDROID=y
+CONFIG_USB_TEGRA_OTG=y
+CONFIG_MMC=y
+CONFIG_MMC_UNSAFE_RESUME=y
+CONFIG_MMC_EMBEDDED_SDIO=y
+CONFIG_MMC_SDHCI=y
+CONFIG_MMC_SDHCI_PLTFM=y
+CONFIG_MMC_SDHCI_TEGRA=y
+CONFIG_SWITCH=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_MAX8907C=y
+CONFIG_RTC_DRV_TPS6586X=y
+CONFIG_RTC_DRV_TEGRA=y
+CONFIG_STAGING=y
+CONFIG_ANDROID=y
+CONFIG_ANDROID_BINDER_IPC=y
+CONFIG_ANDROID_LOGGER=y
+CONFIG_ANDROID_RAM_CONSOLE=y
+CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION=y
+CONFIG_ANDROID_TIMED_GPIO=y
+CONFIG_ANDROID_LOW_MEMORY_KILLER=y
+CONFIG_IIO=y
+CONFIG_SENSORS_ISL29018=y
+CONFIG_EXT2_FS=y
+CONFIG_EXT2_FS_XATTR=y
+CONFIG_EXT2_FS_POSIX_ACL=y
+CONFIG_EXT2_FS_SECURITY=y
+CONFIG_EXT3_FS=y
+# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
+CONFIG_EXT3_FS_POSIX_ACL=y
+CONFIG_EXT3_FS_SECURITY=y
+CONFIG_EXT4_FS=y
+CONFIG_EXT4_FS_POSIX_ACL=y
+# CONFIG_DNOTIFY is not set
+CONFIG_FUSE_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_TMPFS=y
+CONFIG_NFS_FS=y
+CONFIG_ROOT_NFS=y
+CONFIG_PARTITION_ADVANCED=y
+CONFIG_EFI_PARTITION=y
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_PRINTK_TIME=y
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_DEBUG_FS=y
+CONFIG_LOCKUP_DETECTOR=y
+CONFIG_SCHEDSTATS=y
+CONFIG_TIMER_STATS=y
+CONFIG_DEBUG_SLAB=y
+# CONFIG_DEBUG_PREEMPT is not set
+CONFIG_DEBUG_MUTEXES=y
+CONFIG_DEBUG_INFO=y
+CONFIG_DEBUG_VM=y
+CONFIG_DEBUG_SG=y
+CONFIG_CRYPTO_TWOFISH=y
+# CONFIG_CRYPTO_ANSI_CPRNG is not set
diff --git a/arch/arm/configs/tegra_aruba2_android_defconfig b/arch/arm/configs/tegra_aruba2_android_defconfig
new file mode 100644
index 000000000000..9665b2ebf516
--- /dev/null
+++ b/arch/arm/configs/tegra_aruba2_android_defconfig
@@ -0,0 +1,311 @@
+CONFIG_EXPERIMENTAL=y
+CONFIG_CROSS_COMPILE="arm-eabi-"
+# CONFIG_SWAP is not set
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_CGROUPS=y
+CONFIG_CGROUP_DEBUG=y
+CONFIG_CGROUP_FREEZER=y
+CONFIG_CGROUP_CPUACCT=y
+CONFIG_RESOURCE_COUNTERS=y
+CONFIG_CGROUP_SCHED=y
+CONFIG_RT_GROUP_SCHED=y
+CONFIG_BLK_DEV_INITRD=y
+# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
+CONFIG_PANIC_TIMEOUT=10
+# CONFIG_SYSCTL_SYSCALL is not set
+# CONFIG_ELF_CORE is not set
+CONFIG_ASHMEM=y
+CONFIG_EMBEDDED=y
+CONFIG_SLAB=y
+CONFIG_PROFILING=y
+CONFIG_OPROFILE=y
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODULE_FORCE_UNLOAD=y
+# CONFIG_BLK_DEV_BSG is not set
+# CONFIG_IOSCHED_DEADLINE is not set
+# CONFIG_IOSCHED_CFQ is not set
+CONFIG_ARCH_TEGRA=y
+CONFIG_ARCH_TEGRA_3x_SOC=y
+CONFIG_MACH_ARUBA=y
+CONFIG_TEGRA_DEBUG_UARTA=y
+CONFIG_TEGRA_PWM=y
+CONFIG_TEGRA_CLOCK_DEBUG_WRITE=y
+CONFIG_TEGRA_MC_PROFILE=y
+CONFIG_ARM_ERRATA_743622=y
+CONFIG_ARM_ERRATA_751472=y
+CONFIG_ARM_ERRATA_752520=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_SMP=y
+CONFIG_PREEMPT=y
+CONFIG_AEABI=y
+# CONFIG_OABI_COMPAT is not set
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZBOOT_ROM_BSS=0x0
+CONFIG_CMDLINE="mem=448@2048M console=ttyS0,115200n8 earlyprintk init=/bin/ash"
+CONFIG_CPU_IDLE=y
+CONFIG_VFP=y
+CONFIG_NEON=y
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_NET_KEY=y
+CONFIG_INET=y
+CONFIG_INET_ESP=y
+# CONFIG_INET_XFRM_MODE_TUNNEL is not set
+# CONFIG_INET_XFRM_MODE_BEET is not set
+# CONFIG_INET_LRO is not set
+# CONFIG_INET_DIAG is not set
+CONFIG_IPV6=y
+CONFIG_IPV6_PRIVACY=y
+CONFIG_IPV6_ROUTER_PREF=y
+CONFIG_IPV6_OPTIMISTIC_DAD=y
+CONFIG_INET6_AH=y
+CONFIG_INET6_ESP=y
+CONFIG_INET6_IPCOMP=y
+CONFIG_IPV6_MIP6=y
+CONFIG_IPV6_TUNNEL=y
+CONFIG_IPV6_MULTIPLE_TABLES=y
+CONFIG_NETFILTER=y
+CONFIG_NETFILTER_NETLINK_LOG=y
+CONFIG_NF_CONNTRACK=y
+CONFIG_NF_CONNTRACK_EVENTS=y
+CONFIG_NF_CT_PROTO_DCCP=y
+CONFIG_NF_CT_PROTO_SCTP=y
+CONFIG_NF_CT_PROTO_UDPLITE=y
+CONFIG_NF_CONNTRACK_AMANDA=y
+CONFIG_NF_CONNTRACK_FTP=y
+CONFIG_NF_CONNTRACK_H323=y
+CONFIG_NF_CONNTRACK_IRC=y
+CONFIG_NF_CONNTRACK_NETBIOS_NS=y
+CONFIG_NF_CONNTRACK_PPTP=y
+CONFIG_NF_CONNTRACK_SANE=y
+CONFIG_NF_CONNTRACK_SIP=y
+CONFIG_NF_CONNTRACK_TFTP=y
+CONFIG_NF_CT_NETLINK=y
+CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y
+CONFIG_NETFILTER_XT_TARGET_CONNMARK=y
+CONFIG_NETFILTER_XT_TARGET_MARK=y
+CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y
+CONFIG_NETFILTER_XT_MATCH_COMMENT=y
+CONFIG_NETFILTER_XT_MATCH_CONNBYTES=y
+CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y
+CONFIG_NETFILTER_XT_MATCH_CONNMARK=y
+CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y
+CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y
+CONFIG_NETFILTER_XT_MATCH_HELPER=y
+CONFIG_NETFILTER_XT_MATCH_IPRANGE=y
+CONFIG_NETFILTER_XT_MATCH_LENGTH=y
+CONFIG_NETFILTER_XT_MATCH_LIMIT=y
+CONFIG_NETFILTER_XT_MATCH_MAC=y
+CONFIG_NETFILTER_XT_MATCH_MARK=y
+CONFIG_NETFILTER_XT_MATCH_OWNER=y
+CONFIG_NETFILTER_XT_MATCH_POLICY=y
+CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y
+CONFIG_NETFILTER_XT_MATCH_QUOTA=y
+CONFIG_NETFILTER_XT_MATCH_STATE=y
+CONFIG_NETFILTER_XT_MATCH_STATISTIC=y
+CONFIG_NETFILTER_XT_MATCH_STRING=y
+CONFIG_NETFILTER_XT_MATCH_TIME=y
+CONFIG_NETFILTER_XT_MATCH_U32=y
+CONFIG_NF_CONNTRACK_IPV4=y
+CONFIG_IP_NF_IPTABLES=y
+CONFIG_IP_NF_MATCH_AH=y
+CONFIG_IP_NF_MATCH_ECN=y
+CONFIG_IP_NF_MATCH_TTL=y
+CONFIG_IP_NF_FILTER=y
+CONFIG_IP_NF_TARGET_REJECT=y
+CONFIG_IP_NF_TARGET_LOG=y
+CONFIG_NF_NAT=y
+CONFIG_IP_NF_TARGET_MASQUERADE=y
+CONFIG_IP_NF_TARGET_NETMAP=y
+CONFIG_IP_NF_TARGET_REDIRECT=y
+CONFIG_IP_NF_ARPTABLES=y
+CONFIG_IP_NF_ARPFILTER=y
+CONFIG_IP_NF_ARP_MANGLE=y
+CONFIG_NET_SCHED=y
+CONFIG_NET_SCH_HTB=y
+CONFIG_NET_SCH_INGRESS=y
+CONFIG_NET_CLS_U32=y
+CONFIG_NET_EMATCH=y
+CONFIG_NET_EMATCH_U32=y
+CONFIG_NET_CLS_ACT=y
+CONFIG_NET_ACT_POLICE=y
+CONFIG_NET_ACT_GACT=y
+CONFIG_NET_ACT_MIRRED=y
+CONFIG_RFKILL=y
+# CONFIG_FIRMWARE_IN_KERNEL is not set
+CONFIG_MTD=y
+CONFIG_MTD_PARTITIONS=y
+CONFIG_MTD_CMDLINE_PARTS=y
+CONFIG_MTD_CHAR=y
+CONFIG_MTD_BLOCK=y
+CONFIG_MTD_NAND_TEGRA=y
+CONFIG_MTD_NAND=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_MISC_DEVICES=y
+CONFIG_SCSI=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_BLK_DEV_SR=y
+CONFIG_BLK_DEV_SR_VENDOR=y
+CONFIG_CHR_DEV_SG=y
+CONFIG_SCSI_MULTI_LUN=y
+CONFIG_MD=y
+CONFIG_BLK_DEV_DM=y
+CONFIG_DM_CRYPT=y
+CONFIG_DM_UEVENT=y
+CONFIG_NETDEVICES=y
+CONFIG_DUMMY=y
+CONFIG_NET_ETHERNET=y
+CONFIG_SMC91X=y
+# CONFIG_NETDEV_1000 is not set
+# CONFIG_NETDEV_10000 is not set
+CONFIG_USB_USBNET=y
+# CONFIG_USB_NET_AX8817X is not set
+# CONFIG_USB_NET_NET1080 is not set
+# CONFIG_USB_BELKIN is not set
+# CONFIG_USB_ARMLINUX is not set
+# CONFIG_USB_NET_ZAURUS is not set
+CONFIG_PPP=y
+CONFIG_PPP_ASYNC=y
+CONFIG_PPP_DEFLATE=y
+CONFIG_PPP_BSDCOMP=y
+CONFIG_PPP_MPPE=y
+CONFIG_PPPOLAC=y
+CONFIG_PPPOPNS=y
+# CONFIG_INPUT_MOUSEDEV is not set
+CONFIG_INPUT_EVDEV=y
+CONFIG_INPUT_KEYRESET=y
+# CONFIG_KEYBOARD_ATKBD is not set
+CONFIG_KEYBOARD_GPIO=y
+# CONFIG_INPUT_MOUSE is not set
+CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_INPUT_MISC=y
+CONFIG_INPUT_UINPUT=y
+CONFIG_INPUT_GPIO=y
+# CONFIG_SERIO is not set
+# CONFIG_VT is not set
+# CONFIG_LEGACY_PTYS is not set
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_SERIAL_TEGRA=y
+# CONFIG_HW_RANDOM is not set
+CONFIG_I2C_CHARDEV=y
+CONFIG_I2C_TEGRA=y
+CONFIG_SPI=y
+CONFIG_SPI_TEGRA=y
+CONFIG_GPIO_SYSFS=y
+CONFIG_POWER_SUPPLY=y
+CONFIG_PDA_POWER=y
+CONFIG_BATTERY_BQ20Z75=y
+CONFIG_REGULATOR=y
+CONFIG_REGULATOR_DUMMY=y
+CONFIG_REGULATOR_FIXED_VOLTAGE=y
+CONFIG_REGULATOR_VIRTUAL_CONSUMER=y
+CONFIG_MEDIA_SUPPORT=y
+CONFIG_VIDEO_DEV=y
+# CONFIG_MEDIA_TUNER_SIMPLE is not set
+# CONFIG_MEDIA_TUNER_TDA8290 is not set
+# CONFIG_MEDIA_TUNER_TDA827X is not set
+# CONFIG_MEDIA_TUNER_TDA18271 is not set
+# CONFIG_MEDIA_TUNER_TDA9887 is not set
+# CONFIG_MEDIA_TUNER_TEA5761 is not set
+# CONFIG_MEDIA_TUNER_TEA5767 is not set
+# CONFIG_MEDIA_TUNER_MT20XX is not set
+# CONFIG_MEDIA_TUNER_MT2060 is not set
+# CONFIG_MEDIA_TUNER_MT2266 is not set
+# CONFIG_MEDIA_TUNER_MT2131 is not set
+# CONFIG_MEDIA_TUNER_QT1010 is not set
+# CONFIG_MEDIA_TUNER_XC2028 is not set
+# CONFIG_MEDIA_TUNER_XC5000 is not set
+# CONFIG_MEDIA_TUNER_MXL5005S is not set
+# CONFIG_MEDIA_TUNER_MXL5007T is not set
+# CONFIG_MEDIA_TUNER_MC44S803 is not set
+# CONFIG_MEDIA_TUNER_MAX2165 is not set
+# CONFIG_V4L_USB_DRIVERS is not set
+# CONFIG_RADIO_ADAPTERS is not set
+CONFIG_VIDEO_OUTPUT_CONTROL=y
+CONFIG_FB=y
+CONFIG_TEGRA_GRHOST=y
+CONFIG_TEGRA_DC=y
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+CONFIG_LCD_CLASS_DEVICE=y
+CONFIG_BACKLIGHT_CLASS_DEVICE=y
+# CONFIG_BACKLIGHT_GENERIC is not set
+CONFIG_BACKLIGHT_PWM=y
+CONFIG_SOUND=y
+CONFIG_SND=y
+CONFIG_SND_SOC=y
+CONFIG_SND_SOC_TEGRA=y
+CONFIG_USB=y
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
+CONFIG_USB_DEVICEFS=y
+CONFIG_USB_EHCI_HCD=y
+# CONFIG_USB_EHCI_TT_NEWSCHED is not set
+CONFIG_USB_ACM=y
+CONFIG_USB_WDM=y
+CONFIG_USB_STORAGE=y
+CONFIG_USB_LIBUSUAL=y
+CONFIG_USB_SERIAL=y
+CONFIG_USB_GADGET=y
+CONFIG_USB_GADGET_VBUS_DRAW=500
+CONFIG_USB_ANDROID=y
+CONFIG_USB_ANDROID_ADB=y
+CONFIG_USB_ANDROID_MTP=y
+CONFIG_USB_TEGRA_OTG=y
+CONFIG_MMC=y
+CONFIG_MMC_UNSAFE_RESUME=y
+CONFIG_MMC_EMBEDDED_SDIO=y
+CONFIG_MMC_PARANOID_SD_INIT=y
+# CONFIG_MMC_BLOCK_BOUNCE is not set
+CONFIG_MMC_BLOCK_DEFERRED_RESUME=y
+CONFIG_MMC_SDHCI=y
+CONFIG_MMC_SDHCI_PLTFM=y
+CONFIG_MMC_SDHCI_TEGRA=y
+CONFIG_SWITCH=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_TEGRA=y
+CONFIG_STAGING=y
+# CONFIG_STAGING_EXCLUDE_BUILD is not set
+CONFIG_ANDROID=y
+CONFIG_ANDROID_BINDER_IPC=y
+CONFIG_ANDROID_LOGGER=y
+CONFIG_ANDROID_RAM_CONSOLE=y
+CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION=y
+CONFIG_ANDROID_LOW_MEMORY_KILLER=y
+CONFIG_IIO=y
+CONFIG_EXT2_FS=y
+CONFIG_EXT2_FS_XATTR=y
+CONFIG_EXT2_FS_POSIX_ACL=y
+CONFIG_EXT2_FS_SECURITY=y
+CONFIG_EXT3_FS=y
+# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
+CONFIG_EXT3_FS_POSIX_ACL=y
+CONFIG_EXT3_FS_SECURITY=y
+CONFIG_EXT4_FS=y
+# CONFIG_DNOTIFY is not set
+CONFIG_FUSE_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_TMPFS=y
+CONFIG_PARTITION_ADVANCED=y
+CONFIG_EFI_PARTITION=y
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_PRINTK_TIME=y
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_DEBUG_FS=y
+CONFIG_DEBUG_KERNEL=y
+CONFIG_DETECT_HUNG_TASK=y
+CONFIG_SCHEDSTATS=y
+CONFIG_TIMER_STATS=y
+# CONFIG_DEBUG_PREEMPT is not set
+CONFIG_DEBUG_INFO=y
+CONFIG_DEBUG_VM=y
+# CONFIG_RCU_CPU_STALL_DETECTOR is not set
+CONFIG_DEBUG_LL=y
+CONFIG_DEBUG_ICEDCC=y
+CONFIG_CRYPTO_AES=y
+CONFIG_CRYPTO_TWOFISH=y
+# CONFIG_CRYPTO_ANSI_CPRNG is not set
diff --git a/arch/arm/configs/tegra_defconfig b/arch/arm/configs/tegra_defconfig
index 8845f1c9925d..fd59b5923466 100644
--- a/arch/arm/configs/tegra_defconfig
+++ b/arch/arm/configs/tegra_defconfig
@@ -1,4 +1,5 @@
CONFIG_EXPERIMENTAL=y
+CONFIG_SYSVIPC=y
CONFIG_IKCONFIG=y
CONFIG_IKCONFIG_PROC=y
CONFIG_CGROUPS=y
@@ -9,10 +10,14 @@ CONFIG_RESOURCE_COUNTERS=y
CONFIG_CGROUP_SCHED=y
CONFIG_RT_GROUP_SCHED=y
CONFIG_BLK_DEV_INITRD=y
-CONFIG_EMBEDDED=y
# CONFIG_SYSCTL_SYSCALL is not set
+CONFIG_KALLSYMS_ALL=y
+CONFIG_KALLSYMS_EXTRA_PASS=y
# CONFIG_ELF_CORE is not set
+CONFIG_EMBEDDED=y
CONFIG_SLAB=y
+CONFIG_PROFILING=y
+CONFIG_OPROFILE=y
CONFIG_MODULES=y
CONFIG_MODULE_UNLOAD=y
CONFIG_MODULE_FORCE_UNLOAD=y
@@ -21,12 +26,13 @@ CONFIG_MODULE_FORCE_UNLOAD=y
# CONFIG_IOSCHED_CFQ is not set
CONFIG_ARCH_TEGRA=y
CONFIG_MACH_HARMONY=y
+CONFIG_MACH_VENTANA=y
CONFIG_MACH_KAEN=y
CONFIG_MACH_PAZ00=y
CONFIG_MACH_TRIMSLICE=y
CONFIG_MACH_WARIO=y
-CONFIG_TEGRA_DEBUG_UARTD=y
-CONFIG_ARM_ERRATA_742230=y
+CONFIG_MACH_WHISTLER=y
+CONFIG_USB_HOTPLUG=y
CONFIG_NO_HZ=y
CONFIG_HIGH_RES_TIMERS=y
CONFIG_SMP=y
@@ -38,33 +44,61 @@ CONFIG_HIGHMEM=y
CONFIG_ZBOOT_ROM_TEXT=0x0
CONFIG_ZBOOT_ROM_BSS=0x0
CONFIG_VFP=y
-CONFIG_PM=y
+CONFIG_PM_RUNTIME=y
CONFIG_NET=y
CONFIG_PACKET=y
-CONFIG_UNIX=y
-CONFIG_NET_KEY=y
+CONFIG_UNIX=m
+CONFIG_NET_KEY=m
CONFIG_INET=y
CONFIG_IP_PNP=y
CONFIG_IP_PNP_DHCP=y
CONFIG_IP_PNP_BOOTP=y
CONFIG_IP_PNP_RARP=y
-CONFIG_INET_ESP=y
-# CONFIG_INET_XFRM_MODE_TUNNEL is not set
-# CONFIG_INET_XFRM_MODE_BEET is not set
+CONFIG_INET_AH=m
+CONFIG_INET_ESP=m
+CONFIG_INET_IPCOMP=m
+CONFIG_INET_XFRM_MODE_TRANSPORT=m
+CONFIG_INET_XFRM_MODE_TUNNEL=m
+CONFIG_INET_XFRM_MODE_BEET=m
# CONFIG_INET_LRO is not set
# CONFIG_INET_DIAG is not set
-CONFIG_IPV6=y
-CONFIG_IPV6_PRIVACY=y
-CONFIG_IPV6_ROUTER_PREF=y
-CONFIG_IPV6_OPTIMISTIC_DAD=y
-CONFIG_INET6_AH=y
-CONFIG_INET6_ESP=y
-CONFIG_INET6_IPCOMP=y
-CONFIG_IPV6_MIP6=y
-CONFIG_IPV6_TUNNEL=y
-CONFIG_IPV6_MULTIPLE_TABLES=y
-# CONFIG_WIRELESS is not set
+CONFIG_TCP_CONG_ADVANCED=y
+CONFIG_TCP_CONG_CUBIC=m
+# CONFIG_TCP_CONG_HTCP is not set
+CONFIG_TCP_CONG_VEGAS=m
+CONFIG_TCP_CONG_VENO=m
+CONFIG_TCP_CONG_ILLINOIS=m
+# CONFIG_IPV6 is not set
+# CONFIG_ANDROID_PARANOID_NETWORK is not set
+# CONFIG_NET_ACTIVITY_STATS is not set
+CONFIG_NETFILTER=y
+CONFIG_NF_CONNTRACK=m
+CONFIG_NF_CONNTRACK_FTP=m
+CONFIG_NF_CONNTRACK_H323=m
+CONFIG_NF_CONNTRACK_SIP=m
+CONFIG_NF_CT_NETLINK=m
+CONFIG_NETFILTER_XT_MATCH_CONNTRACK=m
+CONFIG_NETFILTER_XT_MATCH_POLICY=m
+CONFIG_NETFILTER_XT_MATCH_STATE=m
+CONFIG_NF_CONNTRACK_IPV4=m
+CONFIG_IP_NF_IPTABLES=m
+CONFIG_IP_NF_FILTER=m
+CONFIG_IP_NF_TARGET_REJECT=m
+CONFIG_IP_NF_TARGET_LOG=m
+CONFIG_IP_NF_TARGET_ULOG=m
+CONFIG_NF_NAT=m
+CONFIG_IP_NF_TARGET_MASQUERADE=m
+CONFIG_NET_SCHED=y
+CONFIG_NET_SCH_CBQ=m
+CONFIG_NET_SCH_SFQ=m
+CONFIG_NET_SCH_MQPRIO=m
# CONFIG_FIRMWARE_IN_KERNEL is not set
+CONFIG_DEBUG_DRIVER=y
+CONFIG_MTD=y
+CONFIG_MTD_BLOCK=y
+CONFIG_SM_FTL=y
+CONFIG_MTD_NAND_TEGRA=y
+CONFIG_MTD_NAND=y
CONFIG_BLK_DEV_LOOP=y
CONFIG_MISC_DEVICES=y
CONFIG_AD525X_DPOT=y
@@ -72,37 +106,82 @@ CONFIG_AD525X_DPOT_I2C=y
CONFIG_ICS932S401=y
CONFIG_APDS9802ALS=y
CONFIG_ISL29003=y
+CONFIG_SENSORS_AK8975=y
+CONFIG_SCSI=y
+CONFIG_BLK_DEV_SD=y
CONFIG_NETDEVICES=y
CONFIG_DUMMY=y
CONFIG_R8169=y
# CONFIG_NETDEV_10000 is not set
-# CONFIG_WLAN is not set
-# CONFIG_INPUT is not set
-# CONFIG_SERIO is not set
-# CONFIG_VT is not set
+CONFIG_BCM4329=m
+CONFIG_BCM4329_FW_PATH="/lib/firmware/bcm4329/fw_bcm4329.bin"
+CONFIG_BCM4329_NVRAM_PATH="/lib/firmware/bcm4329/nvram.txt"
+CONFIG_BCM4329_WIFI_CONTROL_FUNC=y
+CONFIG_USB_CATC=y
+CONFIG_USB_KAWETH=y
+CONFIG_USB_PEGASUS=y
+CONFIG_USB_RTL8150=y
+CONFIG_USB_USBNET=y
+# CONFIG_USB_NET_CDC_NCM is not set
+CONFIG_USB_NET_DM9601=y
+CONFIG_USB_NET_SMSC75XX=y
+CONFIG_USB_NET_SMSC95XX=y
+# CONFIG_USB_NET_NET1080 is not set
+CONFIG_USB_NET_MCS7830=y
+# CONFIG_USB_NET_CDC_SUBSET is not set
+# CONFIG_USB_NET_ZAURUS is not set
+# CONFIG_INPUT_MOUSEDEV is not set
+CONFIG_INPUT_EVDEV=y
+CONFIG_KEYBOARD_TEGRA=y
+# CONFIG_LEGACY_PTYS is not set
# CONFIG_DEVKMEM is not set
CONFIG_SERIAL_8250=y
CONFIG_SERIAL_8250_CONSOLE=y
-# CONFIG_LEGACY_PTYS is not set
# CONFIG_HW_RANDOM is not set
-CONFIG_I2C=y
# CONFIG_I2C_COMPAT is not set
# CONFIG_I2C_HELPER_AUTO is not set
CONFIG_I2C_TEGRA=y
+CONFIG_SENSORS_ADT7461=y
CONFIG_SENSORS_LM90=y
+CONFIG_MFD_MAX8907C=y
CONFIG_MFD_TPS6586X=y
CONFIG_REGULATOR=y
+CONFIG_REGULATOR_FIXED_VOLTAGE=y
+CONFIG_REGULATOR_MAX8907C=y
CONFIG_REGULATOR_TPS6586X=y
-# CONFIG_USB_SUPPORT is not set
+CONFIG_FB=y
+CONFIG_TEGRA_GRHOST=y
+CONFIG_TEGRA_DC=y
+CONFIG_TEGRA_DC_EXTENSIONS=y
+CONFIG_SOUND=y
+CONFIG_SND=y
+CONFIG_SND_SOC=y
+CONFIG_SND_SOC_TEGRA=y
+CONFIG_SND_SOC_TEGRA_WM8903=y
+CONFIG_USB=y
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
+# CONFIG_USB_DEVICE_CLASS is not set
+CONFIG_USB_SUSPEND=y
+CONFIG_USB_OTG=y
+# CONFIG_USB_OTG_WHITELIST is not set
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_EHCI_TEGRA=y
+CONFIG_USB_STORAGE=y
+CONFIG_USB_LIBUSUAL=y
+CONFIG_USB_TEGRA_OTG=y
CONFIG_MMC=y
+CONFIG_MMC_EMBEDDED_SDIO=y
+CONFIG_MMC_BLOCK_DEFERRED_RESUME=y
CONFIG_MMC_SDHCI=y
CONFIG_MMC_SDHCI_PLTFM=y
CONFIG_MMC_SDHCI_TEGRA=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_TPS6586X=y
+CONFIG_RTC_DRV_TEGRA=y
CONFIG_STAGING=y
# CONFIG_STAGING_EXCLUDE_BUILD is not set
CONFIG_IIO=y
CONFIG_SENSORS_ISL29018=y
-CONFIG_SENSORS_AK8975=y
CONFIG_EXT2_FS=y
CONFIG_EXT2_FS_XATTR=y
CONFIG_EXT2_FS_POSIX_ACL=y
@@ -111,18 +190,37 @@ CONFIG_EXT3_FS=y
# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
CONFIG_EXT3_FS_POSIX_ACL=y
CONFIG_EXT3_FS_SECURITY=y
+CONFIG_EXT4_FS=y
+CONFIG_EXT4_FS_POSIX_ACL=y
+CONFIG_EXT4_FS_SECURITY=y
# CONFIG_DNOTIFY is not set
-CONFIG_VFAT_FS=y
+CONFIG_FUSE_FS=m
+CONFIG_ISO9660_FS=m
+CONFIG_JOLIET=y
+CONFIG_ZISOFS=y
+CONFIG_MSDOS_FS=m
+CONFIG_VFAT_FS=m
+CONFIG_NTFS_FS=m
+CONFIG_NTFS_RW=y
CONFIG_TMPFS=y
+CONFIG_YAFFS_FS=y
+CONFIG_YAFFS_DISABLE_TAGS_ECC=y
CONFIG_NFS_FS=y
+CONFIG_NFS_V3=y
+CONFIG_NFS_V3_ACL=y
+CONFIG_NFS_V4=y
+CONFIG_NFS_V4_1=y
CONFIG_ROOT_NFS=y
+CONFIG_CIFS=m
+CONFIG_CIFS_EXPERIMENTAL=y
CONFIG_PARTITION_ADVANCED=y
CONFIG_EFI_PARTITION=y
-CONFIG_NLS_CODEPAGE_437=y
-CONFIG_NLS_ISO8859_1=y
+CONFIG_NLS_CODEPAGE_437=m
+CONFIG_NLS_ASCII=m
+CONFIG_NLS_ISO8859_1=m
+CONFIG_NLS_UTF8=m
CONFIG_PRINTK_TIME=y
CONFIG_MAGIC_SYSRQ=y
-CONFIG_DEBUG_FS=y
CONFIG_DEBUG_KERNEL=y
CONFIG_DETECT_HUNG_TASK=y
CONFIG_SCHEDSTATS=y
@@ -135,12 +233,17 @@ CONFIG_DEBUG_INFO=y
CONFIG_DEBUG_VM=y
CONFIG_DEBUG_SG=y
# CONFIG_RCU_CPU_STALL_DETECTOR is not set
-CONFIG_DEBUG_LL=y
-CONFIG_EARLY_PRINTK=y
+CONFIG_FUNCTION_TRACER=y
+CONFIG_CRYPTO_AUTHENC=y
+CONFIG_CRYPTO_CBC=y
CONFIG_CRYPTO_ECB=y
+CONFIG_CRYPTO_HMAC=y
+CONFIG_CRYPTO_MD5=y
+CONFIG_CRYPTO_SHA1=y
CONFIG_CRYPTO_AES=y
CONFIG_CRYPTO_ARC4=y
+CONFIG_CRYPTO_DES=y
CONFIG_CRYPTO_TWOFISH=y
+CONFIG_CRYPTO_DEFLATE=y
# CONFIG_CRYPTO_ANSI_CPRNG is not set
CONFIG_CRC_CCITT=y
-CONFIG_CRC16=y
diff --git a/arch/arm/configs/tegra_p852_gnu_linux_defconfig b/arch/arm/configs/tegra_p852_gnu_linux_defconfig
new file mode 100644
index 000000000000..e393d437365f
--- /dev/null
+++ b/arch/arm/configs/tegra_p852_gnu_linux_defconfig
@@ -0,0 +1,303 @@
+CONFIG_EXPERIMENTAL=y
+CONFIG_CROSS_COMPILE="arm-eabi-"
+# CONFIG_SWAP is not set
+CONFIG_SYSVIPC=y
+CONFIG_POSIX_MQUEUE=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_CGROUPS=y
+CONFIG_CGROUP_DEBUG=y
+CONFIG_CGROUP_FREEZER=y
+CONFIG_CGROUP_CPUACCT=y
+CONFIG_RESOURCE_COUNTERS=y
+CONFIG_CGROUP_SCHED=y
+CONFIG_RT_GROUP_SCHED=y
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_PANIC_TIMEOUT=10
+CONFIG_ASHMEM=y
+CONFIG_EMBEDDED=y
+CONFIG_ASHMEM=y
+CONFIG_SLAB=y
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODULE_FORCE_UNLOAD=y
+# CONFIG_BLK_DEV_BSG is not set
+# CONFIG_IOSCHED_DEADLINE is not set
+# CONFIG_IOSCHED_CFQ is not set
+CONFIG_ARCH_TEGRA=y
+CONFIG_MACH_P852=y
+CONFIG_TEGRA_PWM=y
+# CONFIG_TEGRA_CPU_DVFS is not set
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_SMP=y
+CONFIG_VMSPLIT_2G=y
+CONFIG_NR_CPUS=2
+CONFIG_PREEMPT=y
+CONFIG_AEABI=y
+# CONFIG_OABI_COMPAT is not set
+CONFIG_HIGHMEM=y
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZBOOT_ROM_BSS=0x0
+CONFIG_CMDLINE="mem=448M@0M console=ttyS0,115200n8 earlyprintk init=/bin/ash"
+CONFIG_VFP=y
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_NET_KEY=y
+CONFIG_INET=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+CONFIG_SYN_COOKIES=y
+CONFIG_INET_ESP=y
+# CONFIG_INET_XFRM_MODE_TUNNEL is not set
+# CONFIG_INET_XFRM_MODE_BEET is not set
+# CONFIG_INET_LRO is not set
+# CONFIG_INET_DIAG is not set
+# CONFIG_IPV6 is not set
+# CONFIG_ANDROID_PARANOID_NETWORK is not set
+CONFIG_NETFILTER=y
+CONFIG_NETFILTER_NETLINK_LOG=y
+CONFIG_NF_CONNTRACK=y
+CONFIG_NF_CONNTRACK_EVENTS=y
+CONFIG_NF_CT_PROTO_DCCP=y
+CONFIG_NF_CT_PROTO_SCTP=y
+CONFIG_NF_CT_PROTO_UDPLITE=y
+CONFIG_NF_CONNTRACK_AMANDA=y
+CONFIG_NF_CONNTRACK_FTP=y
+CONFIG_NF_CONNTRACK_H323=y
+CONFIG_NF_CONNTRACK_IRC=y
+CONFIG_NF_CONNTRACK_NETBIOS_NS=y
+CONFIG_NF_CONNTRACK_PPTP=y
+CONFIG_NF_CONNTRACK_SANE=y
+CONFIG_NF_CONNTRACK_SIP=y
+CONFIG_NF_CONNTRACK_TFTP=y
+CONFIG_NF_CT_NETLINK=y
+CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y
+CONFIG_NETFILTER_XT_TARGET_CONNMARK=y
+CONFIG_NETFILTER_XT_TARGET_MARK=y
+CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y
+CONFIG_NETFILTER_XT_MATCH_COMMENT=y
+CONFIG_NETFILTER_XT_MATCH_CONNBYTES=y
+CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y
+CONFIG_NETFILTER_XT_MATCH_CONNMARK=y
+CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y
+CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y
+CONFIG_NETFILTER_XT_MATCH_HELPER=y
+CONFIG_NETFILTER_XT_MATCH_IPRANGE=y
+CONFIG_NETFILTER_XT_MATCH_LENGTH=y
+CONFIG_NETFILTER_XT_MATCH_LIMIT=y
+CONFIG_NETFILTER_XT_MATCH_MAC=y
+CONFIG_NETFILTER_XT_MATCH_MARK=y
+CONFIG_NETFILTER_XT_MATCH_OWNER=y
+CONFIG_NETFILTER_XT_MATCH_POLICY=y
+CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y
+CONFIG_NETFILTER_XT_MATCH_QUOTA=y
+CONFIG_NETFILTER_XT_MATCH_STATE=y
+CONFIG_NETFILTER_XT_MATCH_STATISTIC=y
+CONFIG_NETFILTER_XT_MATCH_STRING=y
+CONFIG_NETFILTER_XT_MATCH_TIME=y
+CONFIG_NETFILTER_XT_MATCH_U32=y
+CONFIG_NF_CONNTRACK_IPV4=y
+CONFIG_IP_NF_IPTABLES=y
+CONFIG_IP_NF_MATCH_AH=y
+CONFIG_IP_NF_MATCH_ECN=y
+CONFIG_IP_NF_MATCH_TTL=y
+CONFIG_IP_NF_FILTER=y
+CONFIG_IP_NF_TARGET_REJECT=y
+CONFIG_IP_NF_TARGET_LOG=y
+CONFIG_NF_NAT=y
+CONFIG_IP_NF_TARGET_MASQUERADE=y
+CONFIG_IP_NF_TARGET_NETMAP=y
+CONFIG_IP_NF_TARGET_REDIRECT=y
+CONFIG_IP_NF_ARPTABLES=y
+CONFIG_IP_NF_ARPFILTER=y
+CONFIG_IP_NF_ARP_MANGLE=y
+CONFIG_NET_SCHED=y
+CONFIG_NET_SCH_HTB=y
+CONFIG_NET_SCH_INGRESS=y
+CONFIG_NET_CLS_U32=y
+CONFIG_NET_EMATCH=y
+CONFIG_NET_EMATCH_U32=y
+CONFIG_NET_CLS_ACT=y
+CONFIG_NET_ACT_POLICE=y
+CONFIG_NET_ACT_GACT=y
+CONFIG_NET_ACT_MIRRED=y
+CONFIG_BT=y
+CONFIG_BT_L2CAP=y
+CONFIG_BT_SCO=y
+CONFIG_BT_RFCOMM=y
+CONFIG_BT_RFCOMM_TTY=y
+CONFIG_BT_BNEP=y
+CONFIG_BT_HIDP=y
+CONFIG_BT_HCIUART=y
+CONFIG_BT_HCIUART_H4=y
+CONFIG_BT_HCIUART_LL=y
+CONFIG_RFKILL=y
+# CONFIG_FIRMWARE_IN_KERNEL is not set
+CONFIG_MTD=y
+CONFIG_MTD_PARTITIONS=y
+CONFIG_MTD_CMDLINE_PARTS=y
+CONFIG_MTD_CHAR=y
+CONFIG_MTD_BLOCK=y
+CONFIG_MTD_CFI=y
+CONFIG_MTD_CFI_AMDSTD=y
+CONFIG_MTD_COMPLEX_MAPPINGS=y
+CONFIG_MTD_NAND_TEGRA=y
+CONFIG_MTD_NAND=y
+CONFIG_MTD_UBI=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_SCSI=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_BLK_DEV_SR=y
+CONFIG_BLK_DEV_SR_VENDOR=y
+CONFIG_CHR_DEV_SG=y
+CONFIG_SCSI_MULTI_LUN=y
+CONFIG_MD=y
+CONFIG_BLK_DEV_DM=y
+CONFIG_DM_CRYPT=y
+CONFIG_DM_UEVENT=y
+CONFIG_NETDEVICES=y
+CONFIG_DUMMY=y
+CONFIG_NET_ETHERNET=y
+CONFIG_SMC91X=y
+# CONFIG_NETDEV_1000 is not set
+# CONFIG_NETDEV_10000 is not set
+CONFIG_BCM4329_WIFI_CONTROL_FUNC=y
+CONFIG_USB_USBNET=y
+CONFIG_USB_NET_SMSC95XX=y
+# CONFIG_USB_NET_NET1080 is not set
+# CONFIG_USB_BELKIN is not set
+# CONFIG_USB_ARMLINUX is not set
+# CONFIG_USB_NET_ZAURUS is not set
+CONFIG_PPP=y
+CONFIG_PPP_ASYNC=y
+CONFIG_PPP_DEFLATE=y
+CONFIG_PPP_BSDCOMP=y
+CONFIG_PPP_MPPE=y
+CONFIG_PPPOLAC=y
+CONFIG_PPPOPNS=y
+# CONFIG_INPUT_MOUSEDEV is not set
+CONFIG_INPUT_EVDEV=y
+CONFIG_INPUT_KEYRESET=y
+# CONFIG_KEYBOARD_ATKBD is not set
+CONFIG_KEYBOARD_GPIO=y
+# CONFIG_INPUT_MOUSE is not set
+CONFIG_INPUT_MISC=y
+CONFIG_INPUT_UINPUT=y
+CONFIG_INPUT_GPIO=y
+# CONFIG_SERIO is not set
+CONFIG_VT_HW_CONSOLE_BINDING=y
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_SERIAL_8250_NR_UARTS=2
+CONFIG_SERIAL_8250_RUNTIME_UARTS=2
+CONFIG_SERIAL_TEGRA=y
+# CONFIG_LEGACY_PTYS is not set
+# CONFIG_HW_RANDOM is not set
+CONFIG_I2C_CHARDEV=y
+CONFIG_I2C_TEGRA=y
+CONFIG_SPI=y
+CONFIG_SPI_TEGRA=y
+CONFIG_SPI_SPIDEV=y
+CONFIG_GPIO_SYSFS=y
+CONFIG_POWER_SUPPLY=y
+CONFIG_MFD_TPS6586X=y
+CONFIG_REGULATOR=y
+CONFIG_REGULATOR_DUMMY=y
+CONFIG_REGULATOR_FIXED_VOLTAGE=y
+CONFIG_REGULATOR_VIRTUAL_CONSUMER=y
+CONFIG_REGULATOR_TPS6586X=y
+CONFIG_MEDIA_SUPPORT=y
+CONFIG_VIDEO_DEV=y
+# CONFIG_VIDEO_ALLOW_V4L1 is not set
+# CONFIG_RC_MAP is not set
+# CONFIG_IR_NEC_DECODER is not set
+# CONFIG_IR_RC5_DECODER is not set
+# CONFIG_IR_RC6_DECODER is not set
+# CONFIG_IR_JVC_DECODER is not set
+# CONFIG_IR_SONY_DECODER is not set
+# CONFIG_MEDIA_TUNER_CUSTOMISE is not set
+CONFIG_VIDEO_HELPER_CHIPS_AUTO=y
+# CONFIG_TEGRA_CAMERA is not set
+CONFIG_USB_VIDEO_CLASS=y
+# CONFIG_USB_GSPCA is not set
+CONFIG_VIDEO_OUTPUT_CONTROL=y
+CONFIG_FB=y
+CONFIG_TEGRA_DC=y
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+CONFIG_LCD_CLASS_DEVICE=y
+CONFIG_BACKLIGHT_CLASS_DEVICE=y
+# CONFIG_BACKLIGHT_GENERIC is not set
+CONFIG_BACKLIGHT_PWM=y
+CONFIG_SOUND=y
+CONFIG_SND=y
+CONFIG_SND_SOC=y
+CONFIG_HIDRAW=y
+CONFIG_USB_HIDDEV=y
+CONFIG_USB=y
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
+CONFIG_USB_DEVICEFS=y
+CONFIG_USB_EHCI_HCD=y
+# CONFIG_USB_EHCI_TT_NEWSCHED is not set
+CONFIG_USB_EHCI_TEGRA=y
+CONFIG_USB_ACM=y
+CONFIG_USB_WDM=y
+CONFIG_USB_STORAGE=y
+CONFIG_USB_LIBUSUAL=y
+CONFIG_USB_SERIAL=y
+CONFIG_USB_SERIAL_GENERIC=y
+CONFIG_USB_SERIAL_PL2303=y
+CONFIG_USB_TEGRA_OTG=y
+CONFIG_MMC=y
+CONFIG_MMC_UNSAFE_RESUME=y
+# CONFIG_MMC_BLOCK_BOUNCE is not set
+CONFIG_MMC_SDHCI=y
+CONFIG_MMC_SDHCI_TEGRA=y
+CONFIG_SWITCH=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_TPS6586X=y
+CONFIG_DMADEVICES=y
+CONFIG_EXT2_FS=y
+CONFIG_EXT2_FS_XATTR=y
+CONFIG_EXT2_FS_POSIX_ACL=y
+CONFIG_EXT2_FS_SECURITY=y
+CONFIG_EXT3_FS=y
+# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
+CONFIG_EXT3_FS_POSIX_ACL=y
+CONFIG_EXT3_FS_SECURITY=y
+CONFIG_EXT4_FS=y
+# CONFIG_DNOTIFY is not set
+CONFIG_FUSE_FS=y
+CONFIG_ISO9660_FS=y
+CONFIG_JOLIET=y
+CONFIG_UDF_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_TMPFS=y
+CONFIG_NFS_FS=y
+CONFIG_NFS_V3=y
+CONFIG_NFS_V3_ACL=y
+CONFIG_NFS_V4=y
+CONFIG_ROOT_NFS=y
+CONFIG_PARTITION_ADVANCED=y
+CONFIG_EFI_PARTITION=y
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_PRINTK_TIME=y
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_DEBUG_FS=y
+CONFIG_DEBUG_KERNEL=y
+CONFIG_DETECT_HUNG_TASK=y
+CONFIG_SCHEDSTATS=y
+CONFIG_TIMER_STATS=y
+# CONFIG_DEBUG_PREEMPT is not set
+# CONFIG_DEBUG_BUGVERBOSE is not set
+CONFIG_DEBUG_INFO=y
+CONFIG_DEBUG_VM=y
+# CONFIG_RCU_CPU_STALL_DETECTOR is not set
+CONFIG_CRYPTO_AES=y
+CONFIG_CRYPTO_TWOFISH=y
+CONFIG_CRYPTO_DEFLATE=y
+CONFIG_CRYPTO_LZO=y
+# CONFIG_CRYPTO_ANSI_CPRNG is not set
diff --git a/arch/arm/include/asm/cacheflush.h b/arch/arm/include/asm/cacheflush.h
index d5d8d5c72682..f91a748d0736 100644
--- a/arch/arm/include/asm/cacheflush.h
+++ b/arch/arm/include/asm/cacheflush.h
@@ -249,7 +249,7 @@ extern void flush_cache_page(struct vm_area_struct *vma, unsigned long user_addr
* Harvard caches are synchronised for the user space address range.
* This is used for the ARM private sys_cacheflush system call.
*/
-#define flush_cache_user_range(vma,start,end) \
+#define flush_cache_user_range(start,end) \
__cpuc_coherent_user_range((start) & PAGE_MASK, PAGE_ALIGN(end))
/*
@@ -344,4 +344,53 @@ static inline void flush_cache_vunmap(unsigned long start, unsigned long end)
flush_cache_all();
}
+/*
+ * The set_memory_* API can be used to change various attributes of a virtual
+ * address range. The attributes include:
+ * Cachability : UnCached, WriteCombining, WriteBack
+ * Executability : eXeutable, NoteXecutable
+ * Read/Write : ReadOnly, ReadWrite
+ * Presence : NotPresent
+ *
+ * Within a catagory, the attributes are mutually exclusive.
+ *
+ * The implementation of this API will take care of various aspects that
+ * are associated with changing such attributes, such as:
+ * - Flushing TLBs
+ * - Flushing CPU caches
+ * - Making sure aliases of the memory behind the mapping don't violate
+ * coherency rules as defined by the CPU in the system.
+ *
+ * What this API does not do:
+ * - Provide exclusion between various callers - including callers that
+ * operation on other mappings of the same physical page
+ * - Restore default attributes when a page is freed
+ * - Guarantee that mappings other than the requested one are
+ * in any state, other than that these do not violate rules for
+ * the CPU you have. Do not depend on any effects on other mappings,
+ * CPUs other than the one you have may have more relaxed rules.
+ * The caller is required to take care of these.
+ */
+
+int set_memory_uc(unsigned long addr, int numpages);
+int set_memory_wc(unsigned long addr, int numpages);
+int set_memory_wb(unsigned long addr, int numpages);
+int set_memory_iwb(unsigned long addr, int numpages);
+int set_memory_x(unsigned long addr, int numpages);
+int set_memory_nx(unsigned long addr, int numpages);
+int set_memory_ro(unsigned long addr, int numpages);
+int set_memory_rw(unsigned long addr, int numpages);
+int set_memory_np(unsigned long addr, int numpages);
+int set_memory_4k(unsigned long addr, int numpages);
+
+int set_memory_array_uc(unsigned long *addr, int addrinarray);
+int set_memory_array_wc(unsigned long *addr, int addrinarray);
+int set_memory_array_wb(unsigned long *addr, int addrinarray);
+int set_memory_array_iwb(unsigned long *addr, int addrinarray);
+
+int set_pages_array_uc(struct page **pages, int addrinarray);
+int set_pages_array_wc(struct page **pages, int addrinarray);
+int set_pages_array_wb(struct page **pages, int addrinarray);
+int set_pages_array_iwb(struct page **pages, int addrinarray);
+
#endif
diff --git a/arch/arm/include/asm/cpu_pm.h b/arch/arm/include/asm/cpu_pm.h
new file mode 100644
index 000000000000..07b1b6ec025c
--- /dev/null
+++ b/arch/arm/include/asm/cpu_pm.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2011 Google, Inc.
+ *
+ * Author:
+ * Colin Cross <ccross@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 _ASMARM_CPU_PM_H
+#define _ASMARM_CPU_PM_H
+
+#include <linux/kernel.h>
+#include <linux/notifier.h>
+
+/*
+ * When a CPU goes to a low power state that turns off power to the CPU's
+ * power domain, the contents of some blocks (floating point coprocessors,
+ * interrutp controllers, caches, timers) in the same power domain can
+ * be lost. The cpm_pm notifiers provide a method for platform idle, suspend,
+ * and hotplug implementations to notify the drivers for these blocks that
+ * they may be reset.
+ *
+ * All cpu_pm notifications must be called with interrupts disabled.
+ *
+ * The notifications are split into two classes, CPU notifications and CPU
+ * complex notifications.
+ *
+ * CPU notifications apply to a single CPU, and must be called on the affected
+ * CPU. They are used to save per-cpu context for affected blocks.
+ *
+ * CPU complex notifications apply to all CPUs in a single power domain. They
+ * are used to save any global context for affected blocks, and must be called
+ * after all the CPUs in the power domain have been notified of the low power
+ * state.
+ *
+ */
+
+/*
+ * Event codes passed as unsigned long val to notifier calls
+ */
+enum cpu_pm_event {
+ /* A single cpu is entering a low power state */
+ CPU_PM_ENTER,
+
+ /* A single cpu failed to enter a low power state */
+ CPU_PM_ENTER_FAILED,
+
+ /* A single cpu is exiting a low power state */
+ CPU_PM_EXIT,
+
+ /* A cpu power domain is entering a low power state */
+ CPU_COMPLEX_PM_ENTER,
+
+ /* A cpu power domain failed to enter a low power state */
+ CPU_COMPLEX_PM_ENTER_FAILED,
+
+ /* A cpu power domain is exiting a low power state */
+ CPU_COMPLEX_PM_EXIT,
+};
+
+int cpu_pm_register_notifier(struct notifier_block *nb);
+int cpu_pm_unregister_notifier(struct notifier_block *nb);
+
+/*
+ * cpm_pm_enter
+ *
+ * Notifies listeners that a single cpu is entering a low power state that may
+ * cause some blocks in the same power domain as the cpu to reset.
+ *
+ * Must be called on the affected cpu with interrupts disabled. Platform is
+ * responsible for ensuring that cpu_pm_enter is not called twice on the same
+ * cpu before cpu_pm_exit is called.
+ */
+int cpu_pm_enter(void);
+
+/*
+ * cpm_pm_exit
+ *
+ * Notifies listeners that a single cpu is exiting a low power state that may
+ * have caused some blocks in the same power domain as the cpu to reset.
+ *
+ * Must be called on the affected cpu with interrupts disabled.
+ */
+int cpu_pm_exit(void);
+
+/*
+ * cpm_complex_pm_enter
+ *
+ * Notifies listeners that all cpus in a power domain are entering a low power
+ * state that may cause some blocks in the same power domain to reset.
+ *
+ * Must be called after cpu_pm_enter has been called on all cpus in the power
+ * domain, and before cpu_pm_exit has been called on any cpu in the power
+ * domain.
+ *
+ * Must be called with interrupts disabled.
+ */
+int cpu_complex_pm_enter(void);
+
+/*
+ * cpm_pm_enter
+ *
+ * Notifies listeners that a single cpu is entering a low power state that may
+ * cause some blocks in the same power domain as the cpu to reset.
+ *
+ * Must be called after cpu_pm_enter has been called on all cpus in the power
+ * domain, and before cpu_pm_exit has been called on any cpu in the power
+ * domain.
+ *
+ * Must be called with interrupts disabled.
+ */
+int cpu_complex_pm_exit(void);
+
+#endif
diff --git a/arch/arm/include/asm/delay.h b/arch/arm/include/asm/delay.h
index b2deda181549..57f1fa0e983b 100644
--- a/arch/arm/include/asm/delay.h
+++ b/arch/arm/include/asm/delay.h
@@ -8,6 +8,9 @@
#include <asm/param.h> /* HZ */
+#ifdef CONFIG_ARCH_PROVIDES_UDELAY
+#include <mach/delay.h>
+#else
extern void __delay(int loops);
/*
@@ -40,5 +43,6 @@ extern void __const_udelay(unsigned long);
__const_udelay((n) * ((2199023U*HZ)>>11))) : \
__udelay(n))
+#endif /* defined(ARCH_PROVIDES_UDELAY) */
#endif /* defined(_ARM_DELAY_H) */
diff --git a/arch/arm/include/asm/elf.h b/arch/arm/include/asm/elf.h
index 0e9ce8d9686e..0691cdce48e8 100644
--- a/arch/arm/include/asm/elf.h
+++ b/arch/arm/include/asm/elf.h
@@ -52,6 +52,7 @@ typedef struct user_fp elf_fpregset_t;
#define R_ARM_ABS32 2
#define R_ARM_CALL 28
#define R_ARM_JUMP24 29
+#define R_ARM_TARGET1 38
#define R_ARM_V4BX 40
#define R_ARM_PREL31 42
#define R_ARM_MOVW_ABS_NC 43
diff --git a/arch/arm/include/asm/fiq_debugger.h b/arch/arm/include/asm/fiq_debugger.h
new file mode 100644
index 000000000000..4d274883ba6a
--- /dev/null
+++ b/arch/arm/include/asm/fiq_debugger.h
@@ -0,0 +1,64 @@
+/*
+ * arch/arm/include/asm/fiq_debugger.h
+ *
+ * Copyright (C) 2010 Google, Inc.
+ * Author: Colin Cross <ccross@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 _ARCH_ARM_MACH_TEGRA_FIQ_DEBUGGER_H_
+#define _ARCH_ARM_MACH_TEGRA_FIQ_DEBUGGER_H_
+
+#include <linux/serial_core.h>
+
+#define FIQ_DEBUGGER_NO_CHAR NO_POLL_CHAR
+#define FIQ_DEBUGGER_BREAK 0x00ff0100
+
+#define FIQ_DEBUGGER_FIQ_IRQ_NAME "fiq"
+#define FIQ_DEBUGGER_SIGNAL_IRQ_NAME "signal"
+#define FIQ_DEBUGGER_WAKEUP_IRQ_NAME "wakeup"
+
+/**
+ * struct fiq_debugger_pdata - fiq debugger platform data
+ * @uart_resume: used to restore uart state right before enabling
+ * the fiq.
+ * @uart_enable: Do the work necessary to communicate with the uart
+ * hw (enable clocks, etc.). This must be ref-counted.
+ * @uart_disable: Do the work necessary to disable the uart hw
+ * (disable clocks, etc.). This must be ref-counted.
+ * @uart_dev_suspend: called during PM suspend, generally not needed
+ * for real fiq mode debugger.
+ * @uart_dev_resume: called during PM resume, generally not needed
+ * for real fiq mode debugger.
+ */
+struct fiq_debugger_pdata {
+ int (*uart_init)(struct platform_device *pdev);
+ void (*uart_free)(struct platform_device *pdev);
+ int (*uart_resume)(struct platform_device *pdev);
+ int (*uart_getc)(struct platform_device *pdev);
+ void (*uart_putc)(struct platform_device *pdev, unsigned int c);
+ void (*uart_flush)(struct platform_device *pdev);
+ void (*uart_enable)(struct platform_device *pdev);
+ void (*uart_disable)(struct platform_device *pdev);
+
+ int (*uart_dev_suspend)(struct platform_device *pdev);
+ int (*uart_dev_resume)(struct platform_device *pdev);
+
+ void (*fiq_enable)(struct platform_device *pdev, unsigned int fiq,
+ bool enable);
+ void (*fiq_ack)(struct platform_device *pdev, unsigned int fiq);
+
+ void (*force_irq)(struct platform_device *pdev, unsigned int irq);
+ void (*force_irq_ack)(struct platform_device *pdev, unsigned int irq);
+};
+
+#endif
diff --git a/arch/arm/include/asm/fiq_glue.h b/arch/arm/include/asm/fiq_glue.h
new file mode 100644
index 000000000000..d54c29db97a8
--- /dev/null
+++ b/arch/arm/include/asm/fiq_glue.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2010 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 __ASM_FIQ_GLUE_H
+#define __ASM_FIQ_GLUE_H
+
+struct fiq_glue_handler {
+ void (*fiq)(struct fiq_glue_handler *h, void *regs, void *svc_sp);
+ void (*resume)(struct fiq_glue_handler *h);
+};
+
+int fiq_glue_register_handler(struct fiq_glue_handler *handler);
+
+#ifdef CONFIG_FIQ_GLUE
+void fiq_glue_resume(void);
+#else
+static inline void fiq_glue_resume(void) {}
+#endif
+
+#endif
diff --git a/arch/arm/include/asm/hardirq.h b/arch/arm/include/asm/hardirq.h
index 89ad1805e579..2635c8b5bf59 100644
--- a/arch/arm/include/asm/hardirq.h
+++ b/arch/arm/include/asm/hardirq.h
@@ -5,7 +5,7 @@
#include <linux/threads.h>
#include <asm/irq.h>
-#define NR_IPI 5
+#define NR_IPI 6
typedef struct {
unsigned int __softirq_pending;
diff --git a/arch/arm/include/asm/hardware/cache-l2x0.h b/arch/arm/include/asm/hardware/cache-l2x0.h
index 99a6ed7e1bfd..fd04f24055fd 100644
--- a/arch/arm/include/asm/hardware/cache-l2x0.h
+++ b/arch/arm/include/asm/hardware/cache-l2x0.h
@@ -62,6 +62,7 @@
#define L2X0_STNDBY_MODE_EN (1 << 0)
/* Registers shifts and masks */
+#define L2X0_CACHE_ID_REV_MASK (0x3f)
#define L2X0_CACHE_ID_PART_MASK (0xf << 6)
#define L2X0_CACHE_ID_PART_L210 (1 << 6)
#define L2X0_CACHE_ID_PART_L310 (3 << 6)
@@ -77,8 +78,11 @@
#define L2X0_AUX_CTRL_INSTR_PREFETCH_SHIFT 29
#define L2X0_AUX_CTRL_EARLY_BRESP_SHIFT 30
+#define REV_PL310_R2P0 4
+
#ifndef __ASSEMBLY__
-extern void __init l2x0_init(void __iomem *base, __u32 aux_val, __u32 aux_mask);
+extern void l2x0_init(void __iomem *base, __u32 aux_val, __u32 aux_mask);
+extern void l2x0_enable(void);
#endif
#endif
diff --git a/arch/arm/include/asm/hardware/coresight.h b/arch/arm/include/asm/hardware/coresight.h
index 7ecd793b8f5a..6643d6c4f35e 100644
--- a/arch/arm/include/asm/hardware/coresight.h
+++ b/arch/arm/include/asm/hardware/coresight.h
@@ -17,15 +17,17 @@
#define TRACER_ACCESSED_BIT 0
#define TRACER_RUNNING_BIT 1
#define TRACER_CYCLE_ACC_BIT 2
+#define TRACER_TRACE_DATA_BIT 3
#define TRACER_ACCESSED BIT(TRACER_ACCESSED_BIT)
#define TRACER_RUNNING BIT(TRACER_RUNNING_BIT)
#define TRACER_CYCLE_ACC BIT(TRACER_CYCLE_ACC_BIT)
+#define TRACER_TRACE_DATA BIT(TRACER_TRACE_DATA_BIT)
#define TRACER_TIMEOUT 10000
-#define etm_writel(t, v, x) \
- (__raw_writel((v), (t)->etm_regs + (x)))
-#define etm_readl(t, x) (__raw_readl((t)->etm_regs + (x)))
+#define etm_writel(t, id, v, x) \
+ (__raw_writel((v), (t)->etm_regs[(id)] + (x)))
+#define etm_readl(t, id, x) (__raw_readl((t)->etm_regs[(id)] + (x)))
/* CoreSight Management Registers */
#define CSMR_LOCKACCESS 0xfb0
@@ -113,11 +115,19 @@
#define ETMR_TRACEENCTRL 0x24
#define ETMTE_INCLEXCL BIT(24)
#define ETMR_TRACEENEVT 0x20
+
+#define ETMR_VIEWDATAEVT 0x30
+#define ETMR_VIEWDATACTRL1 0x34
+#define ETMR_VIEWDATACTRL2 0x38
+#define ETMR_VIEWDATACTRL3 0x3c
+#define ETMVDC3_EXCLONLY BIT(16)
+
#define ETMCTRL_OPTS (ETMCTRL_DO_CPRT | \
- ETMCTRL_DATA_DO_ADDR | \
ETMCTRL_BRANCH_OUTPUT | \
ETMCTRL_DO_CONTEXTID)
+#define ETMR_TRACEIDR 0x200
+
/* ETM management registers, "ETM Architecture", 3.5.24 */
#define ETMMR_OSLAR 0x300
#define ETMMR_OSLSR 0x304
@@ -140,14 +150,16 @@
#define ETBFF_TRIGIN BIT(8)
#define ETBFF_TRIGEVT BIT(9)
#define ETBFF_TRIGFL BIT(10)
+#define ETBFF_STOPFL BIT(12)
#define etb_writel(t, v, x) \
(__raw_writel((v), (t)->etb_regs + (x)))
#define etb_readl(t, x) (__raw_readl((t)->etb_regs + (x)))
-#define etm_lock(t) do { etm_writel((t), 0, CSMR_LOCKACCESS); } while (0)
-#define etm_unlock(t) \
- do { etm_writel((t), UNLOCK_MAGIC, CSMR_LOCKACCESS); } while (0)
+#define etm_lock(t, id) \
+ do { etm_writel((t), (id), 0, CSMR_LOCKACCESS); } while (0)
+#define etm_unlock(t, id) \
+ do { etm_writel((t), (id), UNLOCK_MAGIC, CSMR_LOCKACCESS); } while (0)
#define etb_lock(t) do { etb_writel((t), 0, CSMR_LOCKACCESS); } while (0)
#define etb_unlock(t) \
diff --git a/arch/arm/include/asm/hardware/gic.h b/arch/arm/include/asm/hardware/gic.h
index 435d3f86c708..67d28c76365a 100644
--- a/arch/arm/include/asm/hardware/gic.h
+++ b/arch/arm/include/asm/hardware/gic.h
@@ -46,6 +46,15 @@ struct gic_chip_data {
unsigned int irq_offset;
void __iomem *dist_base;
void __iomem *cpu_base;
+ u32 saved_spi_enable[DIV_ROUND_UP(1020, 32)];
+ u32 saved_spi_conf[DIV_ROUND_UP(1020, 16)];
+ u32 saved_spi_pri[DIV_ROUND_UP(1020, 4)];
+ u32 saved_spi_target[DIV_ROUND_UP(1020, 4)];
+ u32 __percpu *saved_ppi_enable;
+ u32 __percpu *saved_ppi_conf;
+ u32 __percpu *saved_ppi_pri;
+
+ unsigned int gic_irqs;
};
#endif
diff --git a/arch/arm/include/asm/irq.h b/arch/arm/include/asm/irq.h
index 5a526afb5f18..a5656333d574 100644
--- a/arch/arm/include/asm/irq.h
+++ b/arch/arm/include/asm/irq.h
@@ -26,6 +26,9 @@ extern void asm_do_IRQ(unsigned int, struct pt_regs *);
void handle_IRQ(unsigned int, struct pt_regs *);
void init_IRQ(void);
+void arch_trigger_all_cpu_backtrace(void);
+#define arch_trigger_all_cpu_backtrace arch_trigger_all_cpu_backtrace
+
#endif
#endif
diff --git a/arch/arm/include/asm/mach/mmc.h b/arch/arm/include/asm/mach/mmc.h
new file mode 100644
index 000000000000..bca864ac945f
--- /dev/null
+++ b/arch/arm/include/asm/mach/mmc.h
@@ -0,0 +1,28 @@
+/*
+ * arch/arm/include/asm/mach/mmc.h
+ */
+#ifndef ASMARM_MACH_MMC_H
+#define ASMARM_MACH_MMC_H
+
+#include <linux/mmc/host.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/sdio_func.h>
+
+struct embedded_sdio_data {
+ struct sdio_cis cis;
+ struct sdio_cccr cccr;
+ struct sdio_embedded_func *funcs;
+ int num_funcs;
+};
+
+struct mmc_platform_data {
+ unsigned int ocr_mask; /* available voltages */
+ int built_in; /* built-in device flag */
+ int card_present; /* card detect state */
+ u32 (*translate_vdd)(struct device *, unsigned int);
+ unsigned int (*status)(struct device *);
+ struct embedded_sdio_data *embedded_sdio;
+ int (*register_status_notify)(void (*callback)(int card_present, void *dev_id), void *dev_id);
+};
+
+#endif
diff --git a/arch/arm/include/asm/page.h b/arch/arm/include/asm/page.h
index ac75d0848889..c906a2534c88 100644
--- a/arch/arm/include/asm/page.h
+++ b/arch/arm/include/asm/page.h
@@ -201,6 +201,8 @@ typedef struct page *pgtable_t;
extern int pfn_valid(unsigned long);
#endif
+extern phys_addr_t lowmem_limit;
+
#include <asm/memory.h>
#endif /* !__ASSEMBLY__ */
diff --git a/arch/arm/include/asm/pgalloc.h b/arch/arm/include/asm/pgalloc.h
index 22de005f159c..9a8099ed3ade 100644
--- a/arch/arm/include/asm/pgalloc.h
+++ b/arch/arm/include/asm/pgalloc.h
@@ -64,8 +64,10 @@ pte_alloc_one_kernel(struct mm_struct *mm, unsigned long addr)
pte_t *pte;
pte = (pte_t *)__get_free_page(PGALLOC_GFP);
+#if !defined(CONFIG_CPU_CACHE_V7) || !defined(CONFIG_SMP)
if (pte)
clean_pte_table(pte);
+#endif
return pte;
}
@@ -81,8 +83,10 @@ pte_alloc_one(struct mm_struct *mm, unsigned long addr)
pte = alloc_pages(PGALLOC_GFP, 0);
#endif
if (pte) {
+#if !defined(CONFIG_CPU_CACHE_V7) || !defined(CONFIG_SMP)
if (!PageHighMem(pte))
clean_pte_table(page_address(pte));
+#endif
pgtable_page_ctor(pte);
}
diff --git a/arch/arm/include/asm/pgtable.h b/arch/arm/include/asm/pgtable.h
index 5750704e0271..e6d609c2cb9b 100644
--- a/arch/arm/include/asm/pgtable.h
+++ b/arch/arm/include/asm/pgtable.h
@@ -189,6 +189,7 @@ extern void __pgd_error(const char *file, int line, pgd_t);
#define L_PTE_MT_DEV_WC (_AT(pteval_t, 0x09) << 2) /* 1001 */
#define L_PTE_MT_DEV_CACHED (_AT(pteval_t, 0x0b) << 2) /* 1011 */
#define L_PTE_MT_MASK (_AT(pteval_t, 0x0f) << 2)
+#define L_PTE_MT_INNER_WB (_AT(pteval_t, 0x05) << 2) /* 0101 (armv6, armv7) */
#ifndef __ASSEMBLY__
@@ -244,6 +245,9 @@ extern pgprot_t phys_mem_access_prot(struct file *file, unsigned long pfn,
__pgprot_modify(prot, L_PTE_MT_MASK, L_PTE_MT_UNCACHED | L_PTE_XN)
#endif
+#define pgprot_inner_writeback(prot) \
+ __pgprot_modify(prot, L_PTE_MT_MASK, L_PTE_MT_INNER_WB)
+
#endif /* __ASSEMBLY__ */
/*
@@ -325,6 +329,24 @@ extern pgd_t swapper_pg_dir[PTRS_PER_PGD];
clean_pmd_entry(pmdp); \
} while (0)
+extern spinlock_t pgd_lock;
+extern struct list_head pgd_list;
+
+pte_t *lookup_address(unsigned long address, unsigned int *level);
+enum {
+ PG_LEVEL_NONE,
+ PG_LEVEL_4K,
+ PG_LEVEL_2M,
+ PG_LEVEL_NUM
+};
+
+#ifdef CONFIG_PROC_FS
+extern void update_page_count(int level, unsigned long pages);
+#else
+static inline void update_page_count(int level, unsigned long pages) { }
+#endif
+
+
static inline pte_t *pmd_page_vaddr(pmd_t pmd)
{
return __va(pmd_val(pmd) & PAGE_MASK);
@@ -354,6 +376,9 @@ static inline pte_t *pmd_page_vaddr(pmd_t pmd)
#define pte_pfn(pte) (pte_val(pte) >> PAGE_SHIFT)
#define pfn_pte(pfn,prot) __pte(__pfn_to_phys(pfn) | pgprot_val(prot))
+#define pmd_pfn(pmd) ((pmd_val(pmd) & SECTION_MASK) >> PAGE_SHIFT)
+#define pte_pgprot(pte) ((pgprot_t)(pte_val(pte) & ~PAGE_MASK))
+
#define pte_page(pte) pfn_to_page(pte_pfn(pte))
#define mk_pte(page,prot) pfn_pte(page_to_pfn(page), prot)
diff --git a/arch/arm/include/asm/smp.h b/arch/arm/include/asm/smp.h
index e42d96a45d3e..74f288f4802c 100644
--- a/arch/arm/include/asm/smp.h
+++ b/arch/arm/include/asm/smp.h
@@ -93,4 +93,6 @@ extern void arch_send_call_function_ipi_mask(const struct cpumask *mask);
*/
extern void show_local_irqs(struct seq_file *, int);
+extern void smp_send_all_cpu_backtrace(void);
+
#endif /* ifndef __ASM_ARM_SMP_H */
diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile
index f7887dc53c1f..b6f3c9f80ea7 100644
--- a/arch/arm/kernel/Makefile
+++ b/arch/arm/kernel/Makefile
@@ -65,6 +65,7 @@ obj-$(CONFIG_CPU_PJ4) += pj4-cp0.o
obj-$(CONFIG_IWMMXT) += iwmmxt.o
obj-$(CONFIG_CPU_HAS_PMU) += pmu.o
obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o
+obj-y += cpu_pm.o
AFLAGS_iwmmxt.o := -Wa,-mcpu=iwmmxt
ifneq ($(CONFIG_ARCH_EBSA110),y)
diff --git a/arch/arm/kernel/asm-offsets.c b/arch/arm/kernel/asm-offsets.c
index 16baba2e4369..927522cfc12e 100644
--- a/arch/arm/kernel/asm-offsets.c
+++ b/arch/arm/kernel/asm-offsets.c
@@ -59,9 +59,6 @@ int main(void)
DEFINE(TI_TP_VALUE, offsetof(struct thread_info, tp_value));
DEFINE(TI_FPSTATE, offsetof(struct thread_info, fpstate));
DEFINE(TI_VFPSTATE, offsetof(struct thread_info, vfpstate));
-#ifdef CONFIG_SMP
- DEFINE(VFP_CPU, offsetof(union vfp_state, hard.cpu));
-#endif
#ifdef CONFIG_ARM_THUMBEE
DEFINE(TI_THUMBEE_STATE, offsetof(struct thread_info, thumbee_state));
#endif
diff --git a/arch/arm/kernel/cpu_pm.c b/arch/arm/kernel/cpu_pm.c
new file mode 100644
index 000000000000..748af1f1f437
--- /dev/null
+++ b/arch/arm/kernel/cpu_pm.c
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2011 Google, Inc.
+ *
+ * Author:
+ * Colin Cross <ccross@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/kernel.h>
+#include <linux/module.h>
+#include <linux/notifier.h>
+#include <linux/spinlock.h>
+
+#include <asm/cpu_pm.h>
+
+static DEFINE_RWLOCK(cpu_pm_notifier_lock);
+static RAW_NOTIFIER_HEAD(cpu_pm_notifier_chain);
+
+int cpu_pm_register_notifier(struct notifier_block *nb)
+{
+ unsigned long flags;
+ int ret;
+
+ write_lock_irqsave(&cpu_pm_notifier_lock, flags);
+ ret = raw_notifier_chain_register(&cpu_pm_notifier_chain, nb);
+ write_unlock_irqrestore(&cpu_pm_notifier_lock, flags);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(cpu_pm_register_notifier);
+
+int cpu_pm_unregister_notifier(struct notifier_block *nb)
+{
+ unsigned long flags;
+ int ret;
+
+ write_lock_irqsave(&cpu_pm_notifier_lock, flags);
+ ret = raw_notifier_chain_unregister(&cpu_pm_notifier_chain, nb);
+ write_unlock_irqrestore(&cpu_pm_notifier_lock, flags);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(cpu_pm_unregister_notifier);
+
+static int cpu_pm_notify(enum cpu_pm_event event, int nr_to_call, int *nr_calls)
+{
+ int ret;
+
+ ret = __raw_notifier_call_chain(&cpu_pm_notifier_chain, event, NULL,
+ nr_to_call, nr_calls);
+
+ return notifier_to_errno(ret);
+}
+
+int cpu_pm_enter(void)
+{
+ int nr_calls;
+ int ret = 0;
+
+ read_lock(&cpu_pm_notifier_lock);
+ ret = cpu_pm_notify(CPU_PM_ENTER, -1, &nr_calls);
+ if (ret)
+ cpu_pm_notify(CPU_PM_ENTER_FAILED, nr_calls - 1, NULL);
+ read_unlock(&cpu_pm_notifier_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(cpu_pm_enter);
+
+int cpu_pm_exit(void)
+{
+ int ret;
+
+ read_lock(&cpu_pm_notifier_lock);
+ ret = cpu_pm_notify(CPU_PM_EXIT, -1, NULL);
+ read_unlock(&cpu_pm_notifier_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(cpu_pm_exit);
+
+int cpu_complex_pm_enter(void)
+{
+ int nr_calls;
+ int ret = 0;
+
+ read_lock(&cpu_pm_notifier_lock);
+ ret = cpu_pm_notify(CPU_COMPLEX_PM_ENTER, -1, &nr_calls);
+ if (ret)
+ cpu_pm_notify(CPU_COMPLEX_PM_ENTER_FAILED, nr_calls - 1, NULL);
+ read_unlock(&cpu_pm_notifier_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(cpu_complex_pm_enter);
+
+int cpu_complex_pm_exit(void)
+{
+ int ret;
+
+ read_lock(&cpu_pm_notifier_lock);
+ ret = cpu_pm_notify(CPU_COMPLEX_PM_EXIT, -1, NULL);
+ read_unlock(&cpu_pm_notifier_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(cpu_complex_pm_exit);
diff --git a/arch/arm/kernel/debug.S b/arch/arm/kernel/debug.S
index bcd66e00bdbe..9126592867f4 100644
--- a/arch/arm/kernel/debug.S
+++ b/arch/arm/kernel/debug.S
@@ -154,6 +154,11 @@ ENDPROC(printhex2)
.ltorg
ENTRY(printascii)
+#if defined(CONFIG_DEBUG_ICEDCC) && defined(CONFIG_SMP)
+ mrc p15, 0, r3, c0, c0, 5
+ ands r3, r3, #3
+ movne pc, lr
+#endif
addruart_current r3, r1, r2
b 2f
1: waituart r2, r3
@@ -170,6 +175,11 @@ ENTRY(printascii)
ENDPROC(printascii)
ENTRY(printch)
+#if defined(CONFIG_DEBUG_ICEDCC) && defined(CONFIG_SMP)
+ mrc p15, 0, r3, c0, c0, 5
+ ands r3, r3, #3
+ movne pc, lr
+#endif
addruart_current r3, r1, r2
mov r1, r0
mov r0, #0
diff --git a/arch/arm/kernel/elf.c b/arch/arm/kernel/elf.c
index 9b05c6a0dcea..d4a0da1e48f4 100644
--- a/arch/arm/kernel/elf.c
+++ b/arch/arm/kernel/elf.c
@@ -40,22 +40,15 @@ EXPORT_SYMBOL(elf_check_arch);
void elf_set_personality(const struct elf32_hdr *x)
{
unsigned int eflags = x->e_flags;
- unsigned int personality = current->personality & ~PER_MASK;
-
- /*
- * We only support Linux ELF executables, so always set the
- * personality to LINUX.
- */
- personality |= PER_LINUX;
+ unsigned int personality = PER_LINUX_32BIT;
/*
* APCS-26 is only valid for OABI executables
*/
- if ((eflags & EF_ARM_EABI_MASK) == EF_ARM_EABI_UNKNOWN &&
- (eflags & EF_ARM_APCS_26))
- personality &= ~ADDR_LIMIT_32BIT;
- else
- personality |= ADDR_LIMIT_32BIT;
+ if ((eflags & EF_ARM_EABI_MASK) == EF_ARM_EABI_UNKNOWN) {
+ if (eflags & EF_ARM_APCS_26)
+ personality = PER_LINUX;
+ }
set_personality(personality);
diff --git a/arch/arm/kernel/entry-armv.S b/arch/arm/kernel/entry-armv.S
index a87cbf889ff4..4f8e30f183b9 100644
--- a/arch/arm/kernel/entry-armv.S
+++ b/arch/arm/kernel/entry-armv.S
@@ -450,7 +450,7 @@ __und_usr:
blo __und_usr_unknown
3: ldrht r0, [r4]
add r2, r2, #2 @ r2 is PC + 2, make it PC + 4
- orr r0, r0, r5, lsl #16
+ orr r0, r0, r5, lsl #16
#else
b __und_usr_unknown
#endif
diff --git a/arch/arm/kernel/etm.c b/arch/arm/kernel/etm.c
index 1bec8b5f22f0..496b8b84e455 100644
--- a/arch/arm/kernel/etm.c
+++ b/arch/arm/kernel/etm.c
@@ -15,6 +15,7 @@
#include <linux/init.h>
#include <linux/types.h>
#include <linux/io.h>
+#include <linux/slab.h>
#include <linux/sysrq.h>
#include <linux/device.h>
#include <linux/clk.h>
@@ -36,26 +37,36 @@ MODULE_AUTHOR("Alexander Shishkin");
struct tracectx {
unsigned int etb_bufsz;
void __iomem *etb_regs;
- void __iomem *etm_regs;
+ void __iomem **etm_regs;
+ int etm_regs_count;
unsigned long flags;
int ncmppairs;
int etm_portsz;
+ u32 etb_fc;
+ unsigned long range_start;
+ unsigned long range_end;
+ unsigned long data_range_start;
+ unsigned long data_range_end;
+ bool dump_initial_etb;
struct device *dev;
struct clk *emu_clk;
struct mutex mutex;
};
-static struct tracectx tracer;
+static struct tracectx tracer = {
+ .range_start = (unsigned long)_stext,
+ .range_end = (unsigned long)_etext,
+};
static inline bool trace_isrunning(struct tracectx *t)
{
return !!(t->flags & TRACER_RUNNING);
}
-static int etm_setup_address_range(struct tracectx *t, int n,
+static int etm_setup_address_range(struct tracectx *t, int id, int n,
unsigned long start, unsigned long end, int exclude, int data)
{
- u32 flags = ETMAAT_ARM | ETMAAT_IGNCONTEXTID | ETMAAT_NSONLY | \
+ u32 flags = ETMAAT_ARM | ETMAAT_IGNCONTEXTID | ETMAAT_IGNSECURITY |
ETMAAT_NOVALCMP;
if (n < 1 || n > t->ncmppairs)
@@ -71,95 +82,155 @@ static int etm_setup_address_range(struct tracectx *t, int n,
flags |= ETMAAT_IEXEC;
/* first comparator for the range */
- etm_writel(t, flags, ETMR_COMP_ACC_TYPE(n * 2));
- etm_writel(t, start, ETMR_COMP_VAL(n * 2));
+ etm_writel(t, id, flags, ETMR_COMP_ACC_TYPE(n * 2));
+ etm_writel(t, id, start, ETMR_COMP_VAL(n * 2));
/* second comparator is right next to it */
- etm_writel(t, flags, ETMR_COMP_ACC_TYPE(n * 2 + 1));
- etm_writel(t, end, ETMR_COMP_VAL(n * 2 + 1));
-
- flags = exclude ? ETMTE_INCLEXCL : 0;
- etm_writel(t, flags | (1 << n), ETMR_TRACEENCTRL);
+ etm_writel(t, id, flags, ETMR_COMP_ACC_TYPE(n * 2 + 1));
+ etm_writel(t, id, end, ETMR_COMP_VAL(n * 2 + 1));
+
+ if (data) {
+ flags = exclude ? ETMVDC3_EXCLONLY : 0;
+ if (exclude)
+ n += 8;
+ etm_writel(t, id, flags | BIT(n), ETMR_VIEWDATACTRL3);
+ } else {
+ flags = exclude ? ETMTE_INCLEXCL : 0;
+ etm_writel(t, id, flags | (1 << n), ETMR_TRACEENCTRL);
+ }
return 0;
}
-static int trace_start(struct tracectx *t)
+static int trace_start_etm(struct tracectx *t, int id)
{
u32 v;
unsigned long timeout = TRACER_TIMEOUT;
- etb_unlock(t);
-
- etb_writel(t, 0, ETBR_FORMATTERCTRL);
- etb_writel(t, 1, ETBR_CTRL);
-
- etb_lock(t);
-
- /* configure etm */
v = ETMCTRL_OPTS | ETMCTRL_PROGRAM | ETMCTRL_PORTSIZE(t->etm_portsz);
if (t->flags & TRACER_CYCLE_ACC)
v |= ETMCTRL_CYCLEACCURATE;
- etm_unlock(t);
+ if (t->flags & TRACER_TRACE_DATA)
+ v |= ETMCTRL_DATA_DO_ADDR;
+
+ etm_unlock(t, id);
- etm_writel(t, v, ETMR_CTRL);
+ etm_writel(t, id, v, ETMR_CTRL);
- while (!(etm_readl(t, ETMR_CTRL) & ETMCTRL_PROGRAM) && --timeout)
+ while (!(etm_readl(t, id, ETMR_CTRL) & ETMCTRL_PROGRAM) && --timeout)
;
if (!timeout) {
dev_dbg(t->dev, "Waiting for progbit to assert timed out\n");
- etm_lock(t);
+ etm_lock(t, id);
return -EFAULT;
}
- etm_setup_address_range(t, 1, (unsigned long)_stext,
- (unsigned long)_etext, 0, 0);
- etm_writel(t, 0, ETMR_TRACEENCTRL2);
- etm_writel(t, 0, ETMR_TRACESSCTRL);
- etm_writel(t, 0x6f, ETMR_TRACEENEVT);
+ if (t->range_start || t->range_end)
+ etm_setup_address_range(t, id, 1,
+ t->range_start, t->range_end, 0, 0);
+ else
+ etm_writel(t, id, ETMTE_INCLEXCL, ETMR_TRACEENCTRL);
+
+ etm_writel(t, id, 0, ETMR_TRACEENCTRL2);
+ etm_writel(t, id, 0, ETMR_TRACESSCTRL);
+ etm_writel(t, id, 0x6f, ETMR_TRACEENEVT);
+
+ etm_writel(t, id, 0, ETMR_VIEWDATACTRL1);
+ etm_writel(t, id, 0, ETMR_VIEWDATACTRL2);
+
+ if (t->data_range_start || t->data_range_end)
+ etm_setup_address_range(t, id, 2, t->data_range_start,
+ t->data_range_end, 0, 1);
+ else
+ etm_writel(t, id, ETMVDC3_EXCLONLY, ETMR_VIEWDATACTRL3);
+
+ etm_writel(t, id, 0x6f, ETMR_VIEWDATAEVT);
v &= ~ETMCTRL_PROGRAM;
v |= ETMCTRL_PORTSEL;
- etm_writel(t, v, ETMR_CTRL);
+ etm_writel(t, id, v, ETMR_CTRL);
timeout = TRACER_TIMEOUT;
- while (etm_readl(t, ETMR_CTRL) & ETMCTRL_PROGRAM && --timeout)
+ while (etm_readl(t, id, ETMR_CTRL) & ETMCTRL_PROGRAM && --timeout)
;
if (!timeout) {
dev_dbg(t->dev, "Waiting for progbit to deassert timed out\n");
- etm_lock(t);
+ etm_lock(t, id);
return -EFAULT;
}
- etm_lock(t);
+ etm_lock(t, id);
+ return 0;
+}
+
+static int trace_start(struct tracectx *t)
+{
+ int ret;
+ int id;
+ u32 etb_fc = t->etb_fc;
+
+ etb_unlock(t);
+
+ t->dump_initial_etb = false;
+ etb_writel(t, 0, ETBR_WRITEADDR);
+ etb_writel(t, etb_fc, ETBR_FORMATTERCTRL);
+ etb_writel(t, 1, ETBR_CTRL);
+
+ etb_lock(t);
+
+ /* configure etm(s) */
+ for (id = 0; id < t->etm_regs_count; id++) {
+ ret = trace_start_etm(t, id);
+ if (ret)
+ return ret;
+ }
t->flags |= TRACER_RUNNING;
return 0;
}
-static int trace_stop(struct tracectx *t)
+static int trace_stop_etm(struct tracectx *t, int id)
{
unsigned long timeout = TRACER_TIMEOUT;
- etm_unlock(t);
+ etm_unlock(t, id);
- etm_writel(t, 0x440, ETMR_CTRL);
- while (!(etm_readl(t, ETMR_CTRL) & ETMCTRL_PROGRAM) && --timeout)
+ etm_writel(t, id, 0x441, ETMR_CTRL);
+ while (!(etm_readl(t, id, ETMR_CTRL) & ETMCTRL_PROGRAM) && --timeout)
;
if (!timeout) {
dev_dbg(t->dev, "Waiting for progbit to assert timed out\n");
- etm_lock(t);
+ etm_lock(t, id);
return -EFAULT;
}
- etm_lock(t);
+ etm_lock(t, id);
+ return 0;
+}
+
+static int trace_stop(struct tracectx *t)
+{
+ int id;
+ int ret;
+ unsigned long timeout = TRACER_TIMEOUT;
+ u32 etb_fc = t->etb_fc;
+
+ for (id = 0; id < t->etm_regs_count; id++) {
+ ret = trace_stop_etm(t, id);
+ if (ret)
+ return ret;
+ }
etb_unlock(t);
- etb_writel(t, ETBFF_MANUAL_FLUSH, ETBR_FORMATTERCTRL);
+ if (etb_fc) {
+ etb_fc |= ETBFF_STOPFL;
+ etb_writel(t, t->etb_fc, ETBR_FORMATTERCTRL);
+ }
+ etb_writel(t, etb_fc | ETBFF_MANUAL_FLUSH, ETBR_FORMATTERCTRL);
timeout = TRACER_TIMEOUT;
while (etb_readl(t, ETBR_FORMATTERCTRL) &
@@ -184,24 +255,15 @@ static int trace_stop(struct tracectx *t)
static int etb_getdatalen(struct tracectx *t)
{
u32 v;
- int rp, wp;
+ int wp;
v = etb_readl(t, ETBR_STATUS);
if (v & 1)
return t->etb_bufsz;
- rp = etb_readl(t, ETBR_READADDR);
wp = etb_readl(t, ETBR_WRITEADDR);
-
- if (rp > wp) {
- etb_writel(t, 0, ETBR_READADDR);
- etb_writel(t, 0, ETBR_WRITEADDR);
-
- return 0;
- }
-
- return wp - rp;
+ return wp;
}
/* sysrq+v will always stop the running trace and leave it at that */
@@ -234,21 +296,18 @@ static void etm_dump(void)
printk("%08x", cpu_to_be32(etb_readl(t, ETBR_READMEM)));
printk(KERN_INFO "\n--- ETB buffer end ---\n");
- /* deassert the overflow bit */
- etb_writel(t, 1, ETBR_CTRL);
- etb_writel(t, 0, ETBR_CTRL);
-
- etb_writel(t, 0, ETBR_TRIGGERCOUNT);
- etb_writel(t, 0, ETBR_READADDR);
- etb_writel(t, 0, ETBR_WRITEADDR);
-
etb_lock(t);
}
static void sysrq_etm_dump(int key)
{
+ if (!mutex_trylock(&tracer.mutex)) {
+ printk(KERN_INFO "Tracing hardware busy\n");
+ return;
+ }
dev_dbg(tracer.dev, "Dumping ETB buffer\n");
etm_dump();
+ mutex_unlock(&tracer.mutex);
}
static struct sysrq_key_op sysrq_etm_op = {
@@ -275,6 +334,10 @@ static ssize_t etb_read(struct file *file, char __user *data,
struct tracectx *t = file->private_data;
u32 first = 0;
u32 *buf;
+ int wpos;
+ int skip;
+ long wlength;
+ loff_t pos = *ppos;
mutex_lock(&t->mutex);
@@ -286,31 +349,39 @@ static ssize_t etb_read(struct file *file, char __user *data,
etb_unlock(t);
total = etb_getdatalen(t);
+ if (total == 0 && t->dump_initial_etb)
+ total = t->etb_bufsz;
if (total == t->etb_bufsz)
first = etb_readl(t, ETBR_WRITEADDR);
+ if (pos > total * 4) {
+ skip = 0;
+ wpos = total;
+ } else {
+ skip = (int)pos % 4;
+ wpos = (int)pos / 4;
+ }
+ total -= wpos;
+ first = (first + wpos) % t->etb_bufsz;
+
etb_writel(t, first, ETBR_READADDR);
- length = min(total * 4, (int)len);
- buf = vmalloc(length);
+ wlength = min(total, DIV_ROUND_UP(skip + (int)len, 4));
+ length = min(total * 4 - skip, (int)len);
+ buf = vmalloc(wlength * 4);
- dev_dbg(t->dev, "ETB buffer length: %d\n", total);
+ dev_dbg(t->dev, "ETB read %ld bytes to %lld from %ld words at %d\n",
+ length, pos, wlength, first);
+ dev_dbg(t->dev, "ETB buffer length: %d\n", total + wpos);
dev_dbg(t->dev, "ETB status reg: %x\n", etb_readl(t, ETBR_STATUS));
- for (i = 0; i < length / 4; i++)
+ for (i = 0; i < wlength; i++)
buf[i] = etb_readl(t, ETBR_READMEM);
- /* the only way to deassert overflow bit in ETB status is this */
- etb_writel(t, 1, ETBR_CTRL);
- etb_writel(t, 0, ETBR_CTRL);
-
- etb_writel(t, 0, ETBR_WRITEADDR);
- etb_writel(t, 0, ETBR_READADDR);
- etb_writel(t, 0, ETBR_TRIGGERCOUNT);
-
etb_lock(t);
- length -= copy_to_user(data, buf, length);
+ length -= copy_to_user(data, (u8 *)buf + skip, length);
vfree(buf);
+ *ppos = pos + length;
out:
mutex_unlock(&t->mutex);
@@ -347,28 +418,17 @@ static int __devinit etb_probe(struct amba_device *dev, const struct amba_id *id
if (ret)
goto out;
+ mutex_lock(&t->mutex);
t->etb_regs = ioremap_nocache(dev->res.start, resource_size(&dev->res));
if (!t->etb_regs) {
ret = -ENOMEM;
goto out_release;
}
+ t->dev = &dev->dev;
+ t->dump_initial_etb = true;
amba_set_drvdata(dev, t);
- etb_miscdev.parent = &dev->dev;
-
- ret = misc_register(&etb_miscdev);
- if (ret)
- goto out_unmap;
-
- t->emu_clk = clk_get(&dev->dev, "emu_src_ck");
- if (IS_ERR(t->emu_clk)) {
- dev_dbg(&dev->dev, "Failed to obtain emu_src_ck.\n");
- return -EFAULT;
- }
-
- clk_enable(t->emu_clk);
-
etb_unlock(t);
t->etb_bufsz = etb_readl(t, ETBR_DEPTH);
dev_dbg(&dev->dev, "Size: %x\n", t->etb_bufsz);
@@ -377,6 +437,20 @@ static int __devinit etb_probe(struct amba_device *dev, const struct amba_id *id
etb_writel(t, 0, ETBR_CTRL);
etb_writel(t, 0x1000, ETBR_FORMATTERCTRL);
etb_lock(t);
+ mutex_unlock(&t->mutex);
+
+ etb_miscdev.parent = &dev->dev;
+
+ ret = misc_register(&etb_miscdev);
+ if (ret)
+ goto out_unmap;
+
+ /* Get optional clock. Currently used to select clock source on omap3 */
+ t->emu_clk = clk_get(&dev->dev, "emu_src_ck");
+ if (IS_ERR(t->emu_clk))
+ dev_dbg(&dev->dev, "Failed to obtain emu_src_ck.\n");
+ else
+ clk_enable(t->emu_clk);
dev_dbg(&dev->dev, "ETB AMBA driver initialized.\n");
@@ -384,10 +458,13 @@ out:
return ret;
out_unmap:
+ mutex_lock(&t->mutex);
amba_set_drvdata(dev, NULL);
iounmap(t->etb_regs);
+ t->etb_regs = NULL;
out_release:
+ mutex_unlock(&t->mutex);
amba_release_regions(dev);
return ret;
@@ -402,8 +479,10 @@ static int etb_remove(struct amba_device *dev)
iounmap(t->etb_regs);
t->etb_regs = NULL;
- clk_disable(t->emu_clk);
- clk_put(t->emu_clk);
+ if (!IS_ERR(t->emu_clk)) {
+ clk_disable(t->emu_clk);
+ clk_put(t->emu_clk);
+ }
amba_release_regions(dev);
@@ -447,7 +526,10 @@ static ssize_t trace_running_store(struct kobject *kobj,
return -EINVAL;
mutex_lock(&tracer.mutex);
- ret = value ? trace_start(&tracer) : trace_stop(&tracer);
+ if (!tracer.etb_regs)
+ ret = -ENODEV;
+ else
+ ret = value ? trace_start(&tracer) : trace_stop(&tracer);
mutex_unlock(&tracer.mutex);
return ret ? : n;
@@ -462,36 +544,50 @@ static ssize_t trace_info_show(struct kobject *kobj,
{
u32 etb_wa, etb_ra, etb_st, etb_fc, etm_ctrl, etm_st;
int datalen;
+ int id;
+ int ret;
- etb_unlock(&tracer);
- datalen = etb_getdatalen(&tracer);
- etb_wa = etb_readl(&tracer, ETBR_WRITEADDR);
- etb_ra = etb_readl(&tracer, ETBR_READADDR);
- etb_st = etb_readl(&tracer, ETBR_STATUS);
- etb_fc = etb_readl(&tracer, ETBR_FORMATTERCTRL);
- etb_lock(&tracer);
-
- etm_unlock(&tracer);
- etm_ctrl = etm_readl(&tracer, ETMR_CTRL);
- etm_st = etm_readl(&tracer, ETMR_STATUS);
- etm_lock(&tracer);
+ mutex_lock(&tracer.mutex);
+ if (tracer.etb_regs) {
+ etb_unlock(&tracer);
+ datalen = etb_getdatalen(&tracer);
+ etb_wa = etb_readl(&tracer, ETBR_WRITEADDR);
+ etb_ra = etb_readl(&tracer, ETBR_READADDR);
+ etb_st = etb_readl(&tracer, ETBR_STATUS);
+ etb_fc = etb_readl(&tracer, ETBR_FORMATTERCTRL);
+ etb_lock(&tracer);
+ } else {
+ etb_wa = etb_ra = etb_st = etb_fc = ~0;
+ datalen = -1;
+ }
- return sprintf(buf, "Trace buffer len: %d\nComparator pairs: %d\n"
+ ret = sprintf(buf, "Trace buffer len: %d\nComparator pairs: %d\n"
"ETBR_WRITEADDR:\t%08x\n"
"ETBR_READADDR:\t%08x\n"
"ETBR_STATUS:\t%08x\n"
- "ETBR_FORMATTERCTRL:\t%08x\n"
- "ETMR_CTRL:\t%08x\n"
- "ETMR_STATUS:\t%08x\n",
+ "ETBR_FORMATTERCTRL:\t%08x\n",
datalen,
tracer.ncmppairs,
etb_wa,
etb_ra,
etb_st,
- etb_fc,
+ etb_fc
+ );
+
+ for (id = 0; id < tracer.etm_regs_count; id++) {
+ etm_unlock(&tracer, id);
+ etm_ctrl = etm_readl(&tracer, id, ETMR_CTRL);
+ etm_st = etm_readl(&tracer, id, ETMR_STATUS);
+ etm_lock(&tracer, id);
+ ret += sprintf(buf + ret, "ETMR_CTRL:\t%08x\n"
+ "ETMR_STATUS:\t%08x\n",
etm_ctrl,
etm_st
);
+ }
+ mutex_unlock(&tracer.mutex);
+
+ return ret;
}
static struct kobj_attribute trace_info_attr =
@@ -530,42 +626,121 @@ static ssize_t trace_mode_store(struct kobject *kobj,
static struct kobj_attribute trace_mode_attr =
__ATTR(trace_mode, 0644, trace_mode_show, trace_mode_store);
+static ssize_t trace_range_show(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "%08lx %08lx\n",
+ tracer.range_start, tracer.range_end);
+}
+
+static ssize_t trace_range_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t n)
+{
+ unsigned long range_start, range_end;
+
+ if (sscanf(buf, "%lx %lx", &range_start, &range_end) != 2)
+ return -EINVAL;
+
+ mutex_lock(&tracer.mutex);
+ tracer.range_start = range_start;
+ tracer.range_end = range_end;
+ mutex_unlock(&tracer.mutex);
+
+ return n;
+}
+
+
+static struct kobj_attribute trace_range_attr =
+ __ATTR(trace_range, 0644, trace_range_show, trace_range_store);
+
+static ssize_t trace_data_range_show(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ char *buf)
+{
+ unsigned long range_start;
+ u64 range_end;
+ mutex_lock(&tracer.mutex);
+ range_start = tracer.data_range_start;
+ range_end = tracer.data_range_end;
+ if (!range_end && (tracer.flags & TRACER_TRACE_DATA))
+ range_end = 0x100000000ULL;
+ mutex_unlock(&tracer.mutex);
+ return sprintf(buf, "%08lx %08llx\n", range_start, range_end);
+}
+
+static ssize_t trace_data_range_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t n)
+{
+ unsigned long range_start;
+ u64 range_end;
+
+ if (sscanf(buf, "%lx %llx", &range_start, &range_end) != 2)
+ return -EINVAL;
+
+ mutex_lock(&tracer.mutex);
+ tracer.data_range_start = range_start;
+ tracer.data_range_end = (unsigned long)range_end;
+ if (range_end)
+ tracer.flags |= TRACER_TRACE_DATA;
+ else
+ tracer.flags &= ~TRACER_TRACE_DATA;
+ mutex_unlock(&tracer.mutex);
+
+ return n;
+}
+
+
+static struct kobj_attribute trace_data_range_attr =
+ __ATTR(trace_data_range, 0644,
+ trace_data_range_show, trace_data_range_store);
+
static int __devinit etm_probe(struct amba_device *dev, const struct amba_id *id)
{
struct tracectx *t = &tracer;
int ret = 0;
+ void __iomem **new_regs;
+ int new_count;
- if (t->etm_regs) {
- dev_dbg(&dev->dev, "ETM already initialized\n");
- ret = -EBUSY;
+ mutex_lock(&t->mutex);
+ new_count = t->etm_regs_count + 1;
+ new_regs = krealloc(t->etm_regs,
+ sizeof(t->etm_regs[0]) * new_count, GFP_KERNEL);
+
+ if (!new_regs) {
+ dev_dbg(&dev->dev, "Failed to allocate ETM register array\n");
+ ret = -ENOMEM;
goto out;
}
+ t->etm_regs = new_regs;
ret = amba_request_regions(dev, NULL);
if (ret)
goto out;
- t->etm_regs = ioremap_nocache(dev->res.start, resource_size(&dev->res));
- if (!t->etm_regs) {
+ t->etm_regs[t->etm_regs_count] =
+ ioremap_nocache(dev->res.start, resource_size(&dev->res));
+ if (!t->etm_regs[t->etm_regs_count]) {
ret = -ENOMEM;
goto out_release;
}
- amba_set_drvdata(dev, t);
+ amba_set_drvdata(dev, t->etm_regs[t->etm_regs_count]);
- mutex_init(&t->mutex);
- t->dev = &dev->dev;
- t->flags = TRACER_CYCLE_ACC;
+ t->flags = TRACER_CYCLE_ACC | TRACER_TRACE_DATA;
t->etm_portsz = 1;
- etm_unlock(t);
- (void)etm_readl(t, ETMMR_PDSR);
+ etm_unlock(t, t->etm_regs_count);
+ (void)etm_readl(t, t->etm_regs_count, ETMMR_PDSR);
/* dummy first read */
- (void)etm_readl(&tracer, ETMMR_OSSRR);
+ (void)etm_readl(&tracer, t->etm_regs_count, ETMMR_OSSRR);
- t->ncmppairs = etm_readl(t, ETMR_CONFCODE) & 0xf;
- etm_writel(t, 0x440, ETMR_CTRL);
- etm_lock(t);
+ t->ncmppairs = etm_readl(t, t->etm_regs_count, ETMR_CONFCODE) & 0xf;
+ etm_writel(t, t->etm_regs_count, 0x441, ETMR_CTRL);
+ etm_writel(t, t->etm_regs_count, new_count, ETMR_TRACEIDR);
+ etm_lock(t, t->etm_regs_count);
ret = sysfs_create_file(&dev->dev.kobj,
&trace_running_attr.attr);
@@ -581,36 +756,68 @@ static int __devinit etm_probe(struct amba_device *dev, const struct amba_id *id
if (ret)
dev_dbg(&dev->dev, "Failed to create trace_mode in sysfs\n");
- dev_dbg(t->dev, "ETM AMBA driver initialized.\n");
+ ret = sysfs_create_file(&dev->dev.kobj, &trace_range_attr.attr);
+ if (ret)
+ dev_dbg(&dev->dev, "Failed to create trace_range in sysfs\n");
+
+ ret = sysfs_create_file(&dev->dev.kobj, &trace_data_range_attr.attr);
+ if (ret)
+ dev_dbg(&dev->dev,
+ "Failed to create trace_data_range in sysfs\n");
+
+ dev_dbg(&dev->dev, "ETM AMBA driver initialized.\n");
+
+ /* Enable formatter if there are multiple trace sources */
+ if (new_count > 1)
+ t->etb_fc = ETBFF_ENFCONT | ETBFF_ENFTC;
+
+ t->etm_regs_count = new_count;
out:
+ mutex_unlock(&t->mutex);
return ret;
out_unmap:
amba_set_drvdata(dev, NULL);
- iounmap(t->etm_regs);
+ iounmap(t->etm_regs[t->etm_regs_count]);
out_release:
amba_release_regions(dev);
+ mutex_unlock(&t->mutex);
return ret;
}
static int etm_remove(struct amba_device *dev)
{
- struct tracectx *t = amba_get_drvdata(dev);
+ int i;
+ struct tracectx *t = &tracer;
+ void __iomem *etm_regs = amba_get_drvdata(dev);
+
+ sysfs_remove_file(&dev->dev.kobj, &trace_running_attr.attr);
+ sysfs_remove_file(&dev->dev.kobj, &trace_info_attr.attr);
+ sysfs_remove_file(&dev->dev.kobj, &trace_mode_attr.attr);
+ sysfs_remove_file(&dev->dev.kobj, &trace_range_attr.attr);
+ sysfs_remove_file(&dev->dev.kobj, &trace_data_range_attr.attr);
amba_set_drvdata(dev, NULL);
- iounmap(t->etm_regs);
- t->etm_regs = NULL;
+ mutex_lock(&t->mutex);
+ for (i = 0; i < t->etm_regs_count; i++)
+ if (t->etm_regs[i] == etm_regs)
+ break;
+ for (; i < t->etm_regs_count - 1; i++)
+ t->etm_regs[i] = t->etm_regs[i + 1];
+ t->etm_regs_count--;
+ if (!t->etm_regs_count) {
+ kfree(t->etm_regs);
+ t->etm_regs = NULL;
+ }
+ mutex_unlock(&t->mutex);
+ iounmap(etm_regs);
amba_release_regions(dev);
- sysfs_remove_file(&dev->dev.kobj, &trace_running_attr.attr);
- sysfs_remove_file(&dev->dev.kobj, &trace_info_attr.attr);
- sysfs_remove_file(&dev->dev.kobj, &trace_mode_attr.attr);
-
return 0;
}
@@ -619,6 +826,10 @@ static struct amba_id etm_ids[] = {
.id = 0x0003b921,
.mask = 0x0007ffff,
},
+ {
+ .id = 0x0003b950,
+ .mask = 0x0007ffff,
+ },
{ 0, 0 },
};
@@ -636,6 +847,8 @@ static int __init etm_init(void)
{
int retval;
+ mutex_init(&tracer.mutex);
+
retval = amba_driver_register(&etb_driver);
if (retval) {
printk(KERN_ERR "Failed to register etb\n");
diff --git a/arch/arm/kernel/leds.c b/arch/arm/kernel/leds.c
index 0f107dcb0347..136e8376a3eb 100644
--- a/arch/arm/kernel/leds.c
+++ b/arch/arm/kernel/leds.c
@@ -9,6 +9,8 @@
*/
#include <linux/module.h>
#include <linux/init.h>
+#include <linux/notifier.h>
+#include <linux/cpu.h>
#include <linux/sysdev.h>
#include <linux/syscore_ops.h>
@@ -101,6 +103,25 @@ static struct syscore_ops leds_syscore_ops = {
.resume = leds_resume,
};
+static int leds_idle_notifier(struct notifier_block *nb, unsigned long val,
+ void *data)
+{
+ switch (val) {
+ case IDLE_START:
+ leds_event(led_idle_start);
+ break;
+ case IDLE_END:
+ leds_event(led_idle_end);
+ break;
+ }
+
+ return 0;
+}
+
+static struct notifier_block leds_idle_nb = {
+ .notifier_call = leds_idle_notifier,
+};
+
static int __init leds_init(void)
{
int ret;
@@ -109,8 +130,12 @@ static int __init leds_init(void)
ret = sysdev_register(&leds_device);
if (ret == 0)
ret = sysdev_create_file(&leds_device, &attr_event);
- if (ret == 0)
+
+ if (ret == 0) {
register_syscore_ops(&leds_syscore_ops);
+ idle_notifier_register(&leds_idle_nb);
+ }
+
return ret;
}
diff --git a/arch/arm/kernel/module.c b/arch/arm/kernel/module.c
index cc2020c2c709..09326b62780d 100644
--- a/arch/arm/kernel/module.c
+++ b/arch/arm/kernel/module.c
@@ -89,6 +89,7 @@ apply_relocate(Elf32_Shdr *sechdrs, const char *strtab, unsigned int symindex,
break;
case R_ARM_ABS32:
+ case R_ARM_TARGET1:
*(u32 *)loc += sym->st_value;
break;
diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c
index c9d11eaf4384..d33f09378458 100644
--- a/arch/arm/kernel/process.c
+++ b/arch/arm/kernel/process.c
@@ -31,9 +31,9 @@
#include <linux/random.h>
#include <linux/hw_breakpoint.h>
#include <linux/cpuidle.h>
+#include <linux/console.h>
#include <asm/cacheflush.h>
-#include <asm/leds.h>
#include <asm/processor.h>
#include <asm/system.h>
#include <asm/thread_notify.h>
@@ -63,6 +63,18 @@ static volatile int hlt_counter;
#include <mach/system.h>
+#ifdef CONFIG_SMP
+void arch_trigger_all_cpu_backtrace(void)
+{
+ smp_send_all_cpu_backtrace();
+}
+#else
+void arch_trigger_all_cpu_backtrace(void)
+{
+ dump_stack();
+}
+#endif
+
void disable_hlt(void)
{
hlt_counter++;
@@ -92,8 +104,37 @@ static int __init hlt_setup(char *__unused)
__setup("nohlt", nohlt_setup);
__setup("hlt", hlt_setup);
+#ifdef CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART
+void arm_machine_flush_console(void)
+{
+ printk("\n");
+ pr_emerg("Restarting %s\n", linux_banner);
+ if (console_trylock()) {
+ console_unlock();
+ return;
+ }
+
+ mdelay(50);
+
+ local_irq_disable();
+ if (!console_trylock())
+ pr_emerg("arm_restart: Console was locked! Busting\n");
+ else
+ pr_emerg("arm_restart: Console was locked!\n");
+ console_unlock();
+}
+#else
+void arm_machine_flush_console(void)
+{
+}
+#endif
+
void arm_machine_restart(char mode, const char *cmd)
{
+ /* Flush the console to make sure all the relevant messages make it
+ * out to the console drivers */
+ arm_machine_flush_console();
+
/* Disable interrupts first */
local_irq_disable();
local_fiq_disable();
@@ -183,8 +224,8 @@ void cpu_idle(void)
/* endless idle loop with no priority at all */
while (1) {
+ idle_notifier_call_chain(IDLE_START);
tick_nohz_stop_sched_tick(1);
- leds_event(led_idle_start);
while (!need_resched()) {
#ifdef CONFIG_HOTPLUG_CPU
if (cpu_is_offline(smp_processor_id()))
@@ -212,8 +253,8 @@ void cpu_idle(void)
local_irq_enable();
}
}
- leds_event(led_idle_end);
tick_nohz_restart_sched_tick();
+ idle_notifier_call_chain(IDLE_END);
preempt_enable_no_resched();
schedule();
preempt_disable();
@@ -256,6 +297,77 @@ void machine_restart(char *cmd)
arm_pm_restart(reboot_mode, cmd);
}
+/*
+ * dump a block of kernel memory from around the given address
+ */
+static void show_data(unsigned long addr, int nbytes, const char *name)
+{
+ int i, j;
+ int nlines;
+ u32 *p;
+
+ /*
+ * don't attempt to dump non-kernel addresses or
+ * values that are probably just small negative numbers
+ */
+ if (addr < PAGE_OFFSET || addr > -256UL)
+ return;
+
+ printk("\n%s: %#lx:\n", name, addr);
+
+ /*
+ * round address down to a 32 bit boundary
+ * and always dump a multiple of 32 bytes
+ */
+ p = (u32 *)(addr & ~(sizeof(u32) - 1));
+ nbytes += (addr & (sizeof(u32) - 1));
+ nlines = (nbytes + 31) / 32;
+
+
+ for (i = 0; i < nlines; i++) {
+ /*
+ * just display low 16 bits of address to keep
+ * each line of the dump < 80 characters
+ */
+ printk("%04lx ", (unsigned long)p & 0xffff);
+ for (j = 0; j < 8; j++) {
+ u32 data;
+ if (probe_kernel_address(p, data)) {
+ printk(" ********");
+ } else {
+ printk(" %08x", data);
+ }
+ ++p;
+ }
+ printk("\n");
+ }
+}
+
+static void show_extra_register_data(struct pt_regs *regs, int nbytes)
+{
+ mm_segment_t fs;
+
+ fs = get_fs();
+ set_fs(KERNEL_DS);
+ show_data(regs->ARM_pc - nbytes, nbytes * 2, "PC");
+ show_data(regs->ARM_lr - nbytes, nbytes * 2, "LR");
+ show_data(regs->ARM_sp - nbytes, nbytes * 2, "SP");
+ show_data(regs->ARM_ip - nbytes, nbytes * 2, "IP");
+ show_data(regs->ARM_fp - nbytes, nbytes * 2, "FP");
+ show_data(regs->ARM_r0 - nbytes, nbytes * 2, "R0");
+ show_data(regs->ARM_r1 - nbytes, nbytes * 2, "R1");
+ show_data(regs->ARM_r2 - nbytes, nbytes * 2, "R2");
+ show_data(regs->ARM_r3 - nbytes, nbytes * 2, "R3");
+ show_data(regs->ARM_r4 - nbytes, nbytes * 2, "R4");
+ show_data(regs->ARM_r5 - nbytes, nbytes * 2, "R5");
+ show_data(regs->ARM_r6 - nbytes, nbytes * 2, "R6");
+ show_data(regs->ARM_r7 - nbytes, nbytes * 2, "R7");
+ show_data(regs->ARM_r8 - nbytes, nbytes * 2, "R8");
+ show_data(regs->ARM_r9 - nbytes, nbytes * 2, "R9");
+ show_data(regs->ARM_r10 - nbytes, nbytes * 2, "R10");
+ set_fs(fs);
+}
+
void __show_regs(struct pt_regs *regs)
{
unsigned long flags;
@@ -315,6 +427,8 @@ void __show_regs(struct pt_regs *regs)
printk("Control: %08x%s\n", ctrl, buf);
}
#endif
+
+ show_extra_register_data(regs, 128);
}
void show_regs(struct pt_regs * regs)
diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c
index e514c76043b4..60496fac7bba 100644
--- a/arch/arm/kernel/setup.c
+++ b/arch/arm/kernel/setup.c
@@ -1002,7 +1002,11 @@ static int c_show(struct seq_file *m, void *v)
cpu_name, read_cpuid_id() & 15, elf_platform);
#if defined(CONFIG_SMP)
+# if defined(CONFIG_REPORT_PRESENT_CPUS)
+ for_each_present_cpu(i) {
+# else
for_each_online_cpu(i) {
+# endif
/*
* glibc reads /proc/cpuinfo to determine the number of
* online processors, looking for lines beginning with
diff --git a/arch/arm/kernel/sleep.S b/arch/arm/kernel/sleep.S
index dc902f2c6845..e87f5f243012 100644
--- a/arch/arm/kernel/sleep.S
+++ b/arch/arm/kernel/sleep.S
@@ -94,7 +94,6 @@ ENDPROC(cpu_resume_turn_mmu_on)
cpu_resume_after_mmu:
str r5, [r2, r4, lsl #2] @ restore old mapping
mcr p15, 0, r0, c1, c0, 0 @ turn on D-cache
- bl cpu_init @ restore the und/abt/irq banked regs
mov r0, #0 @ return zero on success
ldmfd sp!, {r4 - r11, pc}
ENDPROC(cpu_resume_after_mmu)
diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c
index d88ff0230e82..a63326923e84 100644
--- a/arch/arm/kernel/smp.c
+++ b/arch/arm/kernel/smp.c
@@ -53,6 +53,7 @@ enum ipi_msg_type {
IPI_CALL_FUNC,
IPI_CALL_FUNC_SINGLE,
IPI_CPU_STOP,
+ IPI_CPU_BACKTRACE,
};
int __cpuinit __cpu_up(unsigned int cpu)
@@ -301,17 +302,7 @@ asmlinkage void __cpuinit secondary_start_kernel(void)
*/
platform_secondary_init(cpu);
- /*
- * Enable local interrupts.
- */
notify_cpu_starting(cpu);
- local_irq_enable();
- local_fiq_enable();
-
- /*
- * Setup the percpu timer for this CPU.
- */
- percpu_timer_setup();
calibrate_delay();
@@ -323,10 +314,23 @@ asmlinkage void __cpuinit secondary_start_kernel(void)
* before we continue.
*/
set_cpu_online(cpu, true);
+
+ /*
+ * Setup the percpu timer for this CPU.
+ */
+ percpu_timer_setup();
+
while (!cpu_active(cpu))
cpu_relax();
/*
+ * cpu_active bit is set, so it's safe to enable interrupts
+ * now.
+ */
+ local_irq_enable();
+ local_fiq_enable();
+
+ /*
* OK, it's off to the idle thread for us
*/
cpu_idle();
@@ -412,6 +416,7 @@ static const char *ipi_types[NR_IPI] = {
S(IPI_CALL_FUNC, "Function call interrupts"),
S(IPI_CALL_FUNC_SINGLE, "Single function call interrupts"),
S(IPI_CPU_STOP, "CPU stop interrupts"),
+ S(IPI_CPU_BACKTRACE, "CPU backtrace"),
};
void show_ipi_list(struct seq_file *p, int prec)
@@ -562,6 +567,58 @@ static void ipi_cpu_stop(unsigned int cpu)
cpu_relax();
}
+static cpumask_t backtrace_mask;
+static DEFINE_RAW_SPINLOCK(backtrace_lock);
+
+/* "in progress" flag of arch_trigger_all_cpu_backtrace */
+static unsigned long backtrace_flag;
+
+void smp_send_all_cpu_backtrace(void)
+{
+ unsigned int this_cpu = smp_processor_id();
+ int i;
+
+ if (test_and_set_bit(0, &backtrace_flag))
+ /*
+ * If there is already a trigger_all_cpu_backtrace() in progress
+ * (backtrace_flag == 1), don't output double cpu dump infos.
+ */
+ return;
+
+ cpumask_copy(&backtrace_mask, cpu_online_mask);
+ cpu_clear(this_cpu, backtrace_mask);
+
+ pr_info("Backtrace for cpu %d (current):\n", this_cpu);
+ dump_stack();
+
+ pr_info("\nsending IPI to all other CPUs:\n");
+ smp_cross_call(&backtrace_mask, IPI_CPU_BACKTRACE);
+
+ /* Wait for up to 10 seconds for all other CPUs to do the backtrace */
+ for (i = 0; i < 10 * 1000; i++) {
+ if (cpumask_empty(&backtrace_mask))
+ break;
+ mdelay(1);
+ }
+
+ clear_bit(0, &backtrace_flag);
+ smp_mb__after_clear_bit();
+}
+
+/*
+ * ipi_cpu_backtrace - handle IPI from smp_send_all_cpu_backtrace()
+ */
+static void ipi_cpu_backtrace(unsigned int cpu, struct pt_regs *regs)
+{
+ if (cpu_isset(cpu, backtrace_mask)) {
+ raw_spin_lock(&backtrace_lock);
+ pr_warning("IPI backtrace for cpu %d\n", cpu);
+ show_regs(regs);
+ raw_spin_unlock(&backtrace_lock);
+ cpu_clear(cpu, backtrace_mask);
+ }
+}
+
/*
* Main handler for inter-processor interrupts
*/
@@ -594,6 +651,10 @@ asmlinkage void __exception_irq_entry do_IPI(int ipinr, struct pt_regs *regs)
ipi_cpu_stop(cpu);
break;
+ case IPI_CPU_BACKTRACE:
+ ipi_cpu_backtrace(cpu, regs);
+ break;
+
default:
printk(KERN_CRIT "CPU%u: Unknown IPI message 0x%x\n",
cpu, ipinr);
diff --git a/arch/arm/kernel/smp_twd.c b/arch/arm/kernel/smp_twd.c
index 01c186222f3b..1953102bec9f 100644
--- a/arch/arm/kernel/smp_twd.c
+++ b/arch/arm/kernel/smp_twd.c
@@ -10,13 +10,17 @@
*/
#include <linux/init.h>
#include <linux/kernel.h>
+#include <linux/clk.h>
+#include <linux/cpufreq.h>
#include <linux/delay.h>
#include <linux/device.h>
+#include <linux/err.h>
#include <linux/smp.h>
#include <linux/jiffies.h>
#include <linux/clockchips.h>
#include <linux/irq.h>
#include <linux/io.h>
+#include <linux/percpu.h>
#include <asm/smp_twd.h>
#include <asm/hardware/gic.h>
@@ -24,7 +28,9 @@
/* set up by the platform code */
void __iomem *twd_base;
+static struct clk *twd_clk;
static unsigned long twd_timer_rate;
+static DEFINE_PER_CPU(struct clock_event_device *, twd_ce);
static void twd_set_mode(enum clock_event_mode mode,
struct clock_event_device *clk)
@@ -80,6 +86,48 @@ int twd_timer_ack(void)
return 0;
}
+/*
+ * Updates clockevent frequency when the cpu frequency changes.
+ * Called on the cpu that is changing frequency with interrupts disabled.
+ */
+static void twd_update_frequency(void *data)
+{
+ twd_timer_rate = clk_get_rate(twd_clk);
+
+ clockevents_update_freq(__get_cpu_var(twd_ce), twd_timer_rate);
+}
+
+static int twd_cpufreq_transition(struct notifier_block *nb,
+ unsigned long state, void *data)
+{
+ struct cpufreq_freqs *freqs = data;
+
+ /*
+ * The twd clock events must be reprogrammed to account for the new
+ * frequency. The timer is local to a cpu, so cross-call to the
+ * changing cpu.
+ */
+ if (state == CPUFREQ_POSTCHANGE || state == CPUFREQ_RESUMECHANGE)
+ smp_call_function_single(freqs->cpu, twd_update_frequency,
+ NULL, 1);
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block twd_cpufreq_nb = {
+ .notifier_call = twd_cpufreq_transition,
+};
+
+static int twd_cpufreq_init(void)
+{
+ if (!IS_ERR_OR_NULL(twd_clk))
+ return cpufreq_register_notifier(&twd_cpufreq_nb,
+ CPUFREQ_TRANSITION_NOTIFIER);
+
+ return 0;
+}
+core_initcall(twd_cpufreq_init);
+
static void __cpuinit twd_calibrate_rate(void)
{
unsigned long count;
@@ -119,12 +167,39 @@ static void __cpuinit twd_calibrate_rate(void)
}
}
+static struct clk *twd_get_clock(void)
+{
+ struct clk *clk;
+ int err;
+
+ clk = clk_get_sys("smp_twd", NULL);
+ if (IS_ERR(clk)) {
+ pr_err("smp_twd: clock not found: %d\n", (int)PTR_ERR(clk));
+ return clk;
+ }
+
+ err = clk_enable(clk);
+ if (err) {
+ pr_err("smp_twd: clock failed to enable: %d\n", err);
+ clk_put(clk);
+ return ERR_PTR(err);
+ }
+
+ return clk;
+}
+
/*
* Setup the local clock events for a CPU.
*/
void __cpuinit twd_timer_setup(struct clock_event_device *clk)
{
- twd_calibrate_rate();
+ if (!twd_clk)
+ twd_clk = twd_get_clock();
+
+ if (!IS_ERR_OR_NULL(twd_clk))
+ twd_timer_rate = clk_get_rate(twd_clk);
+ else
+ twd_calibrate_rate();
clk->name = "local_timer";
clk->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT |
@@ -132,12 +207,11 @@ void __cpuinit twd_timer_setup(struct clock_event_device *clk)
clk->rating = 350;
clk->set_mode = twd_set_mode;
clk->set_next_event = twd_set_next_event;
- clk->shift = 20;
- clk->mult = div_sc(twd_timer_rate, NSEC_PER_SEC, clk->shift);
- clk->max_delta_ns = clockevent_delta2ns(0xffffffff, clk);
- clk->min_delta_ns = clockevent_delta2ns(0xf, clk);
- clockevents_register_device(clk);
+ __get_cpu_var(twd_ce) = clk;
+
+ clockevents_config_and_register(clk, twd_timer_rate,
+ 0xf, 0xffffffff);
/* Make sure our local interrupt controller has this enabled */
gic_enable_ppi(clk->irq);
diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c
index bc9f9da782cb..4ef9f0d04e5a 100644
--- a/arch/arm/kernel/traps.c
+++ b/arch/arm/kernel/traps.c
@@ -466,7 +466,9 @@ do_cache_op(unsigned long start, unsigned long end, int flags)
if (end > vma->vm_end)
end = vma->vm_end;
- flush_cache_user_range(vma, start, end);
+ up_read(&mm->mmap_sem);
+ flush_cache_user_range(start, end);
+ return;
}
up_read(&mm->mmap_sem);
}
diff --git a/arch/arm/lib/Makefile b/arch/arm/lib/Makefile
index cf73a7f742dd..775132a100f0 100644
--- a/arch/arm/lib/Makefile
+++ b/arch/arm/lib/Makefile
@@ -6,7 +6,7 @@
lib-y := backtrace.o changebit.o csumipv6.o csumpartial.o \
csumpartialcopy.o csumpartialcopyuser.o clearbit.o \
- delay.o findbit.o memchr.o memcpy.o \
+ findbit.o memchr.o memcpy.o \
memmove.o memset.o memzero.o setbit.o \
strncpy_from_user.o strnlen_user.o \
strchr.o strrchr.o \
@@ -17,6 +17,10 @@ lib-y := backtrace.o changebit.o csumipv6.o csumpartial.o \
mmu-y := clear_user.o copy_page.o getuser.o putuser.o
+ifneq ($(CONFIG_ARCH_PROVIDES_UDELAY),y)
+ lib-y += delay.o
+endif
+
# the code in uaccess.S is not preemption safe and
# probably faster on ARMv3 only
ifeq ($(CONFIG_PREEMPT),y)
diff --git a/arch/arm/mach-pxa/pm.c b/arch/arm/mach-pxa/pm.c
index 37178a8559b1..51e1583265b2 100644
--- a/arch/arm/mach-pxa/pm.c
+++ b/arch/arm/mach-pxa/pm.c
@@ -42,6 +42,7 @@ int pxa_pm_enter(suspend_state_t state)
/* *** go zzz *** */
pxa_cpu_pm_fns->enter(state);
+ cpu_init();
if (state != PM_SUSPEND_STANDBY && pxa_cpu_pm_fns->restore) {
/* after sleeping, validate the checksum */
diff --git a/arch/arm/mach-sa1100/pm.c b/arch/arm/mach-sa1100/pm.c
index bf85b8b259d5..e4512cdb9236 100644
--- a/arch/arm/mach-sa1100/pm.c
+++ b/arch/arm/mach-sa1100/pm.c
@@ -78,6 +78,8 @@ static int sa11x0_pm_enter(suspend_state_t state)
/* go zzz */
cpu_suspend(0, sa1100_finish_suspend);
+ cpu_init();
+
/*
* Ensure not to come back here if it wasn't intended
*/
diff --git a/arch/arm/mach-tegra/Kconfig b/arch/arm/mach-tegra/Kconfig
index d82ebab50e11..f71b92e2a069 100644
--- a/arch/arm/mach-tegra/Kconfig
+++ b/arch/arm/mach-tegra/Kconfig
@@ -2,34 +2,75 @@ if ARCH_TEGRA
comment "NVIDIA Tegra options"
-choice
- prompt "Select Tegra processor family for target system"
-
config ARCH_TEGRA_2x_SOC
- bool "Tegra 2 family"
+ bool "Tegra 2 family SOC"
+ default y
+ depends on !ARCH_TEGRA_3x_SOC
+ select ARCH_TEGRA_HAS_PCIE
select CPU_V7
select ARM_GIC
select ARCH_REQUIRE_GPIOLIB
select USB_ARCH_HAS_EHCI if USB_SUPPORT
select USB_ULPI if USB_SUPPORT
select USB_ULPI_VIEWPORT if USB_SUPPORT
+ select ARM_ERRATA_742230 if SMP
+ select USB_ARCH_HAS_EHCI if USB_SUPPORT
+ select USB_ULPI if USB_SUPPORT
+ select USB_ULPI_VIEWPORT if USB_SUPPORT
help
Support for NVIDIA Tegra AP20 and T20 processors, based on the
ARM CortexA9MP CPU and the ARM PL310 L2 cache controller
-endchoice
+config ARCH_TEGRA_3x_SOC
+ bool "Tegra 3 family SOC"
+ select ARCH_TEGRA_HAS_PCIE
+ select ARCH_TEGRA_HAS_SATA
+ select ARCH_TEGRA_HAS_DUAL_3D
+ select ARCH_TEGRA_HAS_DUAL_CPU_CLUSTERS
+ select CPU_V7
+ select ARM_GIC
+ select ARCH_REQUIRE_GPIOLIB
+ select USB_ARCH_HAS_EHCI if USB_SUPPORT
+ select USB_EHCI_TEGRA if USB_SUPPORT
+ select USB_ULPI if USB_SUPPORT
+ select USB_ULPI_VIEWPORT if USB_SUPPORT
+ select REPORT_PRESENT_CPUS if TEGRA_AUTO_HOTPLUG
+ help
+ Support for NVIDIA Tegra 3 family of SoCs, based upon the
+ ARM CortexA9MP CPU and the ARM PL310 L2 cache controller
+
+config ARCH_TEGRA_HAS_DUAL_3D
+ bool
+
+config ARCH_TEGRA_HAS_DUAL_CPU_CLUSTERS
+ bool
+
+config ARCH_TEGRA_HAS_PCIE
+ bool
+
+config ARCH_TEGRA_HAS_SATA
+ bool
config TEGRA_PCI
bool "PCI Express support"
select PCI
+ depends on ARCH_TEGRA_HAS_PCIE
comment "Tegra board type"
config MACH_HARMONY
bool "Harmony board"
+ depends on ARCH_TEGRA_2x_SOC
select MACH_HAS_SND_SOC_TEGRA_WM8903 if SND_SOC
help
- Support for nVidia Harmony development platform
+ Support for NVIDIA Harmony development platform
+
+config MACH_VENTANA
+ bool "Ventana board"
+ depends on ARCH_TEGRA_2x_SOC
+ select MACH_HAS_SND_SOC_TEGRA_WM8903 if SND_SOC
+ help
+ Support for NVIDIA Ventana development platform
config MACH_KAEN
bool "Kaen board"
@@ -40,11 +81,13 @@ config MACH_KAEN
config MACH_PAZ00
bool "Paz00 board"
+ depends on ARCH_TEGRA_2x_SOC
help
Support for the Toshiba AC100/Dynabook AZ netbook
config MACH_SEABOARD
bool "Seaboard board"
+ depends on ARCH_TEGRA_2x_SOC
select MACH_HAS_SND_SOC_TEGRA_WM8903 if SND_SOC
help
Support for nVidia Seaboard development platform. It will
@@ -59,16 +102,73 @@ config MACH_TEGRA_DT
config MACH_TRIMSLICE
bool "TrimSlice board"
+ depends on ARCH_TEGRA_2x_SOC
select TEGRA_PCI
help
Support for CompuLab TrimSlice platform
config MACH_WARIO
bool "Wario board"
+ depends on ARCH_TEGRA_2x_SOC
select MACH_SEABOARD
help
Support for the Wario version of Seaboard
+config MACH_WHISTLER
+ bool "Whistler board"
+ depends on ARCH_TEGRA_2x_SOC
+ select MACH_HAS_SND_SOC_TEGRA_WM8753 if SND_SOC
+ select MACH_HAS_SND_SOC_TEGRA_TLV320AIC326X if SND_SOC
+ help
+ Support for NVIDIA Whistler development platform
+
+config MACH_ARUBA
+ bool "Aruba board"
+ depends on ARCH_TEGRA_3x_SOC
+ select TEGRA_FPGA_PLATFORM
+ help
+ Support for NVIDIA Aruba2 FPGA development platform
+
+config MACH_CARDHU
+ bool "Cardhu board"
+ depends on ARCH_TEGRA_3x_SOC
+ select MACH_HAS_SND_SOC_TEGRA_WM8903 if SND_SOC
+ help
+ Support for NVIDIA Cardhu development platform
+
+config MACH_TEGRA_ENTERPRISE
+ bool "Enterprise board"
+ depends on ARCH_TEGRA_3x_SOC
+ select MACH_HAS_SND_SOC_TEGRA_MAX98088 if SND_SOC
+ help
+ Support for NVIDIA Enterprise development platform
+
+choice
+ prompt "Tegra platform type"
+ default TEGRA_SILICON_PLATFORM
+
+config TEGRA_SILICON_PLATFORM
+ bool "Silicon"
+ help
+ This enables support for a Tegra silicon platform.
+
+config TEGRA_SIMULATION_PLATFORM
+ bool "Simulation"
+ help
+ This enables support for a Tegra simulation platform.
+ Select this only if you are an NVIDIA developer working
+ on a simulation platform.
+
+config TEGRA_FPGA_PLATFORM
+ bool "FPGA"
+ help
+ This enables support for a Tegra FPGA platform.
+ Select this only if you are an NVIDIA developer working
+ on a FPGA platform.
+endchoice
+
+source "arch/arm/mach-tegra/p852/Kconfig"
+
choice
prompt "Low-level debug console UART"
default TEGRA_DEBUG_UART_NONE
@@ -78,19 +178,23 @@ config TEGRA_DEBUG_UART_NONE
config TEGRA_DEBUG_UARTA
bool "UART-A"
+ depends on DEBUG_LL
config TEGRA_DEBUG_UARTB
bool "UART-B"
+ depends on DEBUG_LL
config TEGRA_DEBUG_UARTC
bool "UART-C"
+ depends on DEBUG_LL
config TEGRA_DEBUG_UARTD
bool "UART-D"
+ depends on DEBUG_LL
config TEGRA_DEBUG_UARTE
bool "UART-E"
-
+ depends on DEBUG_LL
endchoice
config TEGRA_SYSTEM_DMA
@@ -100,7 +204,233 @@ config TEGRA_SYSTEM_DMA
Adds system DMA functionality for NVIDIA Tegra SoCs, used by
several Tegra device drivers
+config TEGRA_PWM
+ tristate "Enable PWM driver"
+ select HAVE_PWM
+ help
+ Enable support for the Tegra PWM controller(s).
+
+config TEGRA_FIQ_DEBUGGER
+ bool "Enable the FIQ serial debugger on Tegra"
+ default n
+ select FIQ_DEBUGGER
+ help
+ Enables the FIQ serial debugger on Tegra
+
+config TEGRA_CARDHU_DSI
+ bool "Support DSI panel on Cardhu"
+ depends on MACH_CARDHU
+ select TEGRA_DSI
+ help
+ Support for DSI Panel on Nvidia Cardhu
+
config TEGRA_EMC_SCALING_ENABLE
bool "Enable scaling the memory frequency"
+ depends on TEGRA_SILICON_PLATFORM
+ default n
+
+config TEGRA_CPU_DVFS
+ bool "Enable voltage scaling on Tegra CPU"
+ depends on TEGRA_SILICON_PLATFORM
+ default y
+
+config TEGRA_CORE_DVFS
+ bool "Enable voltage scaling on Tegra core"
+ depends on TEGRA_SILICON_PLATFORM
+ depends on TEGRA_CPU_DVFS
+ default y
+
+config TEGRA_IOVMM_GART
+ bool "Enable I/O virtual memory manager for GART"
+ depends on ARCH_TEGRA_2x_SOC
+ default y
+ select TEGRA_IOVMM
+ help
+ Enables support for remapping discontiguous physical memory
+ shared with the operating system into contiguous I/O virtual
+ space through the GART (Graphics Address Relocation Table)
+ hardware included on Tegra SoCs.
+config TEGRA_IOVMM_SMMU
+ bool "Enable I/O virtual memory manager for SMMU"
+ depends on ARCH_TEGRA_3x_SOC
+ default y
+ select TEGRA_IOVMM
+ help
+ Enables support for remapping discontiguous physical memory
+ shared with the operating system into contiguous I/O virtual
+ space through the SMMU (System Memory Management Unit)
+ hardware included on Tegra SoCs.
+
+config TEGRA_IOVMM_SMMU_SYSFS
+ bool "Enable SMMU register access for debugging"
+ depends on TEGRA_IOVMM_SMMU
+ default n
+ help
+ Enables SMMU register access through /sys/devices/smmu/* files.
+
+config TEGRA_IOVMM
+ bool
+
+config TEGRA_AVP_KERNEL_ON_MMU
+ bool "Use AVP MMU to relocate AVP kernel"
+ depends on ARCH_TEGRA_2x_SOC
+ default y
+ help
+ Use AVP MMU to relocate AVP kernel (nvrm_avp.bin).
+
+config TEGRA_AVP_KERNEL_ON_SMMU
+ bool "Use SMMU to relocate AVP kernel"
+ depends on TEGRA_IOVMM_SMMU
+ default y
+ help
+ Use SMMU to relocate AVP kernel (nvrm_avp.bin).
+
+config TEGRA_ARB_SEMAPHORE
+ bool
+
+config TEGRA_THERMAL_THROTTLE
+ bool "Enable throttling of CPU speed on overtemp"
+ depends on TEGRA_SILICON_PLATFORM
+ depends on CPU_FREQ
+ default y
+ help
+ Also requires enabling a temperature sensor such as NCT1008.
+
+config WIFI_CONTROL_FUNC
+ bool "Enable WiFi control function abstraction"
+ help
+ Enables Power/Reset/Carddetect function abstraction
+
+config TEGRA_CLOCK_DEBUG_WRITE
+ bool "Enable debugfs write access to clock tree"
+ depends on DEBUG_FS
+ default n
+
+config TEGRA_CLUSTER_CONTROL
+ bool
+ depends on ARCH_TEGRA_HAS_DUAL_CPU_CLUSTERS
+ default y if PM_SLEEP
+
+config TEGRA_AUTO_HOTPLUG
+ bool "Enable automatic CPU hot-plugging"
+ depends on HOTPLUG_CPU && CPU_FREQ && !ARCH_CPU_PROBE_RELEASE && !ARCH_TEGRA_2x_SOC
+ default y
+ help
+ This option enables turning CPUs off/on and switching tegra
+ high/low power CPU clusters automatically, corresponding to
+ CPU frequency scaling.
+
+config TEGRA_MC_PROFILE
+ tristate "Enable profiling memory controller utilization"
+ default n
+ help
+ When enabled, provides a mechanism to perform statistical
+ sampling of the memory controller usage on a client-by-client
+ basis, and report the log through sysfs.
+
+config TEGRA_EDP_LIMITS
+ bool "Enforce electrical design limits"
+ depends on TEGRA_SILICON_PLATFORM
+ depends on CPU_FREQ
+ default y if ARCH_TEGRA_3x_SOC
+ default n
+ help
+ Limit maximum CPU frequency based on temperature and number
+ of on-line CPUs to keep CPU rail current within power supply
+ capabilities.
+
+config TEGRA_INTERNAL_TSENSOR_EDP_SUPPORT
+ bool "Enable EDP and thermal throttling using internal TSensor"
+ depends on TEGRA_EDP_LIMITS && ARCH_TEGRA_3x_SOC
+ help
+ When enabled, uses internal tsensor to support EDP and
+ thermal throttling on tegra platform
+
+config TEGRA_EMC_TO_DDR_CLOCK
+ int "EMC to DDR clocks ratio"
+ default "2" if ARCH_TEGRA_2x_SOC
+ default "1"
+
+config TEGRA_CONVSERVATIVE_GOV_ON_EARLYSUPSEND
+ bool "Use conservative cpu frequency governor when device enters early suspend"
+ depends on HAS_EARLYSUSPEND && CPU_FREQ
+ default n
+ help
+ Also will restore to original cpu frequency governor when device is resumed
+
+config TEGRA_LEGACY_AUDIO
+ bool "Enable Tegra Legacy Audio APIs"
+ default n
+ help
+ Say Y if you want to add support legacy (non-ALSA) audio APIs on
+ Tegra. This will disable ALSA (ASoC) support.
+
+config TEGRA_STAT_MON
+ bool "Enable H/W statistics monitor"
+ depends on ARCH_TEGRA_2x_SOC
+ default n
+ help
+ Enables support for hardware statistics monitor for AVP.
+
+config USB_HOTPLUG
+ bool "Enabling the USB hotplug"
+ default n
+
+config TEGRA_DYNAMIC_PWRDET
+ bool "Enable dynamic activation of IO level auto-detection"
+ depends on TEGRA_SILICON_PLATFORM
+ default n
+ help
+ This option allows turning off tegra IO level auto-detection
+ when IO power is stable. If set auto-detection cells are active
+ only during power transitions, otherwise, the cells are active
+ always
+
+config TEGRA_EDP_EXACT_FREQ
+ bool "Use maximum possible cpu frequency when EDP capping"
+ depends on TEGRA_EDP_LIMITS
+ default y
+ help
+ When enabled the cpu will run at the exact frequency
+ specified in the EDP table when EDP capping is applied; when
+ disabled the next lower cpufreq frequency will be used.
+
+config TEGRA_USB_MODEM_POWER
+ bool "Enable tegra usb modem power management"
+ default n
+ help
+ This option enables support for out-of_band remote wakeup, selective
+ suspend and system suspend/resume.
+
+config TEGRA_BB_XMM_POWER
+ bool "Enable power driver for XMM modem"
+ default n
+ help
+ Enables power driver which controls gpio signals to XMM modem.
+
+config TEGRA_BB_XMM_POWER2
+ tristate "Enable power driver for XMM modem (flashless)"
+ default n
+ help
+ Enables power driver which controls gpio signals to XMM modem
+ (in flashless configuration). User-mode application must
+ insert this LKM to initiate 2nd USB enumeration power sequence
+ - after modem software has been downloaded to flashless device.
+
+config TEGRA_THERMAL_SYSFS
+ bool "Enable Thermal driver to use Thermal Sysfs infrastructure"
+ depends on THERMAL
+ default y
+
+config TEGRA_PLLM_RESTRICTED
+ bool "Restrict PLLM usage as module clock source"
+ depends on !ARCH_TEGRA_2x_SOC
+ default n
+ help
+ When enabled, PLLM usage may be restricted to modules with dividers
+ capable of dividing maximum PLLM frequency at minimum voltage. When
+ disabled, PLLM is used as a clock source with no restrictions (which
+ may effectively increase lower limit for core voltage).
endif
+
diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile
index f11b9100114a..67b4e3294607 100644
--- a/arch/arm/mach-tegra/Makefile
+++ b/arch/arm/mach-tegra/Makefile
@@ -1,30 +1,113 @@
+GCOV_PROFILE := y
+
+obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += ahb.o
+obj-y += apbio.o
obj-y += common.o
-obj-y += devices.o
+obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += common-t2.o
+obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += common-t3.o
obj-y += io.o
obj-y += irq.o
+obj-$(CONFIG_TEGRA_GRHOST) += syncpt.o
obj-y += clock.o
obj-y += timer.o
+ifeq ($(CONFIG_ARCH_TEGRA_2x_SOC),y)
+obj-y += tegra2_clocks.o
+obj-y += timer-t2.o
+else
+obj-y += tegra3_clocks.o
+obj-y += timer-t3.o
+endif
obj-y += pinmux.o
+obj-y += devices.o
+obj-y += delay.o
obj-y += powergate.o
-obj-y += fuse.o
-obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += clock.o
-obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra2_clocks.o
-obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra2_emc.o
-obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += pinmux-t2-tables.o
-obj-$(CONFIG_SMP) += platsmp.o localtimer.o headsmp.o
+obj-y += pm.o
+obj-$(CONFIG_PM_SLEEP) += pm-irq.o
+obj-y += gic.o
+obj-y += sleep.o
+obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += sleep-t2.o
+obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += sleep-t3.o
+obj-y += fuse.o
+obj-y += kfuse.o
+obj-y += csi.o
+obj-$(CONFIG_TEGRA_SILICON_PLATFORM) += tegra_odm_fuses.o
+obj-y += i2c_error_recovery.o
+obj-$(CONFIG_TEGRA_LEGACY_AUDIO) += tegra_i2s_audio.o
+obj-$(CONFIG_TEGRA_LEGACY_AUDIO) += tegra_spdif_audio.o
+obj-y += mc.o
+obj-$(CONFIG_TEGRA_STAT_MON) += tegra2_statmon.o
+obj-$(CONFIG_USB_SUPPORT) += usb_phy.o
+obj-$(CONFIG_FIQ) += fiq.o
+obj-$(CONFIG_TEGRA_FIQ_DEBUGGER) += tegra_fiq_debugger.o
+obj-$(CONFIG_TEGRA_PWM) += pwm.o
+obj-$(CONFIG_TEGRA_ARB_SEMAPHORE) += arb_sema.o
+
+ifeq ($(CONFIG_TEGRA_SILICON_PLATFORM),y)
+obj-y += dvfs.o
+obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra2_dvfs.o
+obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += tegra3_dvfs.o
+obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += latency_allowance.o
+obj-$(CONFIG_TEGRA_EDP_LIMITS) += edp.o
+endif
+ifeq ($(CONFIG_TEGRA_SILICON_PLATFORM),y)
+obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra2_speedo.o
+obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += tegra3_speedo.o
+obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += tegra3_actmon.o
+endif
+obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra2_emc.o
+obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += tegra3_emc.o
+obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += wakeups-t2.o
+obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += wakeups-t3.o
+obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += pm-t2.o
+obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += pm-t3.o
+
+obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += pinmux-t2-tables.o
+obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += pinmux-t3-tables.o
+obj-$(CONFIG_LOCAL_TIMERS) += localtimer.o
+obj-$(CONFIG_SMP) += platsmp.o
obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
-obj-$(CONFIG_TEGRA_SYSTEM_DMA) += dma.o
+obj-y += headsmp.o
+obj-y += reset.o
+obj-$(CONFIG_TEGRA_SYSTEM_DMA) += dma.o
obj-$(CONFIG_CPU_FREQ) += cpu-tegra.o
-obj-$(CONFIG_TEGRA_PCI) += pcie.o
-obj-$(CONFIG_USB_SUPPORT) += usb_phy.o
+ifeq ($(CONFIG_TEGRA_AUTO_HOTPLUG),y)
+obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += cpu-tegra3.o
+endif
+obj-$(CONFIG_TEGRA_PCI) += pcie.o
+obj-$(CONFIG_USB_SUPPORT) += usb_phy.o
+ifeq ($(CONFIG_CPU_IDLE),y)
+obj-y += cpuidle.o
+ifeq ($(CONFIG_PM_SLEEP),y)
+obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += cpuidle-t2.o
+obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += cpuidle-t3.o
+endif
+endif
+ifeq ($(CONFIG_TEGRA_THERMAL_THROTTLE),y)
+obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra2_throttle.o
+obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += tegra3_throttle.o
+endif
+obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += tegra3_thermal.o
+obj-$(CONFIG_TEGRA_IOVMM) += iovmm.o
+obj-$(CONFIG_TEGRA_IOVMM_GART) += iovmm-gart.o
+obj-$(CONFIG_TEGRA_IOVMM_SMMU) += iovmm-smmu.o
+obj-$(CONFIG_DEBUG_ICEDCC) += sysfs-dcc.o
+obj-$(CONFIG_TEGRA_CLUSTER_CONTROL) += sysfs-cluster.o
+ifeq ($(CONFIG_TEGRA_MC_PROFILE),y)
+obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra2_mc.o
+endif
+obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += tegra3_tsensor.o
+obj-$(CONFIG_TEGRA_DYNAMIC_PWRDET) += powerdetect.o
+obj-$(CONFIG_TEGRA_USB_MODEM_POWER) += tegra_usb_modem_power.o
obj-${CONFIG_MACH_HARMONY} += board-harmony.o
+obj-${CONFIG_MACH_HARMONY} += board-harmony-kbc.o
+obj-${CONFIG_MACH_HARMONY} += board-harmony-panel.o
obj-${CONFIG_MACH_HARMONY} += board-harmony-pinmux.o
obj-${CONFIG_MACH_HARMONY} += board-harmony-pcie.o
obj-${CONFIG_MACH_HARMONY} += board-harmony-power.o
-obj-${CONFIG_MACH_PAZ00} += board-paz00.o
-obj-${CONFIG_MACH_PAZ00} += board-paz00-pinmux.o
+obj-${CONFIG_MACH_PAZ00} += board-paz00.o
+obj-${CONFIG_MACH_PAZ00} += board-paz00-pinmux.o
obj-${CONFIG_MACH_SEABOARD} += board-seaboard.o
obj-${CONFIG_MACH_SEABOARD} += board-seaboard-pinmux.o
@@ -34,3 +117,56 @@ obj-${CONFIG_MACH_TEGRA_DT} += board-harmony-pinmux.o
obj-${CONFIG_MACH_TRIMSLICE} += board-trimslice.o
obj-${CONFIG_MACH_TRIMSLICE} += board-trimslice-pinmux.o
+
+obj-${CONFIG_MACH_P852} += p852/
+
+obj-${CONFIG_MACH_VENTANA} += board-ventana.o
+obj-${CONFIG_MACH_VENTANA} += board-ventana-pinmux.o
+obj-${CONFIG_MACH_VENTANA} += board-ventana-sdhci.o
+obj-${CONFIG_MACH_VENTANA} += board-ventana-power.o
+obj-${CONFIG_MACH_VENTANA} += board-ventana-panel.o
+obj-${CONFIG_MACH_VENTANA} += board-ventana-sensors.o
+obj-${CONFIG_MACH_VENTANA} += board-ventana-memory.o
+
+obj-${CONFIG_MACH_ARUBA} += board-aruba.o
+obj-${CONFIG_MACH_ARUBA} += board-aruba-panel.o
+obj-${CONFIG_MACH_ARUBA} += board-aruba-pinmux.o
+obj-${CONFIG_MACH_ARUBA} += board-aruba-power.o
+obj-${CONFIG_MACH_ARUBA} += board-aruba-sdhci.o
+obj-${CONFIG_MACH_ARUBA} += board-aruba-sensors.o
+
+obj-${CONFIG_MACH_WHISTLER} += board-whistler.o
+obj-${CONFIG_MACH_WHISTLER} += board-whistler-pinmux.o
+obj-${CONFIG_MACH_WHISTLER} += board-whistler-sdhci.o
+obj-${CONFIG_MACH_WHISTLER} += board-whistler-power.o
+obj-${CONFIG_MACH_WHISTLER} += board-whistler-panel.o
+obj-${CONFIG_MACH_WHISTLER} += board-whistler-sensors.o
+obj-${CONFIG_MACH_WHISTLER} += board-whistler-kbc.o
+obj-${CONFIG_MACH_WHISTLER} += board-whistler-baseband.o
+obj-${CONFIG_MACH_WHISTLER} += board-whistler-memory.o
+
+obj-${CONFIG_MACH_CARDHU} += board-cardhu.o
+obj-${CONFIG_MACH_CARDHU} += board-cardhu-kbc.o
+obj-${CONFIG_MACH_CARDHU} += board-cardhu-panel.o
+obj-${CONFIG_MACH_CARDHU} += board-cardhu-pinmux.o
+obj-${CONFIG_MACH_CARDHU} += board-cardhu-power.o
+obj-${CONFIG_MACH_CARDHU} += board-cardhu-pm298-power-rails.o
+obj-${CONFIG_MACH_CARDHU} += board-cardhu-pm299-power-rails.o
+obj-${CONFIG_MACH_CARDHU} += board-cardhu-sdhci.o
+obj-${CONFIG_MACH_CARDHU} += board-cardhu-sensors.o
+obj-${CONFIG_MACH_CARDHU} += board-cardhu-memory.o
+obj-${CONFIG_MACH_CARDHU} += board-cardhu-powermon.o
+
+obj-${CONFIG_MACH_TEGRA_ENTERPRISE} += board-enterprise.o
+obj-${CONFIG_MACH_TEGRA_ENTERPRISE} += board-enterprise-panel.o
+obj-${CONFIG_MACH_TEGRA_ENTERPRISE} += board-enterprise-pinmux.o
+obj-${CONFIG_MACH_TEGRA_ENTERPRISE} += board-enterprise-sdhci.o
+obj-${CONFIG_MACH_TEGRA_ENTERPRISE} += board-enterprise-memory.o
+obj-${CONFIG_MACH_TEGRA_ENTERPRISE} += board-enterprise-power.o
+obj-${CONFIG_MACH_TEGRA_ENTERPRISE} += board-enterprise-baseband.o
+obj-${CONFIG_MACH_TEGRA_ENTERPRISE} += board-enterprise-kbc.o
+obj-${CONFIG_MACH_TEGRA_ENTERPRISE} += board-enterprise-sensors.o
+
+obj-${CONFIG_TEGRA_BB_XMM_POWER} += baseband-xmm-power.o
+obj-${CONFIG_TEGRA_BB_XMM_POWER2} += baseband-xmm-power2.o
+
diff --git a/arch/arm/mach-tegra/Makefile.boot b/arch/arm/mach-tegra/Makefile.boot
index 428ad122be03..d8cb9173cdf7 100644
--- a/arch/arm/mach-tegra/Makefile.boot
+++ b/arch/arm/mach-tegra/Makefile.boot
@@ -2,5 +2,9 @@ zreladdr-$(CONFIG_ARCH_TEGRA_2x_SOC) := 0x00008000
params_phys-$(CONFIG_ARCH_TEGRA_2x_SOC) := 0x00000100
initrd_phys-$(CONFIG_ARCH_TEGRA_2x_SOC) := 0x00800000
+zreladdr-$(CONFIG_ARCH_TEGRA_3x_SOC) := 0x80008000
+params_phys-$(CONFIG_ARCH_TEGRA_3x_SOC) := 0x80000100
+initrd_phys-$(CONFIG_ARCH_TEGRA_3x_SOC) := 0x80800000
+
dtb-$(CONFIG_MACH_HARMONY) += tegra-harmony.dtb
dtb-$(CONFIG_MACH_SEABOARD) += tegra-seaboard.dtb
diff --git a/arch/arm/mach-tegra/ahb.c b/arch/arm/mach-tegra/ahb.c
new file mode 100644
index 000000000000..b7f3fb5219bb
--- /dev/null
+++ b/arch/arm/mach-tegra/ahb.c
@@ -0,0 +1,218 @@
+/*
+ * arch/arm/mach-tegra/ahb.c
+ *
+ * Copyright (C) 2011 Google, Inc.
+ *
+ * Author:
+ * Jay Cheng <jacheng@nvidia.com>
+ * James Wylder <james.wylder@motorola.com>
+ * Benoit Goby <benoit@android.com>
+ * Colin Cross <ccross@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/kernel.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/syscore_ops.h>
+
+#include <mach/iomap.h>
+
+#define AHB_ARBITRATION_DISABLE 0x00
+#define AHB_ARBITRATION_PRIORITY_CTRL 0x04
+#define AHB_PRIORITY_WEIGHT(x) (((x) & 0x7) << 29)
+#define PRIORITY_SELECT_USB BIT(6)
+#define PRIORITY_SELECT_USB2 BIT(18)
+#define PRIORITY_SELECT_USB3 BIT(17)
+
+#define AHB_GIZMO_AHB_MEM 0x0c
+#define ENB_FAST_REARBITRATE BIT(2)
+#define DONT_SPLIT_AHB_WR BIT(7)
+
+#define AHB_GIZMO_APB_DMA 0x10
+#define AHB_GIZMO_IDE 0x18
+#define AHB_GIZMO_USB 0x1c
+#define AHB_GIZMO_AHB_XBAR_BRIDGE 0x20
+#define AHB_GIZMO_CPU_AHB_BRIDGE 0x24
+#define AHB_GIZMO_COP_AHB_BRIDGE 0x28
+#define AHB_GIZMO_XBAR_APB_CTLR 0x2c
+#define AHB_GIZMO_VCP_AHB_BRIDGE 0x30
+#define AHB_GIZMO_NAND 0x3c
+#define AHB_GIZMO_SDMMC4 0x44
+#define AHB_GIZMO_XIO 0x48
+#define AHB_GIZMO_BSEV 0x60
+#define AHB_GIZMO_BSEA 0x70
+#define AHB_GIZMO_NOR 0x74
+#define AHB_GIZMO_USB2 0x78
+#define AHB_GIZMO_USB3 0x7c
+#define IMMEDIATE BIT(18)
+
+#define AHB_GIZMO_SDMMC1 0x80
+#define AHB_GIZMO_SDMMC2 0x84
+#define AHB_GIZMO_SDMMC3 0x88
+#define AHB_MEM_PREFETCH_CFG_X 0xd8
+#define AHB_ARBITRATION_XBAR_CTRL 0xdc
+#define AHB_MEM_PREFETCH_CFG3 0xe0
+#define AHB_MEM_PREFETCH_CFG4 0xe4
+#define AHB_MEM_PREFETCH_CFG1 0xec
+#define AHB_MEM_PREFETCH_CFG2 0xf0
+#define PREFETCH_ENB BIT(31)
+#define MST_ID(x) (((x) & 0x1f) << 26)
+#define AHBDMA_MST_ID MST_ID(5)
+#define USB_MST_ID MST_ID(6)
+#define USB2_MST_ID MST_ID(18)
+#define USB3_MST_ID MST_ID(17)
+#define ADDR_BNDRY(x) (((x) & 0xf) << 21)
+#define INACTIVITY_TIMEOUT(x) (((x) & 0xffff) << 0)
+
+#define AHB_ARBITRATION_AHB_MEM_WRQUE_MST_ID 0xf8
+
+
+static inline unsigned long gizmo_readl(unsigned long offset)
+{
+ return readl(IO_TO_VIRT(TEGRA_AHB_GIZMO_BASE + offset));
+}
+
+static inline void gizmo_writel(unsigned long value, unsigned long offset)
+{
+ writel(value, IO_TO_VIRT(TEGRA_AHB_GIZMO_BASE + offset));
+}
+
+static u32 ahb_gizmo[29];
+
+#ifdef CONFIG_PM
+int tegra_ahbgizmo_suspend(void)
+{
+ ahb_gizmo[0] = gizmo_readl(AHB_ARBITRATION_DISABLE);
+ ahb_gizmo[1] = gizmo_readl(AHB_ARBITRATION_PRIORITY_CTRL);
+ ahb_gizmo[2] = gizmo_readl(AHB_GIZMO_AHB_MEM);
+ ahb_gizmo[3] = gizmo_readl(AHB_GIZMO_APB_DMA);
+ ahb_gizmo[4] = gizmo_readl(AHB_GIZMO_IDE);
+ ahb_gizmo[5] = gizmo_readl(AHB_GIZMO_USB);
+ ahb_gizmo[6] = gizmo_readl(AHB_GIZMO_AHB_XBAR_BRIDGE);
+ ahb_gizmo[7] = gizmo_readl(AHB_GIZMO_CPU_AHB_BRIDGE);
+ ahb_gizmo[8] = gizmo_readl(AHB_GIZMO_COP_AHB_BRIDGE);
+ ahb_gizmo[9] = gizmo_readl(AHB_GIZMO_XBAR_APB_CTLR);
+ ahb_gizmo[10] = gizmo_readl(AHB_GIZMO_VCP_AHB_BRIDGE);
+ ahb_gizmo[11] = gizmo_readl(AHB_GIZMO_NAND);
+ ahb_gizmo[12] = gizmo_readl(AHB_GIZMO_SDMMC4);
+ ahb_gizmo[13] = gizmo_readl(AHB_GIZMO_XIO);
+ ahb_gizmo[14] = gizmo_readl(AHB_GIZMO_BSEV);
+ ahb_gizmo[15] = gizmo_readl(AHB_GIZMO_BSEA);
+ ahb_gizmo[16] = gizmo_readl(AHB_GIZMO_NOR);
+ ahb_gizmo[17] = gizmo_readl(AHB_GIZMO_USB2);
+ ahb_gizmo[18] = gizmo_readl(AHB_GIZMO_USB3);
+ ahb_gizmo[19] = gizmo_readl(AHB_GIZMO_SDMMC1);
+ ahb_gizmo[20] = gizmo_readl(AHB_GIZMO_SDMMC2);
+ ahb_gizmo[21] = gizmo_readl(AHB_GIZMO_SDMMC3);
+ ahb_gizmo[22] = gizmo_readl(AHB_MEM_PREFETCH_CFG_X);
+ ahb_gizmo[23] = gizmo_readl(AHB_ARBITRATION_XBAR_CTRL);
+ ahb_gizmo[24] = gizmo_readl(AHB_MEM_PREFETCH_CFG3);
+ ahb_gizmo[25] = gizmo_readl(AHB_MEM_PREFETCH_CFG4);
+ ahb_gizmo[26] = gizmo_readl(AHB_MEM_PREFETCH_CFG1);
+ ahb_gizmo[27] = gizmo_readl(AHB_MEM_PREFETCH_CFG2);
+ ahb_gizmo[28] = gizmo_readl(AHB_ARBITRATION_AHB_MEM_WRQUE_MST_ID);
+ return 0;
+}
+
+void tegra_ahbgizmo_resume(void)
+{
+ gizmo_writel(ahb_gizmo[0], AHB_ARBITRATION_DISABLE);
+ gizmo_writel(ahb_gizmo[1], AHB_ARBITRATION_PRIORITY_CTRL);
+ gizmo_writel(ahb_gizmo[2], AHB_GIZMO_AHB_MEM);
+ gizmo_writel(ahb_gizmo[3], AHB_GIZMO_APB_DMA);
+ gizmo_writel(ahb_gizmo[4], AHB_GIZMO_IDE);
+ gizmo_writel(ahb_gizmo[5], AHB_GIZMO_USB);
+ gizmo_writel(ahb_gizmo[6], AHB_GIZMO_AHB_XBAR_BRIDGE);
+ gizmo_writel(ahb_gizmo[7], AHB_GIZMO_CPU_AHB_BRIDGE);
+ gizmo_writel(ahb_gizmo[8], AHB_GIZMO_COP_AHB_BRIDGE);
+ gizmo_writel(ahb_gizmo[9], AHB_GIZMO_XBAR_APB_CTLR);
+ gizmo_writel(ahb_gizmo[10], AHB_GIZMO_VCP_AHB_BRIDGE);
+ gizmo_writel(ahb_gizmo[11], AHB_GIZMO_NAND);
+ gizmo_writel(ahb_gizmo[12], AHB_GIZMO_SDMMC4);
+ gizmo_writel(ahb_gizmo[13], AHB_GIZMO_XIO);
+ gizmo_writel(ahb_gizmo[14], AHB_GIZMO_BSEV);
+ gizmo_writel(ahb_gizmo[15], AHB_GIZMO_BSEA);
+ gizmo_writel(ahb_gizmo[16], AHB_GIZMO_NOR);
+ gizmo_writel(ahb_gizmo[17], AHB_GIZMO_USB2);
+ gizmo_writel(ahb_gizmo[18], AHB_GIZMO_USB3);
+ gizmo_writel(ahb_gizmo[19], AHB_GIZMO_SDMMC1);
+ gizmo_writel(ahb_gizmo[20], AHB_GIZMO_SDMMC2);
+ gizmo_writel(ahb_gizmo[21], AHB_GIZMO_SDMMC3);
+ gizmo_writel(ahb_gizmo[22], AHB_MEM_PREFETCH_CFG_X);
+ gizmo_writel(ahb_gizmo[23], AHB_ARBITRATION_XBAR_CTRL);
+ gizmo_writel(ahb_gizmo[24], AHB_MEM_PREFETCH_CFG3);
+ gizmo_writel(ahb_gizmo[25], AHB_MEM_PREFETCH_CFG4);
+ gizmo_writel(ahb_gizmo[26], AHB_MEM_PREFETCH_CFG1);
+ gizmo_writel(ahb_gizmo[27], AHB_MEM_PREFETCH_CFG2);
+ gizmo_writel(ahb_gizmo[28], AHB_ARBITRATION_AHB_MEM_WRQUE_MST_ID);
+}
+#else
+#define tegra_ahbgizmo_suspend NULL
+#define tegra_ahbgizmo_resume NULL
+#endif
+
+static struct syscore_ops tegra_ahbgizmo_syscore_ops = {
+ .suspend = tegra_ahbgizmo_suspend,
+ .resume = tegra_ahbgizmo_resume,
+};
+
+static int __init tegra_init_ahb_gizmo_settings(void)
+{
+ unsigned long val;
+
+ val = gizmo_readl(AHB_GIZMO_AHB_MEM);
+ val |= ENB_FAST_REARBITRATE | IMMEDIATE | DONT_SPLIT_AHB_WR;
+ gizmo_writel(val, AHB_GIZMO_AHB_MEM);
+
+ val = gizmo_readl(AHB_GIZMO_USB);
+ val |= IMMEDIATE;
+ gizmo_writel(val, AHB_GIZMO_USB);
+
+ val = gizmo_readl(AHB_GIZMO_USB2);
+ val |= IMMEDIATE;
+ gizmo_writel(val, AHB_GIZMO_USB2);
+
+ val = gizmo_readl(AHB_GIZMO_USB3);
+ val |= IMMEDIATE;
+ gizmo_writel(val, AHB_GIZMO_USB3);
+
+ val = gizmo_readl(AHB_ARBITRATION_PRIORITY_CTRL);
+ val |= PRIORITY_SELECT_USB | PRIORITY_SELECT_USB2 | PRIORITY_SELECT_USB3
+ | AHB_PRIORITY_WEIGHT(7);
+ gizmo_writel(val, AHB_ARBITRATION_PRIORITY_CTRL);
+
+ val = gizmo_readl(AHB_MEM_PREFETCH_CFG1);
+ val &= ~MST_ID(~0);
+ val |= PREFETCH_ENB | AHBDMA_MST_ID | ADDR_BNDRY(0xc) | INACTIVITY_TIMEOUT(0x1000);
+ gizmo_writel(val, AHB_MEM_PREFETCH_CFG1);
+
+ val = gizmo_readl(AHB_MEM_PREFETCH_CFG2);
+ val &= ~MST_ID(~0);
+ val |= PREFETCH_ENB | USB_MST_ID | ADDR_BNDRY(0xc) | INACTIVITY_TIMEOUT(0x1000);
+ gizmo_writel(val, AHB_MEM_PREFETCH_CFG2);
+
+ val = gizmo_readl(AHB_MEM_PREFETCH_CFG3);
+ val &= ~MST_ID(~0);
+ val |= PREFETCH_ENB | USB3_MST_ID | ADDR_BNDRY(0xc) | INACTIVITY_TIMEOUT(0x1000);
+ gizmo_writel(val, AHB_MEM_PREFETCH_CFG3);
+
+ val = gizmo_readl(AHB_MEM_PREFETCH_CFG4);
+ val &= ~MST_ID(~0);
+ val |= PREFETCH_ENB | USB2_MST_ID | ADDR_BNDRY(0xc) | INACTIVITY_TIMEOUT(0x1000);
+ gizmo_writel(val, AHB_MEM_PREFETCH_CFG4);
+
+ register_syscore_ops(&tegra_ahbgizmo_syscore_ops);
+
+ return 0;
+}
+postcore_initcall(tegra_init_ahb_gizmo_settings);
diff --git a/arch/arm/mach-tegra/apbio.c b/arch/arm/mach-tegra/apbio.c
new file mode 100644
index 000000000000..41eb0aa3c738
--- /dev/null
+++ b/arch/arm/mach-tegra/apbio.c
@@ -0,0 +1,158 @@
+/*
+ * arch/arm/mach-tegra/apbio.c
+ *
+ * Copyright (C) 2010 NVIDIA Corporation.
+ * Copyright (C) 2010 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/io.h>
+#include <linux/dma-mapping.h>
+#include <linux/spinlock.h>
+#include <linux/completion.h>
+#include <linux/sched.h>
+#include <linux/mutex.h>
+
+#include <mach/dma.h>
+#include <mach/iomap.h>
+
+#include "apbio.h"
+
+static DEFINE_MUTEX(tegra_apb_dma_lock);
+
+#ifdef CONFIG_TEGRA_SYSTEM_DMA
+static struct tegra_dma_channel *tegra_apb_dma;
+static u32 *tegra_apb_bb;
+static dma_addr_t tegra_apb_bb_phys;
+static DECLARE_COMPLETION(tegra_apb_wait);
+
+static void apb_dma_complete(struct tegra_dma_req *req)
+{
+ complete(&tegra_apb_wait);
+}
+
+static inline u32 apb_readl(unsigned long offset)
+{
+ struct tegra_dma_req req;
+ int ret;
+
+ if (!tegra_apb_dma)
+ return readl(IO_TO_VIRT(offset));
+
+ mutex_lock(&tegra_apb_dma_lock);
+ req.complete = apb_dma_complete;
+ req.to_memory = 1;
+ req.dest_addr = tegra_apb_bb_phys;
+ req.dest_bus_width = 32;
+ req.dest_wrap = 1;
+ req.source_addr = offset;
+ req.source_bus_width = 32;
+ req.source_wrap = 4;
+ req.req_sel = 0;
+ req.size = 4;
+
+ INIT_COMPLETION(tegra_apb_wait);
+
+ tegra_dma_enqueue_req(tegra_apb_dma, &req);
+
+ ret = wait_for_completion_timeout(&tegra_apb_wait,
+ msecs_to_jiffies(400));
+
+ if (WARN(ret == 0, "apb read dma timed out")) {
+ tegra_dma_dequeue_req(tegra_apb_dma, &req);
+ *(u32 *)tegra_apb_bb = 0;
+ }
+
+ mutex_unlock(&tegra_apb_dma_lock);
+ return *((u32 *)tegra_apb_bb);
+}
+
+static inline void apb_writel(u32 value, unsigned long offset)
+{
+ struct tegra_dma_req req;
+ int ret;
+
+ if (!tegra_apb_dma) {
+ writel(value, IO_TO_VIRT(offset));
+ return;
+ }
+
+ mutex_lock(&tegra_apb_dma_lock);
+ *((u32 *)tegra_apb_bb) = value;
+ req.complete = apb_dma_complete;
+ req.to_memory = 0;
+ req.dest_addr = offset;
+ req.dest_wrap = 4;
+ req.dest_bus_width = 32;
+ req.source_addr = tegra_apb_bb_phys;
+ req.source_bus_width = 32;
+ req.source_wrap = 1;
+ req.req_sel = 0;
+ req.size = 4;
+
+ INIT_COMPLETION(tegra_apb_wait);
+
+ tegra_dma_enqueue_req(tegra_apb_dma, &req);
+
+ ret = wait_for_completion_timeout(&tegra_apb_wait,
+ msecs_to_jiffies(400));
+
+ if (WARN(ret == 0, "apb write dma timed out"))
+ tegra_dma_dequeue_req(tegra_apb_dma, &req);
+
+ mutex_unlock(&tegra_apb_dma_lock);
+}
+#else
+static inline u32 apb_readl(unsigned long offset)
+{
+ return readl(IO_TO_VIRT(offset));
+}
+
+static inline void apb_writel(u32 value, unsigned long offset)
+{
+ writel(value, IO_TO_VIRT(offset));
+}
+#endif
+
+u32 tegra_apb_readl(unsigned long offset)
+{
+ return apb_readl(offset);
+}
+
+void tegra_apb_writel(u32 value, unsigned long offset)
+{
+ apb_writel(value, offset);
+}
+
+static int tegra_init_apb_dma(void)
+{
+#ifdef CONFIG_TEGRA_SYSTEM_DMA
+ tegra_apb_dma = tegra_dma_allocate_channel(TEGRA_DMA_MODE_ONESHOT |
+ TEGRA_DMA_SHARED, "apbio");
+ if (!tegra_apb_dma) {
+ pr_err("%s: can not allocate dma channel\n", __func__);
+ return -ENODEV;
+ }
+
+ tegra_apb_bb = dma_alloc_coherent(NULL, sizeof(u32),
+ &tegra_apb_bb_phys, GFP_KERNEL);
+ if (!tegra_apb_bb) {
+ pr_err("%s: can not allocate bounce buffer\n", __func__);
+ tegra_dma_free_channel(tegra_apb_dma);
+ tegra_apb_dma = NULL;
+ return -ENOMEM;
+ }
+#endif
+ return 0;
+}
+arch_initcall(tegra_init_apb_dma);
diff --git a/arch/arm/mach-tegra/apbio.h b/arch/arm/mach-tegra/apbio.h
new file mode 100644
index 000000000000..f0c87f06a209
--- /dev/null
+++ b/arch/arm/mach-tegra/apbio.h
@@ -0,0 +1,19 @@
+/*
+ * arch/arm/mach-tegra/apbio.h
+ *
+ * Copyright (C) 2010 NVIDIA Corporation.
+ * Copyright (C) 2010 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.
+ *
+ */
+
+u32 tegra_apb_readl(unsigned long offset);
+void tegra_apb_writel(u32 value, unsigned long offset);
diff --git a/arch/arm/mach-tegra/arb_sema.c b/arch/arm/mach-tegra/arb_sema.c
new file mode 100644
index 000000000000..eecdee5967c8
--- /dev/null
+++ b/arch/arm/mach-tegra/arb_sema.c
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2010, NVIDIA Corporation
+ *
+ * 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/slab.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+#include <linux/mutex.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/completion.h>
+
+#include <mach/arb_sema.h>
+#include <mach/irqs.h>
+#include <mach/iomap.h>
+
+#define TEGRA_RPC_MAX_SEM 32
+
+/* arb_gnt ictrl */
+#define ARB_CPU_INT_EN 0x4
+
+/* arb_sema */
+#define ARB_GRANT_STATUS 0x0
+#define ARB_GRANT_REQUEST 0x4
+#define ARB_GRANT_RELEASE 0x8
+#define ARB_GRANT_PENDING 0xC
+
+struct tegra_arb_dev {
+ void __iomem *sema_base;
+ void __iomem *gnt_base;
+ spinlock_t lock;
+ struct completion arb_gnt_complete[TEGRA_RPC_MAX_SEM];
+ struct mutex mutexes[TEGRA_RPC_MAX_SEM];
+ int irq;
+ int status;
+ bool suspended;
+};
+
+static struct tegra_arb_dev *arb;
+
+static inline u32 arb_sema_read(u32 offset)
+{
+ return readl(arb->sema_base + offset);
+}
+
+static inline void arb_sema_write(u32 value, u32 offset)
+{
+ writel(value, arb->sema_base + offset);
+}
+
+static inline u32 arb_gnt_read(u32 offset)
+{
+ return readl(arb->gnt_base + offset);
+}
+
+static inline void arb_gnt_write(u32 value, u32 offset)
+{
+ writel(value, arb->gnt_base + offset);
+}
+
+static void request_arb_sem(enum tegra_arb_module lock)
+{
+ unsigned long flags;
+ u32 value;
+
+ spin_lock_irqsave(&arb->lock, flags);
+
+ arb_sema_write(1 << lock, ARB_GRANT_REQUEST);
+ value = arb_gnt_read(ARB_CPU_INT_EN);
+ value |= (1 << lock);
+ arb_gnt_write(value, ARB_CPU_INT_EN);
+
+ spin_unlock_irqrestore(&arb->lock, flags);
+}
+
+static void cancel_arb_sem(enum tegra_arb_module lock)
+{
+ unsigned long flags;
+ u32 value;
+
+ spin_lock_irqsave(&arb->lock, flags);
+
+ arb_sema_write(1 << lock, ARB_GRANT_RELEASE);
+ value = arb_gnt_read(ARB_CPU_INT_EN);
+ value &= ~(1 << lock);
+ arb_gnt_write(value, ARB_CPU_INT_EN);
+
+ spin_unlock_irqrestore(&arb->lock, flags);
+}
+
+int tegra_arb_mutex_lock_timeout(enum tegra_arb_module lock, int msecs)
+{
+ int ret;
+
+ if (!arb)
+ return -ENODEV;
+
+ if (arb->suspended) {
+ pr_err("device in suspend\n");
+ return -ETIMEDOUT;
+ }
+
+ mutex_lock(&arb->mutexes[lock]);
+ INIT_COMPLETION(arb->arb_gnt_complete[lock]);
+ request_arb_sem(lock);
+ ret = wait_for_completion_timeout(&arb->arb_gnt_complete[lock], msecs_to_jiffies(msecs));
+ if (ret == 0) {
+ pr_err("timed out. pending:0x%x\n", arb_sema_read(ARB_GRANT_PENDING));
+ cancel_arb_sem(lock);
+ mutex_unlock(&arb->mutexes[lock]);
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(tegra_arb_mutex_lock_timeout);
+
+int tegra_arb_mutex_unlock(enum tegra_arb_module lock)
+{
+ if (!arb)
+ return -ENODEV;
+
+ if (arb->suspended) {
+ pr_err("device in suspend\n");
+ return -ETIMEDOUT;
+ }
+
+ cancel_arb_sem(lock);
+ mutex_unlock(&arb->mutexes[lock]);
+ return 0;
+}
+EXPORT_SYMBOL(tegra_arb_mutex_unlock);
+
+static irqreturn_t arb_gnt_isr(int irq, void *dev_id)
+{
+ struct tegra_arb_dev *dev = dev_id;
+ unsigned long status;
+ u32 cpu_int_en;
+ unsigned int bit;
+ unsigned long flags;
+
+ spin_lock_irqsave(&arb->lock, flags);
+
+ status = arb_sema_read(ARB_GRANT_STATUS);
+ pr_debug("%s: 0x%lx\n", __func__, status);
+
+ /* disable the arb semaphores which were signalled */
+ cpu_int_en = arb_gnt_read(ARB_CPU_INT_EN);
+ arb_gnt_write((cpu_int_en & ~(status & cpu_int_en)),
+ ARB_CPU_INT_EN);
+
+ status &= cpu_int_en;
+ for_each_set_bit(bit, &status, BITS_PER_LONG)
+ complete(&dev->arb_gnt_complete[bit]);
+
+ spin_unlock_irqrestore(&arb->lock, flags);
+ return IRQ_HANDLED;
+}
+
+int tegra_arb_suspend(void)
+{
+ unsigned long status = arb_sema_read(ARB_GRANT_STATUS);
+
+ if (WARN_ON(status != 0)) {
+ pr_err("%s: suspending while holding arbitration "
+ "semaphore: %08lx\n", __func__, status);
+ }
+ arb->suspended = true;
+
+ return status ? -EBUSY : 0;
+}
+
+int tegra_arb_resume(void)
+{
+ arb->suspended = false;
+ return 0;
+}
+
+static int __init tegra_arb_init(void)
+{
+ struct tegra_arb_dev *dev = NULL;
+ int err, i;
+
+ dev = kzalloc(sizeof(struct tegra_arb_dev), GFP_KERNEL);
+ if (dev == NULL) {
+ pr_err("%s: unable to alloc data struct.\n", __func__);
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < TEGRA_RPC_MAX_SEM; i++) {
+ mutex_init(&dev->mutexes[i]);
+ init_completion(&dev->arb_gnt_complete[i]);
+ }
+
+ dev->sema_base = IO_ADDRESS(TEGRA_ARB_SEMA_BASE);
+ if (!dev->sema_base) {
+ pr_err("%s: can't get arb sema_base\n", __func__);
+ err = -ENOMEM;
+ goto out;
+ }
+
+ dev->gnt_base = IO_ADDRESS(TEGRA_ARBGNT_ICTLR_BASE);
+ if (!dev->gnt_base) {
+ pr_err("%s: can't ioremap gnt_base\n", __func__);
+ err = -ENOMEM;
+ goto out;
+ }
+
+ dev->irq = INT_GNT_1;
+ err = request_irq(dev->irq, arb_gnt_isr, 0, "rpc-arbsema", dev);
+ if (err) {
+ pr_err("%s: request_irq(%d) failed(%d)\n", __func__,
+ dev->irq, err);
+ goto out;
+ }
+
+ spin_lock_init(&dev->lock);
+ arb = dev;
+
+ pr_info("%s: initialized\n", __func__);
+ return 0;
+
+out:
+ kfree(dev);
+ pr_err("%s: initialization failed.\n", __func__);
+ return err;
+}
+subsys_initcall(tegra_arb_init);
+
+MODULE_LICENSE("GPLv2");
diff --git a/arch/arm/mach-tegra/asm_macros.h b/arch/arm/mach-tegra/asm_macros.h
new file mode 100644
index 000000000000..2463d797ce39
--- /dev/null
+++ b/arch/arm/mach-tegra/asm_macros.h
@@ -0,0 +1,72 @@
+/*
+ * arch/arm/mach-tegra/include/mach/asm_macros.h
+ *
+ * Copyright (C) 2011 NVIDIA Corporation
+ *
+ * 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 _MACH_TEGRA_ASM_MACROS_H_
+#define _MACH_TEGRA_ASM_MACROS_H_
+
+#ifdef __ASSEMBLY__
+
+/* waits until the microsecond counter (base) ticks, for exact timing loops */
+.macro wait_for_us, rd, base, tmp
+ ldr \rd, [\base]
+1001: ldr \tmp, [\base]
+ cmp \rd, \tmp
+ beq 1001b
+ mov \tmp, \rd
+.endm
+
+/* waits until the microsecond counter (base) is > rn */
+.macro wait_until, rn, base, tmp
+ add \rn, \rn, #1
+1002: ldr \tmp, [\base]
+ sub \tmp, \tmp, \rn
+ ands \tmp, \tmp, #0x80000000
+ dmb
+ bne 1002b
+.endm
+
+/* returns the offset of the flow controller halt register for a cpu */
+.macro cpu_to_halt_reg rd, rcpu
+ cmp \rcpu, #0
+ subne \rd, \rcpu, #1
+ movne \rd, \rd, lsl #3
+ addne \rd, \rd, #0x14
+ moveq \rd, #0
+.endm
+
+/* returns the offset of the flow controller csr register for a cpu */
+.macro cpu_to_csr_reg rd, rcpu
+ cmp \rcpu, #0
+ subne \rd, \rcpu, #1
+ movne \rd, \rd, lsl #3
+ addne \rd, \rd, #0x18
+ moveq \rd, #8
+.endm
+
+/* returns the ID of the current processor */
+.macro cpu_id, rd
+ mrc p15, 0, \rd, c0, c0, 5
+ and \rd, \rd, #0xF
+.endm
+
+/* loads a 32-bit value into a register without a data access */
+.macro mov32, reg, val
+ movw \reg, #:lower16:\val
+ movt \reg, #:upper16:\val
+.endm
+
+#endif
+#endif
diff --git a/arch/arm/mach-tegra/baseband-xmm-power.c b/arch/arm/mach-tegra/baseband-xmm-power.c
new file mode 100644
index 000000000000..26ca73d3c4f5
--- /dev/null
+++ b/arch/arm/mach-tegra/baseband-xmm-power.c
@@ -0,0 +1,895 @@
+/*
+ * arch/arm/mach-tegra/baseband-xmm-power.c
+ *
+ * Copyright (C) 2011 NVIDIA Corporation
+ *
+ * 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/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#include <linux/wakelock.h>
+#include <linux/usb.h>
+#include <mach/usb_phy.h>
+#include "board.h"
+#include "devices.h"
+#include "gpio-names.h"
+#include "baseband-xmm-power.h"
+
+MODULE_LICENSE("GPL");
+
+unsigned long modem_ver = XMM_MODEM_VER_1121;
+EXPORT_SYMBOL(modem_ver);
+
+unsigned long modem_flash;
+EXPORT_SYMBOL(modem_flash);
+
+unsigned long modem_pm = 1;
+EXPORT_SYMBOL(modem_pm);
+
+unsigned long enum_delay_ms = 1000; /* ignored if !modem_flash */
+
+module_param(modem_ver, ulong, 0644);
+MODULE_PARM_DESC(modem_ver,
+ "baseband xmm power - modem software version");
+module_param(modem_flash, ulong, 0644);
+MODULE_PARM_DESC(modem_flash,
+ "baseband xmm power - modem flash (1 = flash, 0 = flashless)");
+module_param(modem_pm, ulong, 0644);
+MODULE_PARM_DESC(modem_pm,
+ "baseband xmm power - modem power management (1 = pm, 0 = no pm)");
+module_param(enum_delay_ms, ulong, 0644);
+MODULE_PARM_DESC(enum_delay_ms,
+ "baseband xmm power - delay in ms between modem on and enumeration");
+
+static struct usb_device_id xmm_pm_ids[] = {
+ { USB_DEVICE(VENDOR_ID, PRODUCT_ID),
+ .driver_info = 0 },
+ {}
+};
+
+static struct gpio tegra_baseband_gpios[] = {
+ { -1, GPIOF_OUT_INIT_LOW, "BB_RSTn" },
+ { -1, GPIOF_OUT_INIT_LOW, "BB_ON" },
+ { -1, GPIOF_OUT_INIT_LOW, "IPC_BB_WAKE" },
+ { -1, GPIOF_IN, "IPC_AP_WAKE" },
+ { -1, GPIOF_OUT_INIT_HIGH, "IPC_HSIC_ACTIVE" },
+ { -1, GPIOF_IN, "IPC_HSIC_SUS_REQ" },
+};
+
+static enum {
+ IPC_AP_WAKE_UNINIT,
+ IPC_AP_WAKE_IRQ_READY,
+ IPC_AP_WAKE_INIT1,
+ IPC_AP_WAKE_INIT2,
+ IPC_AP_WAKE_L,
+ IPC_AP_WAKE_H,
+} ipc_ap_wake_state;
+
+enum baseband_xmm_powerstate_t baseband_xmm_powerstate;
+static struct workqueue_struct *workqueue;
+static struct work_struct init1_work;
+static struct work_struct init2_work;
+static struct work_struct L2_resume_work;
+static struct baseband_power_platform_data *baseband_power_driver_data;
+static bool register_hsic_device;
+static struct wake_lock wakelock;
+static struct usb_device *usbdev;
+static bool CP_initiated_L2toL0;
+static bool modem_power_on;
+static int power_onoff;
+static void baseband_xmm_power_L2_resume(void);
+
+static int baseband_modem_power_on(struct baseband_power_platform_data *data)
+{
+ /* set IPC_HSIC_ACTIVE active */
+ gpio_set_value(baseband_power_driver_data->
+ modem.xmm.ipc_hsic_active, 1);
+
+ /* wait 20 ms */
+ mdelay(20);
+
+ /* reset / power on sequence */
+ mdelay(40);
+ gpio_set_value(data->modem.xmm.bb_rst, 1);
+ mdelay(1);
+ gpio_set_value(data->modem.xmm.bb_on, 1);
+ udelay(40);
+ gpio_set_value(data->modem.xmm.bb_on, 0);
+
+ return 0;
+}
+
+static int baseband_xmm_power_on(struct platform_device *device)
+{
+ struct baseband_power_platform_data *data
+ = (struct baseband_power_platform_data *)
+ device->dev.platform_data;
+ pr_debug("%s {\n", __func__);
+
+ /* check for platform data */
+ if (!data) {
+ pr_err("%s: !data\n", __func__);
+ return -EINVAL;
+ }
+
+ /* reset the state machine */
+ baseband_xmm_powerstate = BBXMM_PS_INIT;
+ if (modem_ver < XMM_MODEM_VER_1130)
+ ipc_ap_wake_state = IPC_AP_WAKE_INIT1;
+ else
+ ipc_ap_wake_state = IPC_AP_WAKE_INIT2;
+
+ pr_debug("%s - %d\n", __func__, __LINE__);
+
+ /* register / turn on usb host controller */
+ if (!modem_flash) {
+ pr_debug("%s - %d\n", __func__, __LINE__);
+ /* register usb host controller only once */
+ if (register_hsic_device) {
+ pr_debug("%s: register usb host controller\n",
+ __func__);
+ modem_power_on = true;
+ platform_device_register(data->modem.xmm.hsic_device);
+ register_hsic_device = false;
+ } else {
+ /* turn on ehci controller */
+ mm_segment_t oldfs;
+ struct file *filp;
+ pr_debug("%s: register usb host controller echo on\n",
+ __func__);
+ oldfs = get_fs();
+ set_fs(KERNEL_DS);
+ filp = filp_open(TEGRA_EHCI_DEVICE, O_RDWR, 0);
+ if (IS_ERR(filp) || (filp == NULL)) {
+ pr_err("open ehci_power failed\n");
+ } else {
+ filp->f_op->write(filp, "1", 1, &filp->f_pos);
+ filp_close(filp, NULL);
+ }
+ set_fs(oldfs);
+ /* turn on modem */
+ pr_debug("%s call baseband_modem_power_on\n", __func__);
+ baseband_modem_power_on(data);
+ }
+ }
+
+ pr_debug("%s }\n", __func__);
+
+ return 0;
+}
+
+static int baseband_xmm_power_off(struct platform_device *device)
+{
+ struct baseband_power_platform_data *data;
+
+ pr_debug("%s {\n", __func__);
+
+ /* check for device / platform data */
+ if (!device) {
+ pr_err("%s: !device\n", __func__);
+ return -EINVAL;
+ }
+ data = (struct baseband_power_platform_data *)
+ device->dev.platform_data;
+ if (!data) {
+ pr_err("%s: !data\n", __func__);
+ return -EINVAL;
+ }
+
+ /* turn off usb host controller */
+ {
+ mm_segment_t oldfs;
+ struct file *filp;
+ oldfs = get_fs();
+ set_fs(KERNEL_DS);
+ filp = filp_open(TEGRA_EHCI_DEVICE, O_RDWR, 0);
+ if (IS_ERR(filp) || (filp == NULL)) {
+ pr_err("open ehci_power failed\n");
+ } else {
+ filp->f_op->write(filp, "0", 1, &filp->f_pos);
+ filp_close(filp, NULL);
+ }
+ set_fs(oldfs);
+ }
+
+ /* set IPC_HSIC_ACTIVE low */
+ gpio_set_value(baseband_power_driver_data->
+ modem.xmm.ipc_hsic_active, 0);
+
+ /* wait 20 ms */
+ mdelay(20);
+
+ /* drive bb_rst low */
+ gpio_set_value(data->modem.xmm.bb_rst, 0);
+ mdelay(1);
+
+ pr_debug("%s }\n", __func__);
+
+ return 0;
+}
+
+static ssize_t baseband_xmm_onoff(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int size;
+ struct platform_device *device = to_platform_device(dev);
+
+ pr_debug("%s\n", __func__);
+
+ /* check input */
+ if (buf == NULL) {
+ pr_err("%s: buf NULL\n", __func__);
+ return -EINVAL;
+ }
+ pr_debug("%s: count=%d\n", __func__, count);
+
+ /* parse input */
+ size = sscanf(buf, "%d", &power_onoff);
+ if (size != 1) {
+ pr_err("%s: size=%d -EINVAL\n", __func__, size);
+ return -EINVAL;
+ }
+ pr_debug("%s power_onoff=%d\n", __func__, power_onoff);
+
+ if (power_onoff == 0)
+ baseband_xmm_power_off(device);
+ else if (power_onoff == 1)
+ baseband_xmm_power_on(device);
+ return count;
+}
+
+static DEVICE_ATTR(xmm_onoff, S_IRUSR | S_IWUSR | S_IRGRP,
+ NULL, baseband_xmm_onoff);
+
+void baseband_xmm_set_power_status(unsigned int status)
+{
+ struct baseband_power_platform_data *data = baseband_power_driver_data;
+ int value = 0;
+
+ pr_debug("%s\n", __func__);
+
+ if (baseband_xmm_powerstate == status)
+ return;
+
+ switch (status) {
+ case BBXMM_PS_L0:
+ pr_info("L0\n");
+ value = gpio_get_value(data->modem.xmm.ipc_hsic_active);
+ pr_debug("before L0 ipc_hsic_active=%d\n", value);
+ if (!value) {
+ pr_debug("before L0 gpio set ipc_hsic_active=1 ->\n");
+ gpio_set_value(data->modem.xmm.ipc_hsic_active, 1);
+ }
+ if (modem_power_on) {
+ modem_power_on = false;
+ baseband_modem_power_on(data);
+ }
+ wake_lock(&wakelock);
+ pr_debug("gpio host active high->\n");
+ break;
+ case BBXMM_PS_L2:
+ pr_info("L2\n");
+ wake_unlock(&wakelock);
+ break;
+ case BBXMM_PS_L3:
+ pr_info("L3\n");
+ if (wake_lock_active(&wakelock)) {
+ pr_info("%s: releasing wakelock before L3\n",
+ __func__);
+ wake_unlock(&wakelock);
+ }
+ gpio_set_value(data->modem.xmm.ipc_hsic_active, 0);
+ pr_debug("gpio host active low->\n");
+ break;
+ case BBXMM_PS_L2TOL0:
+ /* do this only from L2 state */
+ if (baseband_xmm_powerstate == BBXMM_PS_L2) {
+ baseband_xmm_powerstate = status;
+ pr_debug("BB XMM POWER STATE = %d\n", status);
+ baseband_xmm_power_L2_resume();
+ }
+ default:
+ break;
+ }
+ baseband_xmm_powerstate = status;
+ pr_debug("BB XMM POWER STATE = %d\n", status);
+}
+EXPORT_SYMBOL_GPL(baseband_xmm_set_power_status);
+
+
+irqreturn_t baseband_xmm_power_ipc_ap_wake_irq(int irq, void *dev_id)
+{
+ int value;
+
+ pr_debug("%s\n", __func__);
+
+ if (ipc_ap_wake_state < IPC_AP_WAKE_IRQ_READY) {
+ pr_err("%s - spurious irq\n", __func__);
+ } else if (ipc_ap_wake_state == IPC_AP_WAKE_IRQ_READY) {
+ value = gpio_get_value(baseband_power_driver_data->
+ modem.xmm.ipc_ap_wake);
+ if (!value) {
+ pr_debug("%s - IPC_AP_WAKE_INIT1"
+ " - got falling edge\n",
+ __func__);
+ /* go to IPC_AP_WAKE_INIT1 state */
+ ipc_ap_wake_state = IPC_AP_WAKE_INIT1;
+ /* queue work */
+ queue_work(workqueue, &init1_work);
+ } else {
+ pr_debug("%s - IPC_AP_WAKE_INIT1"
+ " - wait for falling edge\n",
+ __func__);
+ }
+ } else if (ipc_ap_wake_state == IPC_AP_WAKE_INIT1) {
+ value = gpio_get_value(baseband_power_driver_data->
+ modem.xmm.ipc_ap_wake);
+ if (!value) {
+ pr_debug("%s - IPC_AP_WAKE_INIT2"
+ " - wait for rising edge\n",
+ __func__);
+ } else {
+ pr_debug("%s - IPC_AP_WAKE_INIT2"
+ " - got rising edge\n",
+ __func__);
+ /* go to IPC_AP_WAKE_INIT2 state */
+ ipc_ap_wake_state = IPC_AP_WAKE_INIT2;
+ /* queue work */
+ queue_work(workqueue, &init2_work);
+ }
+ } else {
+ value = gpio_get_value(baseband_power_driver_data->
+ modem.xmm.ipc_ap_wake);
+ if (!value) {
+ pr_debug("%s - falling\n", __func__);
+ /* [ver < 1130] gpio protocol falling edge */
+ if (modem_ver < XMM_MODEM_VER_1130) {
+ pr_debug("gpio host wakeup done <-\n");
+ value = gpio_get_value
+ (baseband_power_driver_data->
+ modem.xmm.ipc_bb_wake);
+ if (value) {
+ /* Clear the slave wakeup request */
+ gpio_set_value
+ (baseband_power_driver_data->
+ modem.xmm.ipc_bb_wake, 0);
+ pr_debug("gpio slave wakeup done ->\n");
+ }
+ }
+ /* [ver >= 1130] gpio protocol falling edge */
+ if (modem_ver >= XMM_MODEM_VER_1130) {
+ if (baseband_xmm_powerstate == BBXMM_PS_L2) {
+ CP_initiated_L2toL0 = true;
+ baseband_xmm_set_power_status
+ (BBXMM_PS_L2TOL0);
+ }
+ }
+ /* save gpio state */
+ ipc_ap_wake_state = IPC_AP_WAKE_L;
+ } else {
+ pr_debug("%s - rising\n", __func__);
+ /* [ver >= 1130] gpio protocol rising edge */
+ if (modem_ver >= XMM_MODEM_VER_1130) {
+ pr_debug("gpio host wakeup done <-\n");
+ value = gpio_get_value
+ (baseband_power_driver_data->
+ modem.xmm.ipc_bb_wake);
+ if (value) {
+ /* Clear the slave wakeup request */
+ gpio_set_value
+ (baseband_power_driver_data->
+ modem.xmm.ipc_bb_wake, 0);
+ pr_debug("gpio slave wakeup done ->\n");
+ }
+ baseband_xmm_set_power_status(BBXMM_PS_L0);
+ }
+ /* save gpio state */
+ ipc_ap_wake_state = IPC_AP_WAKE_H;
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+EXPORT_SYMBOL(baseband_xmm_power_ipc_ap_wake_irq);
+
+static void baseband_xmm_power_init1_work(struct work_struct *work)
+{
+ int value;
+
+ pr_debug("%s {\n", __func__);
+
+ /* check if IPC_HSIC_ACTIVE high */
+ value = gpio_get_value(baseband_power_driver_data->
+ modem.xmm.ipc_hsic_active);
+ if (value != 1) {
+ pr_err("%s - expected IPC_HSIC_ACTIVE high!\n", __func__);
+ return;
+ }
+
+ /* wait 100 ms */
+ mdelay(100);
+
+ /* set IPC_HSIC_ACTIVE low */
+ gpio_set_value(baseband_power_driver_data->
+ modem.xmm.ipc_hsic_active, 0);
+
+ /* wait 10 ms */
+ mdelay(10);
+
+ /* set IPC_HSIC_ACTIVE high */
+ gpio_set_value(baseband_power_driver_data->
+ modem.xmm.ipc_hsic_active, 1);
+
+ /* wait 20 ms */
+ mdelay(20);
+
+ pr_debug("%s }\n", __func__);
+}
+
+static void baseband_xmm_power_init2_work(struct work_struct *work)
+{
+ struct baseband_power_platform_data *data = baseband_power_driver_data;
+
+ pr_debug("%s\n", __func__);
+
+ /* check input */
+ if (!data)
+ return;
+
+ /* register usb host controller only once */
+ if (register_hsic_device) {
+ platform_device_register(data->modem.xmm.hsic_device);
+ register_hsic_device = false;
+ }
+
+}
+
+/* Do the work for AP/CP initiated L2->L0 */
+static void baseband_xmm_power_L2_resume(void)
+{
+ struct baseband_power_platform_data *data = baseband_power_driver_data;
+ int value;
+ int delay = 10000; /* maxmum delay in msec */
+
+ pr_debug("%s\n", __func__);
+
+ if (!baseband_power_driver_data)
+ return;
+ if (CP_initiated_L2toL0) {
+ pr_info("CP L2->L0\n");
+ CP_initiated_L2toL0 = false;
+ queue_work(workqueue, &L2_resume_work);
+ } else {
+ /* set the slave wakeup request */
+ pr_info("AP L2->L0\n");
+ gpio_set_value(data->modem.xmm.ipc_bb_wake, 1);
+ pr_debug("waiting for host wakeup from CP...\n");
+ do {
+ mdelay(1);
+ value = gpio_get_value(data->modem.xmm.ipc_ap_wake);
+ delay--;
+ } while ((value) && (delay));
+ if (delay)
+ pr_debug("gpio host wakeup low <-\n");
+ else
+ pr_info("!!AP L2->L0 Failed\n");
+ }
+}
+
+/* Do the work for CP initiated L2->L0 */
+static void baseband_xmm_power_L2_resume_work(struct work_struct *work)
+{
+ struct usb_interface *intf;
+
+ pr_debug("%s {\n", __func__);
+
+ if (!usbdev)
+ return;
+ usb_lock_device(usbdev);
+ intf = usb_ifnum_to_if(usbdev, 0);
+ if (usb_autopm_get_interface(intf) == 0)
+ usb_autopm_put_interface(intf);
+ usb_unlock_device(usbdev);
+
+ pr_debug("} %s\n", __func__);
+}
+
+static void baseband_xmm_power_reset_on(void)
+{
+ /* reset / power on sequence */
+ mdelay(40);
+ gpio_set_value(baseband_power_driver_data->modem.xmm.bb_rst, 1);
+ mdelay(1);
+ gpio_set_value(baseband_power_driver_data->modem.xmm.bb_on, 1);
+ udelay(40);
+ gpio_set_value(baseband_power_driver_data->modem.xmm.bb_on, 0);
+}
+
+static struct baseband_xmm_power_work_t *baseband_xmm_power_work;
+
+static void baseband_xmm_power_work_func(struct work_struct *work)
+{
+ struct baseband_xmm_power_work_t *bbxmm_work
+ = (struct baseband_xmm_power_work_t *) work;
+
+ pr_debug("%s\n", __func__);
+
+ switch (bbxmm_work->state) {
+ case BBXMM_WORK_UNINIT:
+ pr_debug("BBXMM_WORK_UNINIT\n");
+ break;
+ case BBXMM_WORK_INIT:
+ pr_debug("BBXMM_WORK_INIT\n");
+ /* go to next state */
+ bbxmm_work->state = (modem_flash && !modem_pm)
+ ? BBXMM_WORK_INIT_FLASH_STEP1
+ : (modem_flash && modem_pm)
+ ? BBXMM_WORK_INIT_FLASH_PM_STEP1
+ : (!modem_flash && modem_pm)
+ ? BBXMM_WORK_INIT_FLASHLESS_PM_STEP1
+ : BBXMM_WORK_UNINIT;
+ pr_debug("Go to next state %d\n", bbxmm_work->state);
+ queue_work(workqueue, work);
+ break;
+ case BBXMM_WORK_INIT_FLASH_STEP1:
+ pr_debug("BBXMM_WORK_INIT_FLASH_STEP1\n");
+ /* register usb host controller */
+ pr_debug("%s: register usb host controller\n", __func__);
+ platform_device_register(baseband_power_driver_data->modem
+ .xmm.hsic_device);
+ break;
+ case BBXMM_WORK_INIT_FLASH_PM_STEP1:
+ pr_debug("BBXMM_WORK_INIT_FLASH_PM_STEP1\n");
+ /* [modem ver >= 1130] start with IPC_HSIC_ACTIVE low */
+ if (modem_ver >= XMM_MODEM_VER_1130) {
+ pr_debug("%s: ver > 1130:"
+ " ipc_hsic_active -> 0\n", __func__);
+ gpio_set_value(baseband_power_driver_data->
+ modem.xmm.ipc_hsic_active, 0);
+ }
+ /* reset / power on sequence */
+ baseband_xmm_power_reset_on();
+ /* optional delay
+ * 0 = flashless
+ * ==> causes next step to enumerate modem boot rom
+ * (058b / 0041)
+ * some delay > boot rom timeout
+ * ==> causes next step to enumerate modem software
+ * (1519 / 0020)
+ * (requires modem to be flash version, not flashless
+ * version)
+ */
+ if (enum_delay_ms)
+ mdelay(enum_delay_ms);
+ /* register usb host controller */
+ pr_debug("%s: register usb host controller\n", __func__);
+ platform_device_register(baseband_power_driver_data->modem
+ .xmm.hsic_device);
+ /* go to next state */
+ bbxmm_work->state = (modem_ver < XMM_MODEM_VER_1130)
+ ? BBXMM_WORK_INIT_FLASH_PM_VER_LT_1130_STEP1
+ : BBXMM_WORK_INIT_FLASH_PM_VER_GE_1130_STEP1;
+ queue_work(workqueue, work);
+ pr_debug("Go to next state %d\n", bbxmm_work->state);
+ break;
+ case BBXMM_WORK_INIT_FLASH_PM_VER_LT_1130_STEP1:
+ pr_debug("BBXMM_WORK_INIT_FLASH_PM_VER_LT_1130_STEP1\n");
+ break;
+ case BBXMM_WORK_INIT_FLASH_PM_VER_GE_1130_STEP1:
+ pr_debug("BBXMM_WORK_INIT_FLASH_PM_VER_GE_1130_STEP1\n");
+ break;
+ case BBXMM_WORK_INIT_FLASHLESS_PM_STEP1:
+ pr_debug("BBXMM_WORK_INIT_FLASHLESS_PM_STEP1\n");
+ /* go to next state */
+ bbxmm_work->state = (modem_ver < XMM_MODEM_VER_1130)
+ ? BBXMM_WORK_INIT_FLASHLESS_PM_VER_LT_1130_WAIT_IRQ
+ : BBXMM_WORK_INIT_FLASHLESS_PM_VER_GE_1130_STEP1;
+ queue_work(workqueue, work);
+ break;
+ case BBXMM_WORK_INIT_FLASHLESS_PM_VER_LT_1130_STEP1:
+ pr_debug("BBXMM_WORK_INIT_FLASHLESS_PM_VER_LT_1130_STEP1\n");
+ break;
+ case BBXMM_WORK_INIT_FLASHLESS_PM_VER_GE_1130_STEP1:
+ pr_debug("BBXMM_WORK_INIT_FLASHLESS_PM_VER_GE_1130_STEP1\n");
+ break;
+ default:
+ break;
+ }
+
+}
+
+static void baseband_xmm_device_add_handler(struct usb_device *udev)
+{
+ struct usb_interface *intf = usb_ifnum_to_if(udev, 0);
+ const struct usb_device_id *id = usb_match_id(intf, xmm_pm_ids);
+
+ if (id) {
+ pr_debug("persist_enabled: %u\n", udev->persist_enabled);
+ pr_info("Add device %d <%s %s>\n", udev->devnum,
+ udev->manufacturer, udev->product);
+ usbdev = udev;
+ usb_enable_autosuspend(udev);
+ pr_info("enable autosuspend\n");
+ }
+}
+
+static void baseband_xmm_device_remove_handler(struct usb_device *udev)
+{
+ if (usbdev == udev) {
+ pr_info("Remove device %d <%s %s>\n", udev->devnum,
+ udev->manufacturer, udev->product);
+ usbdev = 0;
+ }
+
+}
+
+static int usb_xmm_notify(struct notifier_block *self, unsigned long action,
+ void *blob)
+{
+ switch (action) {
+ case USB_DEVICE_ADD:
+ baseband_xmm_device_add_handler(blob);
+ break;
+ case USB_DEVICE_REMOVE:
+ baseband_xmm_device_remove_handler(blob);
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+
+
+static struct notifier_block usb_xmm_nb = {
+ .notifier_call = usb_xmm_notify,
+};
+
+static int baseband_xmm_power_driver_probe(struct platform_device *device)
+{
+ struct baseband_power_platform_data *data
+ = (struct baseband_power_platform_data *)
+ device->dev.platform_data;
+ struct device *dev = &device->dev;
+ int err;
+
+ pr_debug("%s\n", __func__);
+ pr_debug("[XMM] enum_delay_ms=%d\n", enum_delay_ms);
+
+ /* check for platform data */
+ if (!data)
+ return -ENODEV;
+
+ /* check if supported modem */
+ if (data->baseband_type != BASEBAND_XMM) {
+ pr_err("unsuppported modem\n");
+ return -ENODEV;
+ }
+
+ /* save platform data */
+ baseband_power_driver_data = data;
+
+ /* create device file */
+ err = device_create_file(dev, &dev_attr_xmm_onoff);
+ if (err < 0) {
+ pr_err("%s - device_create_file failed\n", __func__);
+ return -ENODEV;
+ }
+
+ /* init wake lock */
+ wake_lock_init(&wakelock, WAKE_LOCK_SUSPEND, "baseband_xmm_power");
+
+ /* request baseband gpio(s) */
+ tegra_baseband_gpios[0].gpio = baseband_power_driver_data
+ ->modem.xmm.bb_rst;
+ tegra_baseband_gpios[1].gpio = baseband_power_driver_data
+ ->modem.xmm.bb_on;
+ tegra_baseband_gpios[2].gpio = baseband_power_driver_data
+ ->modem.xmm.ipc_bb_wake;
+ tegra_baseband_gpios[3].gpio = baseband_power_driver_data
+ ->modem.xmm.ipc_ap_wake;
+ tegra_baseband_gpios[4].gpio = baseband_power_driver_data
+ ->modem.xmm.ipc_hsic_active;
+ tegra_baseband_gpios[5].gpio = baseband_power_driver_data
+ ->modem.xmm.ipc_hsic_sus_req;
+ err = gpio_request_array(tegra_baseband_gpios,
+ ARRAY_SIZE(tegra_baseband_gpios));
+ if (err < 0) {
+ pr_err("%s - request gpio(s) failed\n", __func__);
+ return -ENODEV;
+ }
+
+ /* request baseband irq(s) */
+ if (modem_flash && modem_pm) {
+ pr_debug("%s: request_irq IPC_AP_WAKE_IRQ\n", __func__);
+ ipc_ap_wake_state = IPC_AP_WAKE_UNINIT;
+ err = request_irq(gpio_to_irq(data->modem.xmm.ipc_ap_wake),
+ baseband_xmm_power_ipc_ap_wake_irq,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ "IPC_AP_WAKE_IRQ",
+ NULL);
+ if (err < 0) {
+ pr_err("%s - request irq IPC_AP_WAKE_IRQ failed\n",
+ __func__);
+ return err;
+ }
+ ipc_ap_wake_state = IPC_AP_WAKE_IRQ_READY;
+ if (modem_ver >= XMM_MODEM_VER_1130) {
+ pr_debug("%s: ver > 1130: AP_WAKE_INIT1\n", __func__);
+ /* ver 1130 or later starts in INIT1 state */
+ ipc_ap_wake_state = IPC_AP_WAKE_INIT1;
+ }
+ }
+
+ /* init work queue */
+ workqueue = create_singlethread_workqueue
+ ("baseband_xmm_power_workqueue");
+ if (!workqueue) {
+ pr_err("cannot create workqueue\n");
+ return -1;
+ }
+ baseband_xmm_power_work = (struct baseband_xmm_power_work_t *)
+ kmalloc(sizeof(struct baseband_xmm_power_work_t), GFP_KERNEL);
+ if (!baseband_xmm_power_work) {
+ pr_err("cannot allocate baseband_xmm_power_work\n");
+ return -1;
+ }
+ INIT_WORK((struct work_struct *) baseband_xmm_power_work,
+ baseband_xmm_power_work_func);
+ baseband_xmm_power_work->state = BBXMM_WORK_INIT;
+ queue_work(workqueue,
+ (struct work_struct *) baseband_xmm_power_work);
+
+ /* init work objects */
+ INIT_WORK(&init1_work, baseband_xmm_power_init1_work);
+ INIT_WORK(&init2_work, baseband_xmm_power_init2_work);
+ INIT_WORK(&L2_resume_work, baseband_xmm_power_L2_resume_work);
+
+ /* init state variables */
+ register_hsic_device = true;
+ baseband_xmm_powerstate = BBXMM_PS_UNINIT;
+ CP_initiated_L2toL0 = false;
+
+ usb_register_notify(&usb_xmm_nb);
+
+ pr_debug("%s }\n", __func__);
+ return 0;
+}
+
+static int baseband_xmm_power_driver_remove(struct platform_device *device)
+{
+ struct baseband_power_platform_data *data
+ = (struct baseband_power_platform_data *)
+ device->dev.platform_data;
+ struct device *dev = &device->dev;
+
+ pr_debug("%s\n", __func__);
+
+ /* check for platform data */
+ if (!data)
+ return 0;
+
+ usb_unregister_notify(&usb_xmm_nb);
+
+ /* free work structure */
+ kfree(baseband_xmm_power_work);
+ baseband_xmm_power_work = (struct baseband_xmm_power_work_t *) 0;
+
+ /* free baseband irq(s) */
+ if (modem_flash && modem_pm) {
+ free_irq(gpio_to_irq(baseband_power_driver_data
+ ->modem.xmm.ipc_ap_wake), NULL);
+ }
+
+ /* free baseband gpio(s) */
+ gpio_free_array(tegra_baseband_gpios,
+ ARRAY_SIZE(tegra_baseband_gpios));
+
+ /* destroy wake lock */
+ wake_lock_destroy(&wakelock);
+
+ /* delete device file */
+ device_remove_file(dev, &dev_attr_xmm_onoff);
+
+ /* unregister usb host controller */
+ platform_device_unregister(data->modem.xmm.hsic_device);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int baseband_xmm_power_driver_suspend(struct platform_device *device,
+ pm_message_t state)
+{
+ pr_debug("%s\n", __func__);
+ return 0;
+}
+
+static int baseband_xmm_power_driver_resume(struct platform_device *device)
+{
+ struct baseband_power_platform_data *data
+ = (struct baseband_power_platform_data *)
+ device->dev.platform_data;
+ int value;
+ int delay = 10000; /* maxmum delay in msec */
+
+ pr_debug("%s\n", __func__);
+
+ /* check for platform data */
+ if (!baseband_power_driver_data)
+ return 0;
+
+ /* check if modem is on */
+ if (power_onoff == 0) {
+ pr_debug("%s - flight mode - nop\n", __func__);
+ baseband_xmm_set_power_status(BBXMM_PS_L3TOL0);
+ return 0;
+ }
+
+ /* L3->L0 */
+ baseband_xmm_set_power_status(BBXMM_PS_L3TOL0);
+ value = gpio_get_value(data->modem.xmm.ipc_ap_wake);
+ if (value) {
+ pr_info("AP L3 -> L0\n");
+ /* wake bb */
+ gpio_set_value(data->modem.xmm.ipc_bb_wake, 1);
+
+ pr_debug("waiting for host wakeup...\n");
+ do {
+ mdelay(1);
+ value = gpio_get_value(data->modem.xmm.ipc_ap_wake);
+ delay--;
+ } while ((value) && (delay));
+ if (delay)
+ pr_debug("gpio host wakeup low <-\n");
+ } else {
+ pr_info("CP L3 -> L0\n");
+ }
+
+ return 0;
+}
+#endif
+
+static struct platform_driver baseband_power_driver = {
+ .probe = baseband_xmm_power_driver_probe,
+ .remove = baseband_xmm_power_driver_remove,
+#ifdef CONFIG_PM
+ .suspend = baseband_xmm_power_driver_suspend,
+ .resume = baseband_xmm_power_driver_resume,
+#endif
+ .driver = {
+ .name = "baseband_xmm_power",
+ },
+};
+
+static int __init baseband_xmm_power_init(void)
+{
+ return platform_driver_register(&baseband_power_driver);
+}
+
+static void __exit baseband_xmm_power_exit(void)
+{
+ pr_debug("%s\n", __func__);
+ platform_driver_unregister(&baseband_power_driver);
+}
+
+module_init(baseband_xmm_power_init)
+module_exit(baseband_xmm_power_exit)
diff --git a/arch/arm/mach-tegra/baseband-xmm-power.h b/arch/arm/mach-tegra/baseband-xmm-power.h
new file mode 100644
index 000000000000..0445714a0acc
--- /dev/null
+++ b/arch/arm/mach-tegra/baseband-xmm-power.h
@@ -0,0 +1,109 @@
+/*
+ * arch/arm/mach-tegra/baseband-xmm-power.h
+ *
+ * Copyright (C) 2011 NVIDIA Corporation
+ *
+ * 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 BASEBAND_XMM_POWER_H
+#define BASREBAND_XMM_POWER_H
+
+#include <linux/pm.h>
+#include <linux/suspend.h>
+
+#define VENDOR_ID 0x1519
+#define PRODUCT_ID 0x0020
+#define TEGRA_EHCI_DEVICE "/sys/devices/platform/tegra-ehci.1/ehci_power"
+
+#define XMM_MODEM_VER_1121 0x1121
+#define XMM_MODEM_VER_1130 0x1130
+
+/* shared between baseband-xmm-* modules so they can agree on same
+ * modem configuration
+ */
+extern unsigned long modem_ver;
+extern unsigned long modem_flash;
+extern unsigned long modem_pm;
+
+enum baseband_type {
+ BASEBAND_XMM,
+};
+
+struct baseband_power_platform_data {
+ enum baseband_type baseband_type;
+ union {
+ struct {
+ int mdm_reset;
+ int mdm_on;
+ int ap2mdm_ack;
+ int mdm2ap_ack;
+ int ap2mdm_ack2;
+ int mdm2ap_ack2;
+ struct platform_device *device;
+ } generic;
+ struct {
+ int bb_rst;
+ int bb_on;
+ int ipc_bb_wake;
+ int ipc_ap_wake;
+ int ipc_hsic_active;
+ int ipc_hsic_sus_req;
+ struct platform_device *hsic_device;
+ } xmm;
+ } modem;
+};
+
+enum baseband_xmm_power_work_state_t {
+ BBXMM_WORK_UNINIT,
+ BBXMM_WORK_INIT,
+ /* initialize flash modem */
+ BBXMM_WORK_INIT_FLASH_STEP1,
+ /* initialize flash (with power management support) modem */
+ BBXMM_WORK_INIT_FLASH_PM_STEP1,
+ BBXMM_WORK_INIT_FLASH_PM_VER_LT_1130_STEP1,
+ BBXMM_WORK_INIT_FLASH_PM_VER_GE_1130_STEP1,
+ /* initialize flashless (with power management support) modem */
+ BBXMM_WORK_INIT_FLASHLESS_PM_STEP1,
+ BBXMM_WORK_INIT_FLASHLESS_PM_VER_LT_1130_WAIT_IRQ,
+ BBXMM_WORK_INIT_FLASHLESS_PM_VER_LT_1130_STEP1,
+ BBXMM_WORK_INIT_FLASHLESS_PM_VER_LT_1130_STEP2,
+ BBXMM_WORK_INIT_FLASHLESS_PM_VER_GE_1130_STEP1,
+ BBXMM_WORK_INIT_FLASHLESS_PM_VER_GE_1130_STEP2,
+ BBXMM_WORK_INIT_FLASHLESS_PM_VER_GE_1130_STEP3,
+ BBXMM_WORK_INIT_FLASHLESS_PM_VER_GE_1130_STEP4,
+};
+
+struct baseband_xmm_power_work_t {
+ /* work structure must be first structure member */
+ struct work_struct work;
+ /* xmm modem state */
+ enum baseband_xmm_power_work_state_t state;
+};
+
+enum baseband_xmm_powerstate_t {
+ BBXMM_PS_UNINIT = 0,
+ BBXMM_PS_INIT = 1,
+ BBXMM_PS_L0 = 2,
+ BBXMM_PS_L0TOL2 = 3,
+ BBXMM_PS_L2 = 4,
+ BBXMM_PS_L2TOL0 = 5,
+ BBXMM_PS_L2TOL3 = 6,
+ BBXMM_PS_L3 = 7,
+ BBXMM_PS_L3TOL0 = 8,
+ BBXMM_PS_LAST = -1,
+};
+
+irqreturn_t baseband_xmm_power_ipc_ap_wake_irq(int irq, void *dev_id);
+
+void baseband_xmm_set_power_status(unsigned int status);
+
+#endif /* BASREBAND_XMM_POWER_H */
diff --git a/arch/arm/mach-tegra/baseband-xmm-power2.c b/arch/arm/mach-tegra/baseband-xmm-power2.c
new file mode 100644
index 000000000000..bdb3fe3cfba3
--- /dev/null
+++ b/arch/arm/mach-tegra/baseband-xmm-power2.c
@@ -0,0 +1,707 @@
+/*
+ * arch/arm/mach-tegra/baseband-xmm-power2.c
+ *
+ * Copyright (C) 2011 NVIDIA Corporation
+ *
+ * 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/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#include <linux/wakelock.h>
+#include <mach/usb_phy.h>
+#include "baseband-xmm-power.h"
+#include "board.h"
+#include "devices.h"
+
+MODULE_LICENSE("GPL");
+
+static unsigned long XYZ = 1000 * 1000000 + 800 * 1000 + 500;
+
+module_param(modem_ver, ulong, 0644);
+MODULE_PARM_DESC(modem_ver,
+ "baseband xmm power2 - modem software version");
+module_param(modem_flash, ulong, 0644);
+MODULE_PARM_DESC(modem_flash,
+ "baseband xmm power2 - modem flash (1 = flash, 0 = flashless)");
+module_param(modem_pm, ulong, 0644);
+MODULE_PARM_DESC(modem_pm,
+ "baseband xmm power2 - modem power management (1 = pm, 0 = no pm)");
+module_param(XYZ, ulong, 0644);
+MODULE_PARM_DESC(XYZ,
+ "baseband xmm power2 - timing parameters X/Y/Z delay in ms");
+
+static struct baseband_power_platform_data *baseband_power2_driver_data;
+static struct workqueue_struct *workqueue;
+static struct baseband_xmm_power_work_t *baseband_xmm_power2_work;
+
+static enum {
+ IPC_AP_WAKE_UNINIT,
+ IPC_AP_WAKE_IRQ_READY,
+ IPC_AP_WAKE_INIT1,
+ IPC_AP_WAKE_INIT2,
+ IPC_AP_WAKE_L,
+ IPC_AP_WAKE_H,
+} ipc_ap_wake_state;
+
+static irqreturn_t baseband_xmm_power2_ver_lt_1130_ipc_ap_wake_irq2
+ (int irq, void *dev_id)
+{
+ int value;
+
+ pr_debug("%s\n", __func__);
+
+ /* check for platform data */
+ if (!baseband_power2_driver_data)
+ return IRQ_HANDLED;
+
+ /* IPC_AP_WAKE state machine */
+ if (ipc_ap_wake_state < IPC_AP_WAKE_IRQ_READY) {
+ pr_err("%s - spurious irq\n", __func__);
+ } else if (ipc_ap_wake_state == IPC_AP_WAKE_IRQ_READY) {
+ value = gpio_get_value(baseband_power2_driver_data->
+ modem.xmm.ipc_ap_wake);
+ if (!value) {
+ pr_debug("%s - IPC_AP_WAKE_INIT1"
+ " - got falling edge\n",
+ __func__);
+ /* go to IPC_AP_WAKE_INIT1 state */
+ ipc_ap_wake_state = IPC_AP_WAKE_INIT1;
+ /* queue work */
+ baseband_xmm_power2_work->state =
+ BBXMM_WORK_INIT_FLASHLESS_PM_VER_LT_1130_STEP1;
+ queue_work(workqueue, (struct work_struct *)
+ baseband_xmm_power2_work);
+ } else {
+ pr_debug("%s - IPC_AP_WAKE_INIT1"
+ " - wait for falling edge\n",
+ __func__);
+ }
+ } else if (ipc_ap_wake_state == IPC_AP_WAKE_INIT1) {
+ value = gpio_get_value(baseband_power2_driver_data->
+ modem.xmm.ipc_ap_wake);
+ if (!value) {
+ pr_debug("%s - IPC_AP_WAKE_INIT2"
+ " - wait for rising edge\n",
+ __func__);
+ } else {
+ pr_debug("%s - IPC_AP_WAKE_INIT2"
+ " - got rising edge\n",
+ __func__);
+ /* go to IPC_AP_WAKE_INIT2 state */
+ ipc_ap_wake_state = IPC_AP_WAKE_INIT2;
+ /* queue work */
+ baseband_xmm_power2_work->state =
+ BBXMM_WORK_INIT_FLASHLESS_PM_VER_LT_1130_STEP2;
+ queue_work(workqueue, (struct work_struct *)
+ baseband_xmm_power2_work);
+ }
+ } else {
+ value = gpio_get_value(baseband_power2_driver_data->
+ modem.xmm.ipc_ap_wake);
+ if (!value) {
+ pr_debug("%s - falling\n", __func__);
+ ipc_ap_wake_state = IPC_AP_WAKE_L;
+ } else {
+ pr_debug("%s - rising\n", __func__);
+ ipc_ap_wake_state = IPC_AP_WAKE_H;
+ }
+ return baseband_xmm_power_ipc_ap_wake_irq(irq, dev_id);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t baseband_xmm_power2_ver_ge_1130_ipc_ap_wake_irq2
+ (int irq, void *dev_id)
+{
+ int value;
+
+ pr_debug("%s\n", __func__);
+
+ /* check for platform data */
+ if (!baseband_power2_driver_data)
+ return IRQ_HANDLED;
+
+ /* IPC_AP_WAKE state machine */
+ if (ipc_ap_wake_state < IPC_AP_WAKE_IRQ_READY) {
+ pr_err("%s - spurious irq\n", __func__);
+ } else if (ipc_ap_wake_state == IPC_AP_WAKE_IRQ_READY) {
+ value = gpio_get_value(baseband_power2_driver_data->
+ modem.xmm.ipc_ap_wake);
+ if (!value) {
+ pr_debug("%s - IPC_AP_WAKE_INIT1"
+ " - got falling edge\n",
+ __func__);
+ /* go to IPC_AP_WAKE_INIT2 state */
+ ipc_ap_wake_state = IPC_AP_WAKE_INIT2;
+ /* queue work */
+ baseband_xmm_power2_work->state =
+ BBXMM_WORK_INIT_FLASHLESS_PM_VER_GE_1130_STEP2;
+ queue_work(workqueue, (struct work_struct *)
+ baseband_xmm_power2_work);
+ } else {
+ pr_debug("%s - IPC_AP_WAKE_INIT1"
+ " - wait for falling edge\n",
+ __func__);
+ }
+ } else {
+ value = gpio_get_value(baseband_power2_driver_data->
+ modem.xmm.ipc_ap_wake);
+ if (!value) {
+ pr_debug("%s - falling\n", __func__);
+ ipc_ap_wake_state = IPC_AP_WAKE_L;
+ } else {
+ pr_debug("%s - rising\n", __func__);
+ ipc_ap_wake_state = IPC_AP_WAKE_H;
+ }
+ return baseband_xmm_power_ipc_ap_wake_irq(irq, dev_id);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static void baseband_xmm_power2_flashless_pm_ver_lt_1130_step1
+ (struct work_struct *work)
+{
+ int value;
+
+ pr_debug("%s {\n", __func__);
+
+ /* check for platform data */
+ if (!baseband_power2_driver_data)
+ return;
+
+ /* check if IPC_HSIC_ACTIVE high */
+ value = gpio_get_value(baseband_power2_driver_data->
+ modem.xmm.ipc_hsic_active);
+ if (value != 1) {
+ pr_err("%s - expected IPC_HSIC_ACTIVE high!\n", __func__);
+ return;
+ }
+
+ /* wait 30 ms */
+ mdelay(30);
+
+ /* set IPC_HSIC_ACTIVE low */
+ gpio_set_value(baseband_power2_driver_data->
+ modem.xmm.ipc_hsic_active, 0);
+
+ pr_debug("%s }\n", __func__);
+}
+
+static void baseband_xmm_power2_flashless_pm_ver_lt_1130_step2
+ (struct work_struct *work)
+{
+ int value;
+
+ pr_debug("%s {\n", __func__);
+
+ /* check for platform data */
+ if (!baseband_power2_driver_data)
+ return;
+
+ /* check if IPC_HSIC_ACTIVE low */
+ value = gpio_get_value(baseband_power2_driver_data->
+ modem.xmm.ipc_hsic_active);
+ if (value != 0) {
+ pr_err("%s - expected IPC_HSIC_ACTIVE low!\n", __func__);
+ return;
+ }
+
+ /* wait 1 ms */
+ mdelay(1);
+
+ /* turn on usb host controller */
+ {
+ mm_segment_t oldfs;
+ struct file *filp;
+ oldfs = get_fs();
+ set_fs(KERNEL_DS);
+ filp = filp_open(TEGRA_EHCI_DEVICE, O_RDWR, 0);
+ if (IS_ERR(filp) || (filp == NULL)) {
+ pr_err("open ehci_power failed\n");
+ } else {
+ filp->f_op->write(filp, "1", 1, &filp->f_pos);
+ filp_close(filp, NULL);
+ }
+ set_fs(oldfs);
+ }
+
+ /* set IPC_HSIC_ACTIVE high */
+ gpio_set_value(baseband_power2_driver_data->
+ modem.xmm.ipc_hsic_active, 1);
+
+ /* wait 20 ms */
+ mdelay(20);
+
+ /* set IPC_HSIC_ACTIVE low */
+ gpio_set_value(baseband_power2_driver_data->
+ modem.xmm.ipc_hsic_active, 0);
+
+ /* wait 20 ms */
+ mdelay(20);
+
+ /* set IPC_HSIC_ACTIVE high */
+ gpio_set_value(baseband_power2_driver_data->
+ modem.xmm.ipc_hsic_active, 1);
+
+ pr_debug("%s }\n", __func__);
+}
+
+static void baseband_xmm_power2_flashless_pm_ver_ge_1130_step1
+ (struct work_struct *work)
+{
+ int X = XYZ / 1000000;
+ int Y = XYZ / 1000 - X * 1000;
+ int Z = XYZ % 1000;
+
+ pr_info("%s {\n", __func__);
+
+ pr_info("XYZ=%ld X=%d Y=%d Z=%d\n", XYZ, X, Y, Z);
+
+ /* check for platform data */
+ if (!baseband_power2_driver_data)
+ return;
+
+ /* turn off usb host controller */
+ {
+ mm_segment_t oldfs;
+ struct file *filp;
+ oldfs = get_fs();
+ set_fs(KERNEL_DS);
+ filp = filp_open(TEGRA_EHCI_DEVICE, O_RDWR, 0);
+ if (IS_ERR(filp) || (filp == NULL)) {
+ pr_err("open ehci_power failed\n");
+ } else {
+ filp->f_op->write(filp, "0", 1, &filp->f_pos);
+ filp_close(filp, NULL);
+ }
+ set_fs(oldfs);
+ }
+
+ /* wait X ms */
+ mdelay(X);
+
+ /* set IPC_HSIC_ACTIVE low */
+ gpio_set_value(baseband_power2_driver_data->
+ modem.xmm.ipc_hsic_active, 0);
+
+ pr_info("%s }\n", __func__);
+}
+
+static void baseband_xmm_power2_flashless_pm_ver_ge_1130_step2
+ (struct work_struct *work)
+{
+ int X = XYZ / 1000000;
+ int Y = XYZ / 1000 - X * 1000;
+ int Z = XYZ % 1000;
+
+ pr_info("%s {\n", __func__);
+
+ pr_info("XYZ=%ld X=%d Y=%d Z=%d\n", XYZ, X, Y, Z);
+
+ /* check for platform data */
+ if (!baseband_power2_driver_data)
+ return;
+
+ /* wait Y ms */
+ mdelay(Y);
+
+ /* turn on usb host controller */
+ {
+ mm_segment_t oldfs;
+ struct file *filp;
+ oldfs = get_fs();
+ set_fs(KERNEL_DS);
+ filp = filp_open(TEGRA_EHCI_DEVICE, O_RDWR, 0);
+ if (IS_ERR(filp) || (filp == NULL)) {
+ pr_err("open ehci_power failed\n");
+ } else {
+ filp->f_op->write(filp, "1", 1, &filp->f_pos);
+ filp_close(filp, NULL);
+ }
+ set_fs(oldfs);
+ }
+
+ /* wait Z ms */
+ mdelay(Z);
+
+ /* set IPC_HSIC_ACTIVE high */
+ gpio_set_value(baseband_power2_driver_data->
+ modem.xmm.ipc_hsic_active, 1);
+
+ /* queue work function to check if enumeration succeeded */
+ baseband_xmm_power2_work->state =
+ BBXMM_WORK_INIT_FLASHLESS_PM_VER_GE_1130_STEP3;
+ queue_work(workqueue, (struct work_struct *)
+ baseband_xmm_power2_work);
+
+ pr_info("%s }\n", __func__);
+}
+
+static void baseband_xmm_power2_flashless_pm_ver_ge_1130_step3
+ (struct work_struct *work)
+{
+ int X = XYZ / 1000000;
+ int Y = XYZ / 1000 - X * 1000;
+ int Z = XYZ % 1000;
+ int enum_success = 0;
+
+ pr_info("%s {\n", __func__);
+
+ pr_info("XYZ=%ld X=%d Y=%d Z=%d\n", XYZ, X, Y, Z);
+
+ /* check for platform data */
+ if (!baseband_power2_driver_data)
+ return;
+
+ /* wait 500 ms */
+ mdelay(500);
+
+ /* check if enumeration succeeded */
+ {
+ mm_segment_t oldfs;
+ struct file *filp;
+ oldfs = get_fs();
+ set_fs(KERNEL_DS);
+ filp = filp_open("/dev/ttyACM0",
+ O_RDONLY, 0);
+ if (IS_ERR(filp) || (filp == NULL)) {
+ pr_err("/dev/ttyACM0 %ld\n",
+ PTR_ERR(filp));
+ } else {
+ filp_close(filp, NULL);
+ enum_success = 1;
+ }
+ set_fs(oldfs);
+ }
+
+ /* if enumeration failed, attempt recovery pulse */
+ if (!enum_success) {
+ pr_info("attempting recovery pulse...\n");
+ /* wait 20 ms */
+ mdelay(20);
+ /* set IPC_HSIC_ACTIVE low */
+ gpio_set_value(baseband_power2_driver_data->
+ modem.xmm.ipc_hsic_active, 0);
+ /* wait 20 ms */
+ mdelay(20);
+ /* set IPC_HSIC_ACTIVE high */
+ gpio_set_value(baseband_power2_driver_data->
+ modem.xmm.ipc_hsic_active, 1);
+ /* check if recovery pulse worked */
+ baseband_xmm_power2_work->state =
+ BBXMM_WORK_INIT_FLASHLESS_PM_VER_GE_1130_STEP4;
+ queue_work(workqueue, (struct work_struct *)
+ baseband_xmm_power2_work);
+ }
+
+ pr_info("%s }\n", __func__);
+}
+
+static void baseband_xmm_power2_flashless_pm_ver_ge_1130_step4
+ (struct work_struct *work)
+{
+ int X = XYZ / 1000000;
+ int Y = XYZ / 1000 - X * 1000;
+ int Z = XYZ % 1000;
+ int enum_success = 0;
+
+ pr_info("%s {\n", __func__);
+
+ pr_info("XYZ=%ld X=%d Y=%d Z=%d\n", XYZ, X, Y, Z);
+
+ /* check for platform data */
+ if (!baseband_power2_driver_data)
+ return;
+
+ /* wait 500 ms */
+ mdelay(500);
+
+ /* check if enumeration succeeded */
+ {
+ mm_segment_t oldfs;
+ struct file *filp;
+ oldfs = get_fs();
+ set_fs(KERNEL_DS);
+ filp = filp_open("/dev/ttyACM0",
+ O_RDONLY, 0);
+ if (IS_ERR(filp) || (filp == NULL)) {
+ pr_err("open /dev/ttyACM0 failed %ld\n",
+ PTR_ERR(filp));
+ } else {
+ filp_close(filp, NULL);
+ enum_success = 1;
+ }
+ set_fs(oldfs);
+ }
+
+ /* if recovery pulse did not fix enumeration, retry from beginning */
+ if (!enum_success) {
+ static int retry = 3;
+ if (!retry) {
+ pr_info("failed to enumerate modem software"
+ " - too many retry attempts\n");
+ } else {
+ pr_info("recovery pulse failed to fix modem"
+ " enumeration..."
+ " restarting from beginning"
+ " - attempt #%d\n",
+ retry);
+ --retry;
+ ipc_ap_wake_state = IPC_AP_WAKE_IRQ_READY;
+ baseband_xmm_power2_work->state =
+ BBXMM_WORK_INIT_FLASHLESS_PM_VER_GE_1130_STEP1;
+ queue_work(workqueue, (struct work_struct *)
+ baseband_xmm_power2_work);
+ }
+ }
+
+ pr_info("%s }\n", __func__);
+}
+
+static int free_ipc_ap_wake_irq;
+
+static void baseband_xmm_power2_work_func(struct work_struct *work)
+{
+ struct baseband_xmm_power_work_t *bbxmm_work
+ = (struct baseband_xmm_power_work_t *) work;
+ int err;
+
+ pr_debug("%s bbxmm_work->state=%d\n", __func__, bbxmm_work->state);
+
+ switch (bbxmm_work->state) {
+ case BBXMM_WORK_UNINIT:
+ pr_debug("BBXMM_WORK_UNINIT\n");
+ /* free baseband irq(s) */
+ if (free_ipc_ap_wake_irq) {
+ free_irq(gpio_to_irq(baseband_power2_driver_data
+ ->modem.xmm.ipc_ap_wake), NULL);
+ free_ipc_ap_wake_irq = 0;
+ }
+ break;
+ case BBXMM_WORK_INIT:
+ pr_debug("BBXMM_WORK_INIT\n");
+ /* request baseband irq(s) */
+ ipc_ap_wake_state = IPC_AP_WAKE_UNINIT;
+ err = request_irq(gpio_to_irq(baseband_power2_driver_data
+ ->modem.xmm.ipc_ap_wake),
+ (modem_ver < XMM_MODEM_VER_1130)
+ ? baseband_xmm_power2_ver_lt_1130_ipc_ap_wake_irq2
+ : baseband_xmm_power2_ver_ge_1130_ipc_ap_wake_irq2,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ "BBXMM_POWER2_IPC_AP_WAKE_IRQ",
+ NULL);
+ if (err < 0) {
+ pr_err("%s - request irq IPC_AP_WAKE_IRQ failed\n",
+ __func__);
+ return;
+ }
+ free_ipc_ap_wake_irq = 1;
+ ipc_ap_wake_state = IPC_AP_WAKE_IRQ_READY;
+ /* go to next state */
+ bbxmm_work->state = (modem_flash && !modem_pm)
+ ? BBXMM_WORK_INIT_FLASH_STEP1
+ : (modem_flash && modem_pm)
+ ? BBXMM_WORK_INIT_FLASH_PM_STEP1
+ : (!modem_flash && modem_pm)
+ ? BBXMM_WORK_INIT_FLASHLESS_PM_STEP1
+ : BBXMM_WORK_UNINIT;
+ queue_work(workqueue, work);
+ break;
+ case BBXMM_WORK_INIT_FLASH_STEP1:
+ pr_debug("BBXMM_WORK_INIT_FLASH_STEP1\n");
+ break;
+ case BBXMM_WORK_INIT_FLASH_PM_STEP1:
+ pr_debug("BBXMM_WORK_INIT_FLASH_PM_STEP1\n");
+ /* go to next state */
+ bbxmm_work->state = (modem_ver < XMM_MODEM_VER_1130)
+ ? BBXMM_WORK_INIT_FLASH_PM_VER_LT_1130_STEP1
+ : BBXMM_WORK_INIT_FLASH_PM_VER_GE_1130_STEP1;
+ queue_work(workqueue, work);
+ break;
+ case BBXMM_WORK_INIT_FLASH_PM_VER_LT_1130_STEP1:
+ pr_debug("BBXMM_WORK_INIT_FLASH_PM_VER_LT_1130_STEP1\n");
+ break;
+ case BBXMM_WORK_INIT_FLASH_PM_VER_GE_1130_STEP1:
+ pr_debug("BBXMM_WORK_INIT_FLASH_PM_VER_GE_1130_STEP1\n");
+ break;
+ case BBXMM_WORK_INIT_FLASHLESS_PM_STEP1:
+ pr_debug("BBXMM_WORK_INIT_FLASHLESS_PM_STEP1\n");
+ /* go to next state */
+ bbxmm_work->state = (modem_ver < XMM_MODEM_VER_1130)
+ ? BBXMM_WORK_INIT_FLASHLESS_PM_VER_LT_1130_WAIT_IRQ
+ : BBXMM_WORK_INIT_FLASHLESS_PM_VER_GE_1130_STEP1;
+ queue_work(workqueue, work);
+ break;
+ case BBXMM_WORK_INIT_FLASHLESS_PM_VER_LT_1130_WAIT_IRQ:
+ pr_debug("BBXMM_WORK_INIT_FLASHLESS_PM_VER_LT_1130_WAIT_IRQ"
+ " - waiting for IPC_AP_WAKE_IRQ to trigger step1\n");
+ break;
+ case BBXMM_WORK_INIT_FLASHLESS_PM_VER_LT_1130_STEP1:
+ pr_debug("BBXMM_WORK_INIT_FLASHLESS_PM_VER_LT_1130_STEP1\n");
+ baseband_xmm_power2_flashless_pm_ver_lt_1130_step1(work);
+ break;
+ case BBXMM_WORK_INIT_FLASHLESS_PM_VER_LT_1130_STEP2:
+ pr_debug("BBXMM_WORK_INIT_FLASHLESS_PM_VER_LT_1130_STEP2\n");
+ baseband_xmm_power2_flashless_pm_ver_lt_1130_step2(work);
+ break;
+ case BBXMM_WORK_INIT_FLASHLESS_PM_VER_GE_1130_STEP1:
+ pr_debug("BBXMM_WORK_INIT_FLASHLESS_PM_VER_GE_1130_STEP1\n");
+ baseband_xmm_power2_flashless_pm_ver_ge_1130_step1(work);
+ break;
+ case BBXMM_WORK_INIT_FLASHLESS_PM_VER_GE_1130_STEP2:
+ pr_debug("BBXMM_WORK_INIT_FLASHLESS_PM_VER_GE_1130_STEP2\n");
+ baseband_xmm_power2_flashless_pm_ver_ge_1130_step2(work);
+ break;
+ case BBXMM_WORK_INIT_FLASHLESS_PM_VER_GE_1130_STEP3:
+ pr_debug("BBXMM_WORK_INIT_FLASHLESS_PM_VER_GE_1130_STEP3\n");
+ baseband_xmm_power2_flashless_pm_ver_ge_1130_step3(work);
+ break;
+ case BBXMM_WORK_INIT_FLASHLESS_PM_VER_GE_1130_STEP4:
+ pr_debug("BBXMM_WORK_INIT_FLASHLESS_PM_VER_GE_1130_STEP4\n");
+ baseband_xmm_power2_flashless_pm_ver_ge_1130_step4(work);
+ break;
+ }
+
+}
+
+static int baseband_xmm_power2_driver_probe(struct platform_device *device)
+{
+ struct baseband_power_platform_data *data
+ = (struct baseband_power_platform_data *)
+ device->dev.platform_data;
+
+ pr_debug("%s\n", __func__);
+
+ /* save platform data */
+ baseband_power2_driver_data = data;
+
+ /* init work queue */
+ pr_debug("%s: init work queue\n", __func__);
+ workqueue = create_singlethread_workqueue
+ ("baseband_xmm_power2_workqueue");
+ if (!workqueue) {
+ pr_err("cannot create workqueue\n");
+ return -1;
+ }
+ baseband_xmm_power2_work = (struct baseband_xmm_power_work_t *)
+ kmalloc(sizeof(struct baseband_xmm_power_work_t), GFP_KERNEL);
+ if (!baseband_xmm_power2_work) {
+ pr_err("cannot allocate baseband_xmm_power2_work\n");
+ return -1;
+ }
+ pr_debug("%s: BBXMM_WORK_INIT\n", __func__);
+ INIT_WORK((struct work_struct *) baseband_xmm_power2_work,
+ baseband_xmm_power2_work_func);
+ baseband_xmm_power2_work->state = BBXMM_WORK_INIT;
+ queue_work(workqueue,
+ (struct work_struct *) baseband_xmm_power2_work);
+ return 0;
+}
+
+static int baseband_xmm_power2_driver_remove(struct platform_device *device)
+{
+ struct baseband_power_platform_data *data
+ = (struct baseband_power_platform_data *)
+ device->dev.platform_data;
+
+ pr_debug("%s\n", __func__);
+
+ /* check for platform data */
+ if (!data)
+ return 0;
+
+ /* free irq */
+ if (free_ipc_ap_wake_irq) {
+ free_irq(gpio_to_irq(data->modem.xmm.ipc_ap_wake), NULL);
+ free_ipc_ap_wake_irq = 0;
+ }
+
+ /* free work structure */
+ destroy_workqueue(workqueue);
+ kfree(baseband_xmm_power2_work);
+ baseband_xmm_power2_work = (struct baseband_xmm_power_work_t *) 0;
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int baseband_xmm_power2_driver_suspend(struct platform_device *device,
+ pm_message_t state)
+{
+ struct baseband_power_platform_data *data
+ = (struct baseband_power_platform_data *)
+ device->dev.platform_data;
+
+ pr_debug("%s - nop\n", __func__);
+
+ /* check for platform data */
+ if (!data)
+ return 0;
+
+ return 0;
+}
+
+static int baseband_xmm_power2_driver_resume(struct platform_device *device)
+{
+ struct baseband_power_platform_data *data
+ = (struct baseband_power_platform_data *)
+ device->dev.platform_data;
+
+ pr_debug("%s - nop\n", __func__);
+
+ /* check for platform data */
+ if (!data)
+ return 0;
+
+ return 0;
+}
+#endif
+
+static struct platform_driver baseband_power2_driver = {
+ .probe = baseband_xmm_power2_driver_probe,
+ .remove = baseband_xmm_power2_driver_remove,
+#ifdef CONFIG_PM
+ .suspend = baseband_xmm_power2_driver_suspend,
+ .resume = baseband_xmm_power2_driver_resume,
+#endif
+ .driver = {
+ .name = "baseband_xmm_power2",
+ },
+};
+
+static int __init baseband_xmm_power2_init(void)
+{
+ pr_debug("%s\n", __func__);
+
+ return platform_driver_register(&baseband_power2_driver);
+}
+
+static void __exit baseband_xmm_power2_exit(void)
+{
+ pr_debug("%s\n", __func__);
+ platform_driver_unregister(&baseband_power2_driver);
+}
+
+module_init(baseband_xmm_power2_init)
+module_exit(baseband_xmm_power2_exit)
diff --git a/arch/arm/mach-tegra/board-aruba-panel.c b/arch/arm/mach-tegra/board-aruba-panel.c
new file mode 100644
index 000000000000..e4953bde46c3
--- /dev/null
+++ b/arch/arm/mach-tegra/board-aruba-panel.c
@@ -0,0 +1,247 @@
+/*
+ * arch/arm/mach-tegra/board-aruba-panel.c
+ *
+ * Copyright (c) 2010-2011, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/resource.h>
+#include <asm/mach-types.h>
+#include <linux/platform_device.h>
+#include <linux/pwm_backlight.h>
+#include <linux/nvhost.h>
+#include <mach/nvmap.h>
+#include <mach/irqs.h>
+#include <mach/iomap.h>
+#include <mach/dc.h>
+#include <mach/fb.h>
+
+#include "board.h"
+#include "devices.h"
+#include "gpio-names.h"
+
+#define aruba_lvds_shutdown TEGRA_GPIO_PB2
+#define aruba_bl_enb TEGRA_GPIO_PW1
+
+static int aruba_backlight_init(struct device *dev) {
+ int ret;
+
+ ret = gpio_request(aruba_bl_enb, "backlight_enb");
+ if (ret < 0)
+ return ret;
+
+ ret = gpio_direction_output(aruba_bl_enb, 1);
+ if (ret < 0)
+ gpio_free(aruba_bl_enb);
+ else
+ tegra_gpio_enable(aruba_bl_enb);
+
+ return ret;
+};
+
+static void aruba_backlight_exit(struct device *dev) {
+ gpio_set_value(aruba_bl_enb, 0);
+ gpio_free(aruba_bl_enb);
+ tegra_gpio_disable(aruba_bl_enb);
+}
+
+static int aruba_backlight_notify(struct device *unused, int brightness)
+{
+ gpio_set_value(aruba_bl_enb, !!brightness);
+ return brightness;
+}
+
+static struct platform_pwm_backlight_data aruba_backlight_data = {
+ .pwm_id = 2,
+ .max_brightness = 255,
+ .dft_brightness = 224,
+ .pwm_period_ns = 5000000,
+ .init = aruba_backlight_init,
+ .exit = aruba_backlight_exit,
+ .notify = aruba_backlight_notify,
+};
+
+static struct platform_device aruba_backlight_device = {
+ .name = "pwm-backlight",
+ .id = -1,
+ .dev = {
+ .platform_data = &aruba_backlight_data,
+ },
+};
+
+#ifdef CONFIG_TEGRA_DC
+static int aruba_panel_enable(void)
+{
+ static struct regulator *reg = NULL;
+
+ if (reg == NULL) {
+ reg = regulator_get(NULL, "avdd_lvds");
+ if (WARN_ON(IS_ERR(reg)))
+ pr_err("%s: couldn't get regulator avdd_lvds: %ld\n",
+ __func__, PTR_ERR(reg));
+ else
+ regulator_enable(reg);
+ }
+
+ gpio_set_value(aruba_lvds_shutdown, 1);
+ return 0;
+}
+
+static int aruba_panel_disable(void)
+{
+ gpio_set_value(aruba_lvds_shutdown, 0);
+ return 0;
+}
+
+static struct resource aruba_disp1_resources[] = {
+ {
+ .name = "irq",
+ .start = INT_DISPLAY_GENERAL,
+ .end = INT_DISPLAY_GENERAL,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "regs",
+ .start = TEGRA_DISPLAY_BASE,
+ .end = TEGRA_DISPLAY_BASE + TEGRA_DISPLAY_SIZE-1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .name = "fbmem",
+ .start = 0, /* Filled in by aruba_panel_init() */
+ .end = 0, /* Filled in by aruba_panel_init() */
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct tegra_dc_mode aruba_panel_modes[] = {
+ {
+ .pclk = 18000000,
+ .h_ref_to_sync = 8,
+ .v_ref_to_sync = 2,
+ .h_sync_width = 4,
+ .v_sync_width = 1,
+ .h_back_porch = 20,
+ .v_back_porch = 7,
+ .h_active = 480,
+ .v_active = 640,
+ .h_front_porch = 8,
+ .v_front_porch = 8,
+ },
+};
+
+static struct tegra_fb_data aruba_fb_data = {
+ .win = 0,
+ .xres = 480,
+ .yres = 640,
+ .bits_per_pixel = 16,
+};
+
+static struct tegra_dc_out aruba_disp1_out = {
+ .type = TEGRA_DC_OUT_RGB,
+
+ .align = TEGRA_DC_ALIGN_MSB,
+ .order = TEGRA_DC_ORDER_RED_BLUE,
+
+ .modes = aruba_panel_modes,
+ .n_modes = ARRAY_SIZE(aruba_panel_modes),
+
+ .enable = aruba_panel_enable,
+ .disable = aruba_panel_disable,
+};
+
+static struct tegra_dc_platform_data aruba_disp1_pdata = {
+ .flags = TEGRA_DC_FLAG_ENABLED,
+ .default_out = &aruba_disp1_out,
+ .fb = &aruba_fb_data,
+};
+
+static struct nvhost_device aruba_disp1_device = {
+ .name = "tegradc",
+ .id = 0,
+ .resource = aruba_disp1_resources,
+ .num_resources = ARRAY_SIZE(aruba_disp1_resources),
+ .dev = {
+ .platform_data = &aruba_disp1_pdata,
+ },
+};
+#endif
+
+static struct nvmap_platform_carveout aruba_carveouts[] = {
+ [0] = NVMAP_HEAP_CARVEOUT_IRAM_INIT,
+ [1] = {
+ .name = "generic-0",
+ .usage_mask = NVMAP_HEAP_CARVEOUT_GENERIC,
+ .base = 0, /* Filled in by aruba_panel_init() */
+ .size = 0, /* Filled in by aruba_panel_init() */
+ .buddy_size = SZ_32K,
+ },
+};
+
+static struct nvmap_platform_data aruba_nvmap_data = {
+ .carveouts = aruba_carveouts,
+ .nr_carveouts = ARRAY_SIZE(aruba_carveouts),
+};
+
+static struct platform_device aruba_nvmap_device = {
+ .name = "tegra-nvmap",
+ .id = -1,
+ .dev = {
+ .platform_data = &aruba_nvmap_data,
+ },
+};
+
+static struct platform_device *aruba_gfx_devices[] __initdata = {
+ &aruba_nvmap_device,
+#ifdef CONFIG_TEGRA_GRHOST
+ &tegra_grhost_device,
+#endif
+ &tegra_pwfm2_device,
+ &aruba_backlight_device,
+};
+
+int __init aruba_panel_init(void)
+{
+ int err;
+ struct resource __maybe_unused *res;
+
+ aruba_carveouts[1].base = tegra_carveout_start;
+ aruba_carveouts[1].size = tegra_carveout_size;
+
+ err = platform_add_devices(aruba_gfx_devices,
+ ARRAY_SIZE(aruba_gfx_devices));
+
+#if defined(CONFIG_TEGRA_GRHOST) && defined(CONFIG_TEGRA_DC)
+ res = nvhost_get_resource_byname(&aruba_disp1_device,
+ IORESOURCE_MEM, "fbmem");
+ res->start = tegra_fb_start;
+ res->end = tegra_fb_start + tegra_fb_size - 1;
+#endif
+
+ /* Copy the bootloader fb to the fb. */
+ tegra_move_framebuffer(tegra_fb_start, tegra_bootloader_fb_start,
+ min(tegra_fb_size, tegra_bootloader_fb_size));
+
+#if defined(CONFIG_TEGRA_GRHOST) && defined(CONFIG_TEGRA_DC)
+ if (!err)
+ err = nvhost_device_register(&aruba_disp1_device);
+#endif
+
+ return err;
+}
diff --git a/arch/arm/mach-tegra/board-aruba-pinmux.c b/arch/arm/mach-tegra/board-aruba-pinmux.c
new file mode 100644
index 000000000000..3db2ede1eb1c
--- /dev/null
+++ b/arch/arm/mach-tegra/board-aruba-pinmux.c
@@ -0,0 +1,307 @@
+/*
+ * arch/arm/mach-tegra/board-aruba-pinmux.c
+ *
+ * Copyright (C) 2010 NVIDIA Corporation
+ *
+ * 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/init.h>
+#include <mach/pinmux.h>
+
+#define DEFAULT_DRIVE(_name) \
+ { \
+ .pingroup = TEGRA_DRIVE_PINGROUP_##_name, \
+ .hsm = TEGRA_HSM_DISABLE, \
+ .schmitt = TEGRA_SCHMITT_ENABLE, \
+ .drive = TEGRA_DRIVE_DIV_1, \
+ .pull_down = TEGRA_PULL_31, \
+ .pull_up = TEGRA_PULL_31, \
+ .slew_rising = TEGRA_SLEW_SLOWEST, \
+ .slew_falling = TEGRA_SLEW_SLOWEST, \
+ }
+
+
+// !!!FIXME!!!! POPULATE THIS TABLE
+static __initdata struct tegra_drive_pingroup_config aruba_drive_pinmux[] = {
+ /* DEFAULT_DRIVE(<pin_group>), */
+};
+
+#define DEFAULT_PINMUX(_pingroup, _mux, _pupd, _tri, _io) \
+ { \
+ .pingroup = TEGRA_PINGROUP_##_pingroup, \
+ .func = TEGRA_MUX_##_mux, \
+ .pupd = TEGRA_PUPD_##_pupd, \
+ .tristate = TEGRA_TRI_##_tri, \
+ .io = TEGRA_PIN_##_io, \
+ }
+
+// !!!FIXME!!!! POPULATE THIS TABLE
+static __initdata struct tegra_pingroup_config aruba_pinmux[] = {
+ DEFAULT_PINMUX(ULPI_DATA0, UARTA, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(ULPI_DATA1, UARTA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(ULPI_DATA2, UARTA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(ULPI_DATA3, UARTA, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(ULPI_DATA4, UARTA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(ULPI_DATA5, UARTA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(ULPI_DATA6, UARTA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(ULPI_DATA7, UARTA, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(ULPI_CLK, UARTD, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(ULPI_DIR, UARTD, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(ULPI_NXT, UARTD, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(ULPI_STP, UARTD, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(DAP3_FS, I2S2, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(DAP3_DIN, I2S2, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(DAP3_DOUT, I2S2, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(DAP3_SCLK, I2S2, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC1_CLK, SDMMC1, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC1_CMD, SDMMC1, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC1_DAT3, SDMMC1, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC1_DAT2, SDMMC1, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC1_DAT1, SDMMC1, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC1_DAT0, SDMMC1, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GPIO_PV2, OWR, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(GPIO_PV3, CLK12, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(CLK2_OUT, EXTPERIPH2, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(CLK2_REQ, DAP, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_PWR1, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_PWR2, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_SDIN, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_SDOUT, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_WR_N, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_CS0_N, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_DC0, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_SCK, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_PWR0, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_PCLK, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_DE, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_HSYNC, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_VSYNC, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D0, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D1, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D2, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D3, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D4, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D5, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D6, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D7, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D8, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D9, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D10, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D11, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D12, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D13, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D14, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D15, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D16, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D17, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D18, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D19, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D20, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D21, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D22, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D23, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_CS1_N, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_M1, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_DC1, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(DDC_SCL, I2C4, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(DDC_SDA, I2C4, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(CRT_HSYNC, CRT, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(CRT_VSYNC, CRT, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(VI_D0, RSVD1, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(VI_D1, SDMMC2, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(VI_D2, SDMMC2, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(VI_D3, SDMMC2, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(VI_D4, SDMMC2, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(VI_D5, SDMMC2, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(VI_D6, SDMMC2, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(VI_D7, SDMMC2, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(VI_D8, SDMMC2, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(VI_D9, SDMMC2, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(VI_D10, RSVD1, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(VI_D11, RSVD1, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(VI_PCLK, SDMMC2, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(VI_MCLK, VI, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(VI_VSYNC, RSVD1, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(VI_HSYNC, RSVD1, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(UART2_RXD, IRDA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(UART2_TXD, IRDA, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(UART2_RTS_N, GMI, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(UART2_CTS_N, GMI, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(UART3_TXD, UARTC, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(UART3_RXD, UARTC, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(UART3_CTS_N, UARTC, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(UART3_RTS_N, UARTC, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(GPIO_PU0, RSVD1, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GPIO_PU1, RSVD1, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GPIO_PU2, RSVD1, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GPIO_PU3, PWM0, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(GPIO_PU4, PWM1, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(GPIO_PU5, PWM2, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(GPIO_PU6, PWM3, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(GEN1_I2C_SDA, I2C1, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GEN1_I2C_SCL, I2C1, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(DAP4_FS, I2S3, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(DAP4_DIN, I2S3, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(DAP4_DOUT, I2S3, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(DAP4_SCLK, I2S3, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(CLK3_OUT, EXTPERIPH3, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(CLK3_REQ, DEV3, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GMI_WP_N, RSVD1, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GMI_IORDY, NAND, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(GMI_WAIT, NAND, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GMI_ADV_N, NAND, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(GMI_CLK, NAND, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(GMI_CS0_N, NAND, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(GMI_CS1_N, NAND, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(GMI_CS2_N, NAND, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(GMI_CS3_N, NAND, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(GMI_CS4_N, NAND, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(GMI_CS6_N, NAND_ALT, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(GMI_CS7_N, NAND_ALT, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(GMI_AD0, NAND, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GMI_AD1, NAND, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GMI_AD2, NAND, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GMI_AD3, NAND, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GMI_AD4, NAND, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GMI_AD5, NAND, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GMI_AD6, NAND, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GMI_AD7, NAND, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GMI_AD8, NAND, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GMI_AD9, NAND, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GMI_AD10, NAND, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GMI_AD11, NAND, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GMI_AD12, NAND, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GMI_AD13, NAND, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GMI_AD14, NAND, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GMI_AD15, NAND, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GMI_A16, UARTD, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(GMI_A17, UARTD, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GMI_A18, UARTD, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GMI_A19, UARTD, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(GMI_WR_N, NAND, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(GMI_OE_N, NAND, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(GMI_DQS, NAND, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GMI_RST_N, RSVD3, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GEN2_I2C_SCL, I2C2, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GEN2_I2C_SDA, I2C2, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC4_CLK, SDMMC4, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC4_CMD, SDMMC4, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC4_DAT0, SDMMC4, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC4_DAT1, SDMMC4, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC4_DAT2, SDMMC4, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC4_DAT3, SDMMC4, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC4_DAT4, SDMMC4, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC4_DAT5, SDMMC4, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC4_DAT6, SDMMC4, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC4_DAT7, SDMMC4, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC4_RST_N, RSVD1, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(CAM_MCLK, VI, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GPIO_PCC1, RSVD1, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GPIO_PBB0, RSVD1, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(CAM_I2C_SCL, I2C3, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(CAM_I2C_SDA, I2C3, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GPIO_PBB3, VGP3, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GPIO_PBB4, VGP4, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GPIO_PBB5, VGP5, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GPIO_PBB6, VGP6, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GPIO_PBB7, I2S4, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GPIO_PCC2, I2S4, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(JTAG_RTCK, RTCK, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(PWR_I2C_SCL, I2CPWR, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(PWR_I2C_SDA, I2CPWR, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(KB_ROW0, KBC, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(KB_ROW1, KBC, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(KB_ROW2, KBC, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(KB_ROW3, KBC, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(KB_ROW4, KBC, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(KB_ROW5, KBC, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(KB_ROW6, KBC, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(KB_ROW7, KBC, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(KB_ROW8, KBC, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(KB_ROW9, KBC, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(KB_ROW10, KBC, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(KB_ROW11, KBC, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(KB_ROW12, KBC, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(KB_ROW13, KBC, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(KB_ROW14, KBC, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(KB_ROW15, KBC, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(KB_COL0, KBC, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(KB_COL1, KBC, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(KB_COL2, KBC, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(KB_COL3, KBC, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(KB_COL4, KBC, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(KB_COL5, KBC, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(KB_COL6, KBC, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(KB_COL7, KBC, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(CLK_32K_OUT, BLINK, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(SYS_CLK_REQ, SYSCLK, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(OWR, OWR, NORMAL, NORMAL, INPUT),
+#ifdef CONFIG_SND_HDA_TEGRA
+ DEFAULT_PINMUX(DAP1_FS, HDA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(DAP1_DIN, HDA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(DAP1_DOUT, HDA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(DAP1_SCLK, HDA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(CLK1_REQ, DAP1, NORMAL, NORMAL, INPUT),
+#else
+ DEFAULT_PINMUX(DAP1_FS, I2S0, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(DAP1_DIN, I2S0, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(DAP1_DOUT, I2S0, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(DAP1_SCLK, I2S0, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(CLK1_REQ, DAP, NORMAL, NORMAL, INPUT),
+#endif
+ DEFAULT_PINMUX(CLK1_OUT, EXTPERIPH1, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(SPDIF_IN, SPDIF, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(SPDIF_OUT, SPDIF, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(DAP2_FS, I2S1, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(DAP2_DIN, I2S1, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(DAP2_DOUT, I2S1, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(DAP2_SCLK, I2S1, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(SPI2_MOSI, SPI6, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(SPI2_MISO, SPI6, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(SPI2_CS0_N, SPI6, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(SPI2_SCK, SPI6, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(SPI1_MOSI, SPI1, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(SPI1_SCK, SPI1, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(SPI1_CS0_N, SPI1, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(SPI1_MISO, SPI1, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(SPI2_CS1_N, SPI3, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(SPI2_CS2_N, SPI3, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC3_CLK, SDMMC3, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC3_CMD, SDMMC3, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC3_DAT0, SDMMC3, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC3_DAT1, SDMMC3, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC3_DAT2, SDMMC3, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC3_DAT3, SDMMC3, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC3_DAT4, SDMMC3, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC3_DAT5, SDMMC3, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC3_DAT6, SDMMC3, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC3_DAT7, SDMMC3, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(PEX_L0_PRSNT_N, PCIE, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(PEX_L0_RST_N, PCIE, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(PEX_L0_CLKREQ_N, PCIE, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(PEX_WAKE_N, PCIE, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(PEX_L1_PRSNT_N, PCIE, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(PEX_L1_RST_N, PCIE, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(PEX_L1_CLKREQ_N, PCIE, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(PEX_L2_PRSNT_N, PCIE, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(PEX_L2_RST_N, PCIE, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(PEX_L2_CLKREQ_N, PCIE, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(HDMI_CEC, CEC, NORMAL, NORMAL, INPUT),
+};
+
+void __init aruba_pinmux_init(void)
+{
+ tegra_pinmux_config_table(aruba_pinmux, ARRAY_SIZE(aruba_pinmux));
+ tegra_drive_pinmux_config_table(aruba_drive_pinmux,
+ ARRAY_SIZE(aruba_drive_pinmux));
+}
diff --git a/arch/arm/mach-tegra/board-aruba-power.c b/arch/arm/mach-tegra/board-aruba-power.c
new file mode 100644
index 000000000000..4391f6f19b51
--- /dev/null
+++ b/arch/arm/mach-tegra/board-aruba-power.c
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2010 NVIDIA, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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/i2c.h>
+#include <linux/pda_power.h>
+#include <linux/platform_device.h>
+#include <linux/resource.h>
+#include <linux/io.h>
+
+#include <mach/iomap.h>
+#include <mach/irqs.h>
+
+#include "pm.h"
+#include "board.h"
+#include "wakeups-t3.h"
+
+static int ac_online(void)
+{
+ return 1;
+}
+
+static struct resource aruba_pda_resources[] = {
+ [0] = {
+ .name = "ac",
+ },
+};
+
+static struct pda_power_pdata aruba_pda_data = {
+ .is_ac_online = ac_online,
+};
+
+static struct platform_device aruba_pda_power_device = {
+ .name = "pda-power",
+ .id = -1,
+ .resource = aruba_pda_resources,
+ .num_resources = ARRAY_SIZE(aruba_pda_resources),
+ .dev = {
+ .platform_data = &aruba_pda_data,
+ },
+};
+
+static struct tegra_suspend_platform_data aruba_suspend_data = {
+ .cpu_timer = 2000,
+ .cpu_off_timer = 0,
+ .suspend_mode = TEGRA_SUSPEND_NONE,
+ .core_timer = 0x7e7e,
+ .core_off_timer = 0,
+ .corereq_high = false,
+ .sysclkreq_high = true,
+};
+
+int __init aruba_regulator_init(void)
+{
+ platform_device_register(&aruba_pda_power_device);
+ tegra_init_suspend(&aruba_suspend_data);
+ return 0;
+}
+
+void __init tegra_tsensor_init(void)
+{
+ /* No tsensor on FPGAs */
+}
diff --git a/arch/arm/mach-tegra/board-aruba-sdhci.c b/arch/arm/mach-tegra/board-aruba-sdhci.c
new file mode 100644
index 000000000000..26b04a9021e1
--- /dev/null
+++ b/arch/arm/mach-tegra/board-aruba-sdhci.c
@@ -0,0 +1,248 @@
+/*
+ * arch/arm/mach-tegra/board-harmony-sdhci.c
+ *
+ * Copyright (C) 2010 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/resource.h>
+#include <linux/platform_device.h>
+#include <linux/wlan_plat.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/mmc/host.h>
+
+#include <asm/mach-types.h>
+#include <mach/irqs.h>
+#include <mach/iomap.h>
+#include <mach/sdhci.h>
+
+#include "gpio-names.h"
+#include "board.h"
+
+#define ARUBA_WIFI 0 /* !!!FIXME!!! NOT SUPPORTED YET */
+
+#if ARUBA_WIFI
+
+#define ARUBA_WLAN_PWR TEGRA_GPIO_PK5
+#define ARUBA_WLAN_RST TEGRA_GPIO_PK6
+
+static void (*wifi_status_cb)(int card_present, void *dev_id);
+static void *wifi_status_cb_devid;
+static int aruba_wifi_status_register(void (*callback)(int , void *), void *);
+static struct clk *wifi_32k_clk;
+
+static int aruba_wifi_reset(int on);
+static int aruba_wifi_power(int on);
+static int aruba_wifi_set_carddetect(int val);
+
+static struct wifi_platform_data aruba_wifi_control = {
+ .set_power = aruba_wifi_power,
+ .set_reset = aruba_wifi_reset,
+ .set_carddetect = aruba_wifi_set_carddetect,
+};
+
+static struct platform_device aruba_wifi_device = {
+ .name = "bcm4329_wlan",
+ .id = 1,
+ .dev = {
+ .platform_data = &aruba_wifi_control,
+ },
+};
+#endif
+
+static struct resource sdhci_resource0[] = {
+ [0] = {
+ .start = INT_SDMMC1,
+ .end = INT_SDMMC1,
+ .flags = IORESOURCE_IRQ,
+ },
+ [1] = {
+ .start = TEGRA_SDMMC1_BASE,
+ .end = TEGRA_SDMMC1_BASE + TEGRA_SDMMC1_SIZE-1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct resource sdhci_resource2[] = {
+ [0] = {
+ .start = INT_SDMMC3,
+ .end = INT_SDMMC3,
+ .flags = IORESOURCE_IRQ,
+ },
+ [1] = {
+ .start = TEGRA_SDMMC3_BASE,
+ .end = TEGRA_SDMMC3_BASE + TEGRA_SDMMC3_SIZE-1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct resource sdhci_resource3[] = {
+ [0] = {
+ .start = INT_SDMMC4,
+ .end = INT_SDMMC4,
+ .flags = IORESOURCE_IRQ,
+ },
+ [1] = {
+ .start = TEGRA_SDMMC4_BASE,
+ .end = TEGRA_SDMMC4_BASE + TEGRA_SDMMC4_SIZE-1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+
+static struct tegra_sdhci_platform_data tegra_sdhci_platform_data0 = {
+#if ARUBA_WIFI /* !!!FIXME!!! NOT SUPPORTED YET */
+ .register_status_notify = aruba_wifi_status_register,
+ .cccr = {
+ .sdio_vsn = 2,
+ .multi_block = 1,
+ .low_speed = 0,
+ .wide_bus = 0,
+ .high_power = 1,
+ .high_speed = 1,
+ },
+ .cis = {
+ .vendor = 0x02d0,
+ .device = 0x4329,
+ },
+#endif
+ .cd_gpio = -1,
+ .wp_gpio = -1,
+ .power_gpio = -1,
+/* .max_clk = 12000000, */
+};
+
+static struct tegra_sdhci_platform_data tegra_sdhci_platform_data2 = {
+ .cd_gpio = -1,
+ .wp_gpio = -1,
+ .power_gpio = -1,
+/* .max_clk = 12000000, */
+};
+
+static struct tegra_sdhci_platform_data tegra_sdhci_platform_data3 = {
+ .cd_gpio = -1,
+ .wp_gpio = -1,
+ .power_gpio = -1,
+/* .max_clk = 12000000, */
+};
+
+static struct platform_device tegra_sdhci_device0 = {
+ .name = "sdhci-tegra",
+ .id = 0,
+ .resource = sdhci_resource0,
+ .num_resources = ARRAY_SIZE(sdhci_resource0),
+ .dev = {
+ .platform_data = &tegra_sdhci_platform_data0,
+ },
+};
+
+static struct platform_device tegra_sdhci_device2 = {
+ .name = "sdhci-tegra",
+ .id = 2,
+ .resource = sdhci_resource2,
+ .num_resources = ARRAY_SIZE(sdhci_resource2),
+ .dev = {
+ .platform_data = &tegra_sdhci_platform_data2,
+ },
+};
+
+static struct platform_device tegra_sdhci_device3 = {
+ .name = "sdhci-tegra",
+ .id = 3,
+ .resource = sdhci_resource3,
+ .num_resources = ARRAY_SIZE(sdhci_resource3),
+ .dev = {
+ .platform_data = &tegra_sdhci_platform_data3,
+ },
+};
+
+#if ARUBA_WIFI /* !!!FIXME!!! NOT SUPPORTED YET */
+static int aruba_wifi_status_register(
+ void (*callback)(int card_present, void *dev_id),
+ void *dev_id)
+{
+ if (wifi_status_cb)
+ return -EAGAIN;
+ wifi_status_cb = callback;
+ wifi_status_cb_devid = dev_id;
+ return 0;
+}
+
+static int aruba_wifi_set_carddetect(int val)
+{
+ pr_debug("%s: %d\n", __func__, val);
+ if (wifi_status_cb)
+ wifi_status_cb(val, wifi_status_cb_devid);
+ else
+ pr_warning("%s: Nobody to notify\n", __func__);
+ return 0;
+}
+
+static int aruba_wifi_power(int on)
+{
+ pr_debug("%s: %d\n", __func__, on);
+
+ gpio_set_value(ARUBA_WLAN_PWR, on);
+ mdelay(100);
+ gpio_set_value(ARUBA_WLAN_RST, on);
+ mdelay(200);
+
+ if (on)
+ clk_enable(wifi_32k_clk);
+ else
+ clk_disable(wifi_32k_clk);
+
+ return 0;
+}
+
+static int aruba_wifi_reset(int on)
+{
+ pr_debug("%s: do nothing\n", __func__);
+ return 0;
+}
+
+static int __init aruba_wifi_init(void)
+{
+ wifi_32k_clk = clk_get_sys(NULL, "blink");
+ if (IS_ERR(wifi_32k_clk)) {
+ pr_err("%s: unable to get blink clock\n", __func__);
+ return PTR_ERR(wifi_32k_clk);
+ }
+
+ gpio_request(ARUBA_WLAN_PWR, "wlan_power");
+ gpio_request(ARUBA_WLAN_RST, "wlan_rst");
+
+ tegra_gpio_enable(ARUBA_WLAN_PWR);
+ tegra_gpio_enable(ARUBA_WLAN_RST);
+
+ gpio_direction_output(ARUBA_WLAN_PWR, 0);
+ gpio_direction_output(ARUBA_WLAN_RST, 0);
+
+ platform_device_register(&aruba_wifi_device);
+ return 0;
+}
+#else
+#define aruba_wifi_init() do {} while (0)
+#endif
+
+int __init aruba_sdhci_init(void)
+{
+ platform_device_register(&tegra_sdhci_device3);
+ platform_device_register(&tegra_sdhci_device2);
+ platform_device_register(&tegra_sdhci_device0);
+
+ aruba_wifi_init();
+ return 0;
+}
diff --git a/arch/arm/mach-tegra/board-aruba-sensors.c b/arch/arm/mach-tegra/board-aruba-sensors.c
new file mode 100644
index 000000000000..f5ba3d761634
--- /dev/null
+++ b/arch/arm/mach-tegra/board-aruba-sensors.c
@@ -0,0 +1,109 @@
+/*
+ * arch/arm/mach-tegra/board-aruba-sensors.c
+ *
+ * Copyright (c) 2010-2011, NVIDIA CORPORATION, All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Neither the name of NVIDIA CORPORATION nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <linux/i2c.h>
+#include <mach/gpio.h>
+
+#include "gpio-names.h"
+
+#if 0 // !!!FIXME!!! IMPLEMENT ME
+
+#define ISL29018_IRQ_GPIO TEGRA_GPIO_PZ2
+#define AKM8975_IRQ_GPIO TEGRA_GPIO_PN5
+
+static void aruba_isl29018_init(void)
+{
+ tegra_gpio_enable(ISL29018_IRQ_GPIO);
+ gpio_request(ISL29018_IRQ_GPIO, "isl29018");
+ gpio_direction_input(ISL29018_IRQ_GPIO);
+}
+
+static void aruba_akm8975_init(void)
+{
+ tegra_gpio_enable(AKM8975_IRQ_GPIO);
+ gpio_request(AKM8975_IRQ_GPIO, "akm8975");
+ gpio_direction_input(AKM8975_IRQ_GPIO);
+}
+
+struct nct1008_platform_data aruba_nct1008_pdata = {
+ .conv_rate = 5,
+ .config = NCT1008_CONFIG_ALERT_DISABLE,
+ .thermal_threshold = 110,
+};
+
+static const struct i2c_board_info aruba_i2c0_board_info[] = {
+ {
+ I2C_BOARD_INFO("isl29018", 0x44),
+ .irq = TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PZ2),
+ },
+};
+
+static const struct i2c_board_info aruba_i2c2_board_info[] = {
+ {
+ I2C_BOARD_INFO("bq20z75-battery", 0x0B),
+ },
+};
+
+static struct i2c_board_info aruba_i2c4_board_info[] = {
+ {
+ I2C_BOARD_INFO("nct1008", 0x4C),
+ .platform_data = &aruba_nct1008_pdata,
+ },
+ {
+ I2C_BOARD_INFO("akm8975", 0x0C),
+ .irq = TEGRA_GPIO_TO_IRQ(AKM8975_IRQ_GPIO),
+ }
+};
+
+int __init aruba_sensors_init(void)
+{
+ aruba_isl29018_init();
+ aruba_akm8975_init();
+
+ i2c_register_board_info(0, aruba_i2c0_board_info,
+ ARRAY_SIZE(aruba_i2c0_board_info));
+
+ i2c_register_board_info(2, aruba_i2c2_board_info,
+ ARRAY_SIZE(aruba_i2c2_board_info));
+
+ i2c_register_board_info(4, aruba_i2c4_board_info,
+ ARRAY_SIZE(aruba_i2c4_board_info));
+
+ return 0;
+}
+#else
+int __init aruba_sensors_init(void)
+{
+ return 0;
+}
+#endif
diff --git a/arch/arm/mach-tegra/board-aruba.c b/arch/arm/mach-tegra/board-aruba.c
new file mode 100644
index 000000000000..90d022b1535b
--- /dev/null
+++ b/arch/arm/mach-tegra/board-aruba.c
@@ -0,0 +1,544 @@
+/*
+ * arch/arm/mach-tegra/board-aruba.c
+ *
+ * Copyright (c) 2010-2011, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/serial_8250.h>
+#include <linux/i2c.h>
+#include <linux/i2c/panjit_ts.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/i2c-tegra.h>
+#include <linux/gpio.h>
+#include <linux/gpio_keys.h>
+#include <linux/input.h>
+#include <linux/platform_data/tegra_usb.h>
+#include <mach/clk.h>
+#include <mach/iomap.h>
+#include <mach/irqs.h>
+#include <mach/pinmux.h>
+#include <mach/iomap.h>
+#include <mach/io.h>
+#include <mach/i2s.h>
+#include <mach/audio.h>
+#include <mach/tegra_das.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <mach/usb_phy.h>
+#include <mach/nand.h>
+#include "board.h"
+#include "clock.h"
+#include "board-aruba.h"
+#include "devices.h"
+#include "gpio-names.h"
+#include "fuse.h"
+
+#define ENABLE_OTG 0
+
+static struct plat_serial8250_port debug_uart_platform_data[] = {
+ {
+ .membase = IO_ADDRESS(TEGRA_UARTA_BASE),
+ .mapbase = TEGRA_UARTA_BASE,
+ .irq = INT_UARTA,
+ .flags = UPF_BOOT_AUTOCONF | UPF_FIXED_TYPE,
+ .type = PORT_TEGRA,
+ .iotype = UPIO_MEM,
+ .regshift = 2,
+ .uartclk = 13000000,
+ }, {
+ .flags = 0,
+ }
+};
+
+static struct platform_device debug_uart = {
+ .name = "serial8250",
+ .id = PLAT8250_DEV_PLATFORM,
+ .dev = {
+ .platform_data = debug_uart_platform_data,
+ },
+};
+
+/* !!!FIXME!!! THESE ARE VENTANA SETTINGS */
+static struct tegra_utmip_config utmi_phy_config[] = {
+ [0] = {
+ .hssync_start_delay = 0,
+ .idle_wait_delay = 17,
+ .elastic_limit = 16,
+ .term_range_adj = 6,
+ .xcvr_setup = 15,
+ .xcvr_lsfslew = 2,
+ .xcvr_lsrslew = 2,
+ },
+ [1] = {
+ .hssync_start_delay = 0,
+ .idle_wait_delay = 17,
+ .elastic_limit = 16,
+ .term_range_adj = 6,
+ .xcvr_setup = 8,
+ .xcvr_lsfslew = 2,
+ .xcvr_lsrslew = 2,
+ },
+};
+
+/* !!!FIXME!!! THESE ARE VENTANA SETTINGS */
+static struct tegra_ulpi_config ulpi_phy_config = {
+ .clk = "clk_dev2",
+};
+
+#ifdef CONFIG_BCM4329_RFKILL
+
+static struct resource aruba_bcm4329_rfkill_resources[] = {
+ {
+ .name = "bcm4329_nreset_gpio",
+ .start = TEGRA_GPIO_PU0,
+ .end = TEGRA_GPIO_PU0,
+ .flags = IORESOURCE_IO,
+ },
+ {
+ .name = "bcm4329_nshutdown_gpio",
+ .start = TEGRA_GPIO_PK2,
+ .end = TEGRA_GPIO_PK2,
+ .flags = IORESOURCE_IO,
+ },
+};
+
+static struct platform_device aruba_bcm4329_rfkill_device = {
+ .name = "bcm4329_rfkill",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(aruba_bcm4329_rfkill_resources),
+ .resource = aruba_bcm4329_rfkill_resources,
+};
+
+static noinline void __init aruba_bt_rfkill(void)
+{
+ /*Add Clock Resource*/
+ clk_add_alias("bcm4329_32k_clk", aruba_bcm4329_rfkill_device.name, \
+ "blink", NULL);
+
+ platform_device_register(&aruba_bcm4329_rfkill_device);
+
+ return;
+}
+#else
+static inline void aruba_bt_rfkill(void) { }
+#endif
+
+static __initdata struct tegra_clk_init_table aruba_clk_init_table[] = {
+ /* name parent rate enabled */
+ { "uarta", "clk_m", 13000000, true},
+ { "uartb", "clk_m", 13000000, true},
+ { "uartc", "clk_m", 13000000, true},
+ { "uartd", "clk_m", 13000000, true},
+ { "uarte", "clk_m", 13000000, true},
+ { "pll_m", NULL, 0, true},
+ { "blink", "clk_32k", 32768, false},
+ { "pll_p_out4", "pll_p", 24000000, true },
+ { "pwm", "clk_32k", 32768, false},
+ { "blink", "clk_32k", 32768, false},
+ { "pll_a", NULL, 56448000, true},
+ { "pll_a_out0", NULL, 11289600, true},
+ { "i2s1", "pll_a_out0", 11289600, true},
+ { "i2s2", "pll_a_out0", 11289600, true},
+ { "d_audio", "pll_a_out0", 11289600, false},
+ { "audio_2x", "audio", 22579200, true},
+ { NULL, NULL, 0, 0},
+};
+
+struct tegra_das_platform_data tegra_das_pdata = {
+ .tegra_dap_port_info_table = {
+ /* I2S0 <--> NULL */
+ [0] = {
+ .dac_port = tegra_das_port_none,
+ .codec_type = tegra_audio_codec_type_none,
+ .device_property = {
+ .num_channels = 0,
+ .bits_per_sample = 0,
+ .rate = 0,
+ .master = 0,
+ .lrck_high_left = false,
+ .dac_dap_data_comm_format = 0,
+ },
+ },
+ /* I2S1 <--> Hifi Codec */
+ [1] = {
+ .dac_port = tegra_das_port_i2s1,
+ .codec_type = tegra_audio_codec_type_hifi,
+ .device_property = {
+ .num_channels = 2,
+ .bits_per_sample = 16,
+ .rate = 48000,
+ .master = 0,
+ .lrck_high_left = false,
+ .dac_dap_data_comm_format =
+ dac_dap_data_format_i2s,
+ },
+ },
+ /* I2s2 <--> BB */
+ [2] = {
+ .dac_port = tegra_das_port_i2s2,
+ .codec_type = tegra_audio_codec_type_baseband,
+ .device_property = {
+ .num_channels = 1,
+ .bits_per_sample = 16,
+ .rate = 16000,
+ .master = 0,
+ .lrck_high_left = true,
+ .dac_dap_data_comm_format =
+ dac_dap_data_format_dsp,
+ },
+ },
+ /* I2s3 <--> BT */
+ [3] = {
+ .dac_port = tegra_das_port_i2s3,
+ .codec_type = tegra_audio_codec_type_bluetooth,
+ .device_property = {
+ .num_channels = 1,
+ .bits_per_sample = 16,
+ .rate = 8000,
+ .master = 0,
+ .lrck_high_left = false,
+ .dac_dap_data_comm_format =
+ dac_dap_data_format_dsp,
+ },
+ },
+ [4] = {
+ .dac_port = tegra_das_port_none,
+ .codec_type = tegra_audio_codec_type_none,
+ .device_property = {
+ .num_channels = 0,
+ .bits_per_sample = 0,
+ .rate = 0,
+ .master = 0,
+ .lrck_high_left = false,
+ .dac_dap_data_comm_format = 0,
+ },
+ },
+ },
+};
+
+static struct i2c_board_info __initdata aruba_i2c_bus1_board_info[] = {
+ {
+ I2C_BOARD_INFO("wm8903", 0x1a),
+ },
+};
+
+static struct tegra_i2c_platform_data aruba_i2c1_platform_data = {
+ .adapter_nr = 0,
+ .bus_count = 1,
+ .bus_clk_rate = { 100000, 0 },
+};
+
+#if 0 /* !!!FIXME!!! THESE ARE VENTANA SETTINGS */
+static const struct tegra_pingroup_config i2c2_ddc = {
+ .pingroup = TEGRA_PINGROUP_DDC,
+ .func = TEGRA_MUX_I2C2,
+};
+
+static const struct tegra_pingroup_config i2c2_gen2 = {
+ .pingroup = TEGRA_PINGROUP_PTA,
+ .func = TEGRA_MUX_I2C2,
+};
+#endif
+
+static struct tegra_i2c_platform_data aruba_i2c2_platform_data = {
+ .adapter_nr = 1,
+ .bus_count = 2,
+ .bus_clk_rate = { 100000, 100000 },
+#if 0 /* !!!FIXME!!!! TESE ARE VENTANA SETTINGS */
+ .bus_mux = { &i2c2_ddc, &i2c2_gen2 },
+ .bus_mux_len = { 1, 1 },
+#endif
+};
+
+static struct tegra_i2c_platform_data aruba_i2c3_platform_data = {
+ .adapter_nr = 3,
+ .bus_count = 1,
+ .bus_clk_rate = { 100000, 0 },
+};
+
+static struct tegra_i2c_platform_data aruba_i2c4_platform_data = {
+ .adapter_nr = 4,
+ .bus_count = 1,
+ .bus_clk_rate = { 100000, 0 },
+};
+
+static struct tegra_i2c_platform_data aruba_i2c5_platform_data = {
+ .adapter_nr = 5,
+ .bus_count = 1,
+ .bus_clk_rate = { 100000, 0 },
+};
+
+static void aruba_i2c_init(void)
+{
+ tegra_i2c_device1.dev.platform_data = &aruba_i2c1_platform_data;
+ tegra_i2c_device2.dev.platform_data = &aruba_i2c2_platform_data;
+ tegra_i2c_device3.dev.platform_data = &aruba_i2c3_platform_data;
+ tegra_i2c_device4.dev.platform_data = &aruba_i2c4_platform_data;
+ tegra_i2c_device5.dev.platform_data = &aruba_i2c5_platform_data;
+
+ i2c_register_board_info(0, aruba_i2c_bus1_board_info, 1);
+
+ platform_device_register(&tegra_i2c_device5);
+ platform_device_register(&tegra_i2c_device4);
+ platform_device_register(&tegra_i2c_device3);
+ platform_device_register(&tegra_i2c_device2);
+ platform_device_register(&tegra_i2c_device1);
+}
+
+#define GPIO_KEY(_id, _gpio, _iswake) \
+ { \
+ .code = _id, \
+ .gpio = TEGRA_GPIO_##_gpio, \
+ .active_low = 1, \
+ .desc = #_id, \
+ .type = EV_KEY, \
+ .wakeup = _iswake, \
+ .debounce_interval = 10, \
+ }
+
+// !!!FIXME!!! THESE ARE VENTANA DEFINITIONS
+static struct gpio_keys_button aruba_keys[] = {
+ [0] = GPIO_KEY(KEY_MENU, PQ0, 0),
+ [1] = GPIO_KEY(KEY_HOME, PQ1, 0),
+ [2] = GPIO_KEY(KEY_BACK, PQ2, 0),
+ [3] = GPIO_KEY(KEY_VOLUMEUP, PQ3, 0),
+ [4] = GPIO_KEY(KEY_VOLUMEDOWN, PQ4, 0),
+ [5] = GPIO_KEY(KEY_POWER, PV2, 1),
+};
+
+static struct gpio_keys_platform_data aruba_keys_platform_data = {
+ .buttons = aruba_keys,
+ .nbuttons = ARRAY_SIZE(aruba_keys),
+};
+
+static struct platform_device aruba_keys_device = {
+ .name = "gpio-keys",
+ .id = 0,
+ .dev = {
+ .platform_data = &aruba_keys_platform_data,
+ },
+};
+
+static struct resource tegra_rtc_resources[] = {
+ [0] = {
+ .start = TEGRA_RTC_BASE,
+ .end = TEGRA_RTC_BASE + TEGRA_RTC_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = INT_RTC,
+ .end = INT_RTC,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device tegra_rtc_device = {
+ .name = "tegra_rtc",
+ .id = -1,
+ .resource = tegra_rtc_resources,
+ .num_resources = ARRAY_SIZE(tegra_rtc_resources),
+};
+
+#if defined(CONFIG_MTD_NAND_TEGRA)
+static struct resource nand_resources[] = {
+ [0] = {
+ .start = INT_NANDFLASH,
+ .end = INT_NANDFLASH,
+ .flags = IORESOURCE_IRQ
+ },
+ [1] = {
+ .start = TEGRA_NAND_BASE,
+ .end = TEGRA_NAND_BASE + TEGRA_NAND_SIZE - 1,
+ .flags = IORESOURCE_MEM
+ }
+};
+
+static struct tegra_nand_chip_parms nand_chip_parms[] = {
+ /* Samsung K5E2G1GACM */
+ [0] = {
+ .vendor_id = 0xEC,
+ .device_id = 0xAA,
+ .capacity = 256,
+ .timing = {
+ .trp = 21,
+ .trh = 15,
+ .twp = 21,
+ .twh = 15,
+ .tcs = 31,
+ .twhr = 60,
+ .tcr_tar_trr = 20,
+ .twb = 100,
+ .trp_resp = 30,
+ .tadl = 100,
+ },
+ },
+ /* Hynix H5PS1GB3EFR */
+ [1] = {
+ .vendor_id = 0xAD,
+ .device_id = 0xDC,
+ .capacity = 512,
+ .timing = {
+ .trp = 12,
+ .trh = 10,
+ .twp = 12,
+ .twh = 10,
+ .tcs = 20,
+ .twhr = 80,
+ .tcr_tar_trr = 20,
+ .twb = 100,
+ .trp_resp = 20,
+ .tadl = 70,
+ },
+ },
+};
+
+struct tegra_nand_platform nand_data = {
+ .max_chips = 8,
+ .chip_parms = nand_chip_parms,
+ .nr_chip_parms = ARRAY_SIZE(nand_chip_parms),
+};
+
+struct platform_device tegra_nand_device = {
+ .name = "tegra_nand",
+ .id = -1,
+ .resource = nand_resources,
+ .num_resources = ARRAY_SIZE(nand_resources),
+ .dev = {
+ .platform_data = &nand_data,
+ },
+};
+#endif
+
+static struct platform_device *aruba_devices[] __initdata = {
+#if ENABLE_OTG
+ &tegra_otg_device,
+#endif
+ &debug_uart,
+ &tegra_uartb_device,
+ &tegra_uartc_device,
+ &tegra_uartd_device,
+ &tegra_uarte_device,
+ &tegra_pmu_device,
+ &tegra_rtc_device,
+ &tegra_udc_device,
+#if defined(CONFIG_TEGRA_IOVMM_SMMU)
+ &tegra_smmu_device,
+#endif
+ &aruba_keys_device,
+ &tegra_wdt_device,
+#if defined(CONFIG_SND_HDA_TEGRA)
+ &tegra_hda_device,
+#endif
+ &tegra_avp_device,
+#if defined(CONFIG_MTD_NAND_TEGRA)
+ &tegra_nand_device,
+#endif
+};
+
+static void aruba_keys_init(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(aruba_keys); i++)
+ tegra_gpio_enable(aruba_keys[i].gpio);
+}
+
+static int __init aruba_touch_init(void)
+{
+ return 0;
+}
+
+
+static struct tegra_ehci_platform_data tegra_ehci_pdata[] = {
+ [0] = {
+ .phy_config = &utmi_phy_config[0],
+ .operating_mode = TEGRA_USB_HOST,
+ .power_down_on_bus_suspend = 0,
+ },
+ [1] = {
+ .phy_config = &ulpi_phy_config,
+ .operating_mode = TEGRA_USB_HOST,
+ .power_down_on_bus_suspend = 1,
+ },
+ [2] = {
+ .phy_config = &utmi_phy_config[1],
+ .operating_mode = TEGRA_USB_HOST,
+ .power_down_on_bus_suspend = 0,
+ },
+};
+
+
+static void aruba_usb_init(void)
+{
+ tegra_ehci2_device.dev.platform_data=&tegra_ehci_pdata[1];
+ platform_device_register(&tegra_ehci2_device);
+}
+
+#ifdef CONFIG_SATA_AHCI_TEGRA
+static void aruba_sata_init(void)
+{
+ platform_device_register(&tegra_sata_device);
+}
+#else
+static void aruba_sata_init(void) { }
+#endif
+
+static void __init tegra_aruba_init(void)
+{
+ tegra_clk_init_from_table(aruba_clk_init_table);
+ aruba_pinmux_init();
+
+ platform_add_devices(aruba_devices, ARRAY_SIZE(aruba_devices));
+
+ aruba_sdhci_init();
+ aruba_i2c_init();
+ aruba_regulator_init();
+ aruba_touch_init();
+ aruba_keys_init();
+ aruba_usb_init();
+ aruba_panel_init();
+ aruba_sensors_init();
+ aruba_bt_rfkill();
+ aruba_sata_init();
+ tegra_release_bootloader_fb();
+}
+
+static void __init tegra_aruba_reserve(void)
+{
+#if defined(CONFIG_NVMAP_CONVERT_CARVEOUT_TO_IOVMM)
+ tegra_reserve(0, SZ_4M, 0);
+#else
+ tegra_reserve(SZ_32M, SZ_4M, 0);
+#endif
+}
+
+MACHINE_START(ARUBA, "aruba")
+ .boot_params = 0x80000100,
+ .map_io = tegra_map_common_io,
+ .reserve = tegra_aruba_reserve,
+ .init_early = tegra_init_early,
+ .init_irq = tegra_init_irq,
+ .timer = &tegra_timer,
+ .init_machine = tegra_aruba_init,
+MACHINE_END
diff --git a/arch/arm/mach-tegra/board-aruba.h b/arch/arm/mach-tegra/board-aruba.h
new file mode 100644
index 000000000000..e00e0b071ffb
--- /dev/null
+++ b/arch/arm/mach-tegra/board-aruba.h
@@ -0,0 +1,26 @@
+/*
+ * arch/arm/mach-tegra/board-aruba.h
+ *
+ * Copyright (C) 2010 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 _MACH_TEGRA_BOARD_ARUBA_H
+#define _MACH_TEGRA_BOARD_ARUBA_H
+
+int aruba_regulator_init(void);
+int aruba_sdhci_init(void);
+int aruba_pinmux_init(void);
+int aruba_panel_init(void);
+int aruba_sensors_init(void);
+
+#endif
diff --git a/arch/arm/mach-tegra/board-cardhu-kbc.c b/arch/arm/mach-tegra/board-cardhu-kbc.c
new file mode 100644
index 000000000000..0fffa1582fab
--- /dev/null
+++ b/arch/arm/mach-tegra/board-cardhu-kbc.c
@@ -0,0 +1,275 @@
+/*
+ * arch/arm/mach-tegra/board-cardhu-kbc.c
+ * Keys configuration for Nvidia tegra3 cardhu platform.
+ *
+ * Copyright (C) 2011 NVIDIA, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/device.h>
+#include <linux/gpio.h>
+#include <linux/gpio_keys.h>
+#include <linux/mfd/tps6591x.h>
+#include <linux/mfd/max77663-core.h>
+#include <linux/interrupt_keys.h>
+#include <linux/gpio_scrollwheel.h>
+
+#include <mach/irqs.h>
+#include <mach/io.h>
+#include <mach/iomap.h>
+#include <mach/kbc.h>
+#include "board.h"
+#include "board-cardhu.h"
+
+#include "gpio-names.h"
+#include "devices.h"
+
+#define CARDHU_PM269_ROW_COUNT 2
+#define CARDHU_PM269_COL_COUNT 4
+
+static const u32 kbd_keymap[] = {
+ KEY(0, 0, KEY_POWER),
+ KEY(0, 1, KEY_RESERVED),
+ KEY(0, 2, KEY_VOLUMEUP),
+ KEY(0, 3, KEY_VOLUMEDOWN),
+
+ KEY(1, 0, KEY_HOME),
+ KEY(1, 1, KEY_MENU),
+ KEY(1, 2, KEY_BACK),
+ KEY(1, 3, KEY_SEARCH),
+};
+static const struct matrix_keymap_data keymap_data = {
+ .keymap = kbd_keymap,
+ .keymap_size = ARRAY_SIZE(kbd_keymap),
+};
+
+static struct tegra_kbc_wake_key cardhu_wake_cfg[] = {
+ [0] = {
+ .row = 0,
+ .col = 0,
+ },
+};
+
+static struct tegra_kbc_platform_data cardhu_kbc_platform_data = {
+ .debounce_cnt = 20,
+ .repeat_cnt = 1,
+ .scan_count = 30,
+ .wakeup = true,
+ .keymap_data = &keymap_data,
+ .wake_cnt = 1,
+ .wake_cfg = &cardhu_wake_cfg[0],
+};
+
+int __init cardhu_kbc_init(void)
+{
+ struct tegra_kbc_platform_data *data = &cardhu_kbc_platform_data;
+ int i;
+ struct board_info board_info;
+
+ tegra_get_board_info(&board_info);
+ if ((board_info.board_id == BOARD_E1198) ||
+ (board_info.board_id == BOARD_E1291))
+ return 0;
+
+ pr_info("Registering tegra-kbc\n");
+ tegra_kbc_device.dev.platform_data = &cardhu_kbc_platform_data;
+
+ for (i = 0; i < CARDHU_PM269_ROW_COUNT; i++) {
+ data->pin_cfg[i].num = i;
+ data->pin_cfg[i].is_row = true;
+ data->pin_cfg[i].en = true;
+ }
+ for (i = 0; i < CARDHU_PM269_COL_COUNT; i++) {
+ data->pin_cfg[i + KBC_PIN_GPIO_16].num = i;
+ data->pin_cfg[i + KBC_PIN_GPIO_16].en = true;
+ }
+
+ platform_device_register(&tegra_kbc_device);
+ return 0;
+}
+
+int __init cardhu_scroll_init(void)
+{
+ return 0;
+}
+
+#define GPIO_KEY(_id, _gpio, _iswake) \
+ { \
+ .code = _id, \
+ .gpio = TEGRA_GPIO_##_gpio, \
+ .active_low = 1, \
+ .desc = #_id, \
+ .type = EV_KEY, \
+ .wakeup = _iswake, \
+ .debounce_interval = 10, \
+ }
+
+static struct gpio_keys_button cardhu_keys_e1198[] = {
+ [0] = GPIO_KEY(KEY_HOME, PQ0, 0),
+ [1] = GPIO_KEY(KEY_BACK, PQ1, 0),
+ [2] = GPIO_KEY(KEY_MENU, PQ2, 0),
+ [3] = GPIO_KEY(KEY_SEARCH, PQ3, 0),
+ [4] = GPIO_KEY(KEY_VOLUMEUP, PR0, 0),
+ [5] = GPIO_KEY(KEY_VOLUMEDOWN, PR1, 0),
+ [6] = GPIO_KEY(KEY_POWER, PV0, 1),
+};
+
+static struct gpio_keys_platform_data cardhu_keys_e1198_platform_data = {
+ .buttons = cardhu_keys_e1198,
+ .nbuttons = ARRAY_SIZE(cardhu_keys_e1198),
+};
+
+static struct platform_device cardhu_keys_e1198_device = {
+ .name = "gpio-keys",
+ .id = 0,
+ .dev = {
+ .platform_data = &cardhu_keys_e1198_platform_data,
+ },
+};
+
+static struct gpio_keys_button cardhu_keys_e1291[] = {
+ [0] = GPIO_KEY(KEY_MENU, PR0, 0),
+ [1] = GPIO_KEY(KEY_BACK, PR1, 0),
+ [2] = GPIO_KEY(KEY_HOME, PR2, 0),
+ [3] = GPIO_KEY(KEY_SEARCH, PQ3, 0),
+ [4] = GPIO_KEY(KEY_VOLUMEUP, PQ0, 0),
+ [5] = GPIO_KEY(KEY_VOLUMEDOWN, PQ1, 0),
+};
+
+static struct gpio_keys_button cardhu_keys_e1291_a04[] = {
+ [0] = GPIO_KEY(KEY_MENU, PR0, 0),
+ [1] = GPIO_KEY(KEY_BACK, PR1, 0),
+ [2] = GPIO_KEY(KEY_HOME, PQ2, 0),
+ [3] = GPIO_KEY(KEY_SEARCH, PQ3, 0),
+ [4] = GPIO_KEY(KEY_VOLUMEUP, PQ0, 0),
+ [5] = GPIO_KEY(KEY_VOLUMEDOWN, PQ1, 0),
+};
+
+static struct gpio_keys_platform_data cardhu_keys_e1291_platform_data = {
+ .buttons = cardhu_keys_e1291,
+ .nbuttons = ARRAY_SIZE(cardhu_keys_e1291),
+};
+
+static struct platform_device cardhu_keys_e1291_device = {
+ .name = "gpio-keys",
+ .id = 0,
+ .dev = {
+ .platform_data = &cardhu_keys_e1291_platform_data,
+ },
+};
+
+#define INT_KEY(_id, _irq, _iswake, _deb_int) \
+ { \
+ .code = _id, \
+ .irq = _irq, \
+ .active_low = 1, \
+ .desc = #_id, \
+ .type = EV_KEY, \
+ .wakeup = _iswake, \
+ .debounce_interval = _deb_int, \
+ }
+static struct interrupt_keys_button cardhu_int_keys[] = {
+ [0] = INT_KEY(KEY_POWER, TPS6591X_IRQ_BASE + TPS6591X_INT_PWRON, 0, 100),
+};
+
+static struct interrupt_keys_button cardhu_pm298_int_keys[] = {
+ [0] = INT_KEY(KEY_POWER, MAX77663_IRQ_BASE + MAX77663_IRQ_ONOFF_EN0_FALLING, 0, 100),
+ [1] = INT_KEY(KEY_POWER, MAX77663_IRQ_BASE + MAX77663_IRQ_ONOFF_EN0_1SEC, 0, 3000),
+};
+
+static struct interrupt_keys_button cardhu_pm299_int_keys[] = {
+ [0] = INT_KEY(KEY_POWER, RICOH583_IRQ_BASE + RICOH583_IRQ_ONKEY, 0, 100),
+};
+
+static struct interrupt_keys_platform_data cardhu_int_keys_pdata = {
+ .int_buttons = cardhu_int_keys,
+ .nbuttons = ARRAY_SIZE(cardhu_int_keys),
+};
+
+static struct platform_device cardhu_int_keys_device = {
+ .name = "interrupt-keys",
+ .id = 0,
+ .dev = {
+ .platform_data = &cardhu_int_keys_pdata,
+ },
+};
+
+int __init cardhu_keys_init(void)
+{
+ int i;
+ struct board_info board_info;
+ struct board_info pmu_board_info;
+
+ tegra_get_board_info(&board_info);
+ if (!((board_info.board_id == BOARD_E1198) ||
+ (board_info.board_id == BOARD_E1291) ||
+ (board_info.board_id == BOARD_E1257) ||
+ (board_info.board_id == BOARD_PM305) ||
+ (board_info.board_id == BOARD_PM311) ||
+ (board_info.board_id == BOARD_PM269)))
+ return 0;
+
+ pr_info("Registering gpio keys\n");
+
+ if (board_info.board_id == BOARD_E1291) {
+ if (board_info.fab >= BOARD_FAB_A04) {
+ cardhu_keys_e1291_platform_data.buttons =
+ cardhu_keys_e1291_a04;
+ cardhu_keys_e1291_platform_data.nbuttons =
+ ARRAY_SIZE(cardhu_keys_e1291_a04);
+ }
+
+ /* Enable gpio mode for other pins */
+ for (i = 0; i < cardhu_keys_e1291_platform_data.nbuttons; i++)
+ tegra_gpio_enable(cardhu_keys_e1291_platform_data.
+ buttons[i].gpio);
+
+ platform_device_register(&cardhu_keys_e1291_device);
+ } else if (board_info.board_id == BOARD_E1198) {
+ /* For E1198 */
+ for (i = 0; i < ARRAY_SIZE(cardhu_keys_e1198); i++)
+ tegra_gpio_enable(cardhu_keys_e1198[i].gpio);
+
+ platform_device_register(&cardhu_keys_e1198_device);
+ }
+
+ /* Register on-key through pmu interrupt */
+ tegra_get_pmu_board_info(&pmu_board_info);
+
+ if (pmu_board_info.board_id == BOARD_PMU_PM298) {
+ cardhu_int_keys_pdata.int_buttons = cardhu_pm298_int_keys;
+ cardhu_int_keys_pdata.nbuttons =
+ ARRAY_SIZE(cardhu_pm298_int_keys);
+ }
+
+ if (pmu_board_info.board_id == BOARD_PMU_PM299) {
+ cardhu_int_keys_pdata.int_buttons = cardhu_pm299_int_keys;
+ cardhu_int_keys_pdata.nbuttons =
+ ARRAY_SIZE(cardhu_pm299_int_keys);
+ }
+
+ if ((board_info.board_id == BOARD_E1291) ||
+ (board_info.board_id == BOARD_E1198) ||
+ (board_info.board_id == BOARD_E1257) ||
+ (board_info.board_id == BOARD_PM305) ||
+ (board_info.board_id == BOARD_PM311) ||
+ (board_info.board_id == BOARD_PM269))
+ platform_device_register(&cardhu_int_keys_device);
+ return 0;
+}
diff --git a/arch/arm/mach-tegra/board-cardhu-memory.c b/arch/arm/mach-tegra/board-cardhu-memory.c
new file mode 100644
index 000000000000..7cf947093fa9
--- /dev/null
+++ b/arch/arm/mach-tegra/board-cardhu-memory.c
@@ -0,0 +1,2816 @@
+/*
+ * Copyright (C) 2011 NVIDIA, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+
+#include "board.h"
+#include "board-cardhu.h"
+#include "tegra3_emc.h"
+#include "fuse.h"
+
+
+static const struct tegra_emc_table cardhu_emc_tables_h5tc2g[] = {
+ {
+ 0x30, /* Rev 3.0 */
+ 27000, /* SDRAM frquency */
+ {
+ 0x00000001, /* EMC_RC */
+ 0x00000004, /* EMC_RFC */
+ 0x00000000, /* EMC_RAS */
+ 0x00000000, /* EMC_RP */
+ 0x00000002, /* EMC_R2W */
+ 0x0000000a, /* EMC_W2R */
+ 0x00000003, /* EMC_R2P */
+ 0x0000000b, /* EMC_W2P */
+ 0x00000000, /* EMC_RD_RCD */
+ 0x00000000, /* EMC_WR_RCD */
+ 0x00000003, /* EMC_RRD */
+ 0x00000001, /* EMC_REXT */
+ 0x00000000, /* EMC_WEXT */
+ 0x00000005, /* EMC_WDV */
+ 0x00000005, /* EMC_QUSE */
+ 0x00000004, /* EMC_QRST */
+ 0x00000007, /* EMC_QSAFE */
+ 0x0000000d, /* EMC_RDV */
+ 0x000000cb, /* EMC_REFRESH */
+ 0x00000000, /* EMC_BURST_REFRESH_NUM */
+ 0x00000032, /* EMC_PRE_REFRESH_REQ_CNT */
+ 0x00000002, /* EMC_PDEX2WR */
+ 0x00000002, /* EMC_PDEX2RD */
+ 0x00000001, /* EMC_PCHG2PDEN */
+ 0x00000000, /* EMC_ACT2PDEN */
+ 0x00000007, /* EMC_AR2PDEN */
+ 0x0000000f, /* EMC_RW2PDEN */
+ 0x00000005, /* EMC_TXSR */
+ 0x00000005, /* EMC_TXSRDLL */
+ 0x00000004, /* EMC_TCKE */
+ 0x00000001, /* EMC_TFAW */
+ 0x00000000, /* EMC_TRPAB */
+ 0x00000004, /* EMC_TCLKSTABLE */
+ 0x00000005, /* EMC_TCLKSTOP */
+ 0x000000d3, /* EMC_TREFBW */
+ 0x00000000, /* EMC_QUSE_EXTRA */
+ 0x00000004, /* EMC_FBIO_CFG6 */
+ 0x00000000, /* EMC_ODT_WRITE */
+ 0x00000000, /* EMC_ODT_READ */
+ 0x00006288, /* EMC_FBIO_CFG5 */
+ 0xd0780421, /* EMC_CFG_DIG_DLL */
+ 0x00008000, /* EMC_CFG_DIG_DLL_PERIOD */
+ 0x00080000, /* EMC_DLL_XFORM_DQS0 */
+ 0x00080000, /* EMC_DLL_XFORM_DQS1 */
+ 0x00080000, /* EMC_DLL_XFORM_DQS2 */
+ 0x00080000, /* EMC_DLL_XFORM_DQS3 */
+ 0x00080000, /* EMC_DLL_XFORM_DQS4 */
+ 0x00080000, /* EMC_DLL_XFORM_DQS5 */
+ 0x00080000, /* EMC_DLL_XFORM_DQS6 */
+ 0x00080000, /* EMC_DLL_XFORM_DQS7 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE0 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE1 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE2 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE3 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE4 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE5 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE6 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE7 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS0 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS1 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS2 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS3 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS4 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS5 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS6 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS7 */
+ 0x00080000, /* EMC_DLL_XFORM_DQ0 */
+ 0x00080000, /* EMC_DLL_XFORM_DQ1 */
+ 0x00080000, /* EMC_DLL_XFORM_DQ2 */
+ 0x00080000, /* EMC_DLL_XFORM_DQ3 */
+ 0x000003e0, /* EMC_XM2CMDPADCTRL */
+ 0x0800211d, /* EMC_XM2DQSPADCTRL2 */
+ 0x00000000, /* EMC_XM2DQPADCTRL2 */
+ 0x77ffc084, /* EMC_XM2CLKPADCTRL */
+ 0x01f1f108, /* EMC_XM2COMPPADCTRL */
+ 0x07075504, /* EMC_XM2VTTGENPADCTRL */
+ 0x00000007, /* EMC_XM2VTTGENPADCTRL2 */
+ 0x0800012d, /* EMC_XM2QUSEPADCTRL */
+ 0x08000000, /* EMC_XM2DQSPADCTRL3 */
+ 0x00000802, /* EMC_CTT_TERM_CTRL */
+ 0x00000000, /* EMC_ZCAL_INTERVAL */
+ 0x00000040, /* EMC_ZCAL_WAIT_CNT */
+ 0x000c000c, /* EMC_MRS_WAIT_CNT */
+ 0xa0f10000, /* EMC_AUTO_CAL_CONFIG */
+ 0x00000000, /* EMC_CTT */
+ 0x00000000, /* EMC_CTT_DURATION */
+ 0x8000029e, /* EMC_DYN_SELF_REF_CONTROL */
+ 0x00000001, /* MC_EMEM_ARB_CFG */
+ 0x8000000d, /* MC_EMEM_ARB_OUTSTANDING_REQ */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_RCD */
+ 0x00000004, /* MC_EMEM_ARB_TIMING_RP */
+ 0x00000005, /* MC_EMEM_ARB_TIMING_RC */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_RAS */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_FAW */
+ 0x00000003, /* MC_EMEM_ARB_TIMING_RRD */
+ 0x00000004, /* MC_EMEM_ARB_TIMING_RAP2PRE */
+ 0x0000000f, /* MC_EMEM_ARB_TIMING_WAP2PRE */
+ 0x00000006, /* MC_EMEM_ARB_TIMING_R2R */
+ 0x00000005, /* MC_EMEM_ARB_TIMING_W2W */
+ 0x00000007, /* MC_EMEM_ARB_TIMING_R2W */
+ 0x0000000f, /* MC_EMEM_ARB_TIMING_W2R */
+ 0x0f070506, /* MC_EMEM_ARB_DA_TURNS */
+ 0x00140905, /* MC_EMEM_ARB_DA_COVERS */
+ 0x78430306, /* MC_EMEM_ARB_MISC0 */
+ 0x001f0001, /* MC_EMEM_ARB_RING1_THROTTLE */
+ },
+ 0x00000040, /* EMC_ZCAL_WAIT_CNT after clock change */
+ 0x001fffff, /* EMC_AUTO_CAL_INTERVAL */
+ 0x00000000, /* EMC_CFG.PERIODIC_QRST */
+ 0x00001221, /* Mode Register 0 */
+ 0x00100003, /* Mode Register 1 */
+ 0x00200008, /* Mode Register 2 */
+ },
+ {
+ 0x30, /* Rev 3.0 */
+ 54000, /* SDRAM frquency */
+ {
+ 0x00000002, /* EMC_RC */
+ 0x00000008, /* EMC_RFC */
+ 0x00000001, /* EMC_RAS */
+ 0x00000000, /* EMC_RP */
+ 0x00000002, /* EMC_R2W */
+ 0x0000000a, /* EMC_W2R */
+ 0x00000003, /* EMC_R2P */
+ 0x0000000b, /* EMC_W2P */
+ 0x00000000, /* EMC_RD_RCD */
+ 0x00000000, /* EMC_WR_RCD */
+ 0x00000003, /* EMC_RRD */
+ 0x00000001, /* EMC_REXT */
+ 0x00000000, /* EMC_WEXT */
+ 0x00000005, /* EMC_WDV */
+ 0x00000005, /* EMC_QUSE */
+ 0x00000004, /* EMC_QRST */
+ 0x00000007, /* EMC_QSAFE */
+ 0x0000000d, /* EMC_RDV */
+ 0x00000198, /* EMC_REFRESH */
+ 0x00000000, /* EMC_BURST_REFRESH_NUM */
+ 0x00000066, /* EMC_PRE_REFRESH_REQ_CNT */
+ 0x00000002, /* EMC_PDEX2WR */
+ 0x00000002, /* EMC_PDEX2RD */
+ 0x00000001, /* EMC_PCHG2PDEN */
+ 0x00000000, /* EMC_ACT2PDEN */
+ 0x00000007, /* EMC_AR2PDEN */
+ 0x0000000f, /* EMC_RW2PDEN */
+ 0x0000000a, /* EMC_TXSR */
+ 0x0000000a, /* EMC_TXSRDLL */
+ 0x00000004, /* EMC_TCKE */
+ 0x00000002, /* EMC_TFAW */
+ 0x00000000, /* EMC_TRPAB */
+ 0x00000004, /* EMC_TCLKSTABLE */
+ 0x00000005, /* EMC_TCLKSTOP */
+ 0x000001a6, /* EMC_TREFBW */
+ 0x00000000, /* EMC_QUSE_EXTRA */
+ 0x00000004, /* EMC_FBIO_CFG6 */
+ 0x00000000, /* EMC_ODT_WRITE */
+ 0x00000000, /* EMC_ODT_READ */
+ 0x00006288, /* EMC_FBIO_CFG5 */
+ 0xd0780421, /* EMC_CFG_DIG_DLL */
+ 0x00008000, /* EMC_CFG_DIG_DLL_PERIOD */
+ 0x00080000, /* EMC_DLL_XFORM_DQS0 */
+ 0x00080000, /* EMC_DLL_XFORM_DQS1 */
+ 0x00080000, /* EMC_DLL_XFORM_DQS2 */
+ 0x00080000, /* EMC_DLL_XFORM_DQS3 */
+ 0x00080000, /* EMC_DLL_XFORM_DQS4 */
+ 0x00080000, /* EMC_DLL_XFORM_DQS5 */
+ 0x00080000, /* EMC_DLL_XFORM_DQS6 */
+ 0x00080000, /* EMC_DLL_XFORM_DQS7 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE0 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE1 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE2 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE3 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE4 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE5 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE6 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE7 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS0 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS1 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS2 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS3 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS4 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS5 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS6 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS7 */
+ 0x00080000, /* EMC_DLL_XFORM_DQ0 */
+ 0x00080000, /* EMC_DLL_XFORM_DQ1 */
+ 0x00080000, /* EMC_DLL_XFORM_DQ2 */
+ 0x00080000, /* EMC_DLL_XFORM_DQ3 */
+ 0x000003e0, /* EMC_XM2CMDPADCTRL */
+ 0x0800211d, /* EMC_XM2DQSPADCTRL2 */
+ 0x00000000, /* EMC_XM2DQPADCTRL2 */
+ 0x77ffc084, /* EMC_XM2CLKPADCTRL */
+ 0x01f1f108, /* EMC_XM2COMPPADCTRL */
+ 0x07075504, /* EMC_XM2VTTGENPADCTRL */
+ 0x00000007, /* EMC_XM2VTTGENPADCTRL2 */
+ 0x0800012d, /* EMC_XM2QUSEPADCTRL */
+ 0x08000000, /* EMC_XM2DQSPADCTRL3 */
+ 0x00000802, /* EMC_CTT_TERM_CTRL */
+ 0x00000000, /* EMC_ZCAL_INTERVAL */
+ 0x00000040, /* EMC_ZCAL_WAIT_CNT */
+ 0x000c000c, /* EMC_MRS_WAIT_CNT */
+ 0xa0f10000, /* EMC_AUTO_CAL_CONFIG */
+ 0x00000000, /* EMC_CTT */
+ 0x00000000, /* EMC_CTT_DURATION */
+ 0x80000439, /* EMC_DYN_SELF_REF_CONTROL */
+ 0x00000001, /* MC_EMEM_ARB_CFG */
+ 0x80000014, /* MC_EMEM_ARB_OUTSTANDING_REQ */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_RCD */
+ 0x00000004, /* MC_EMEM_ARB_TIMING_RP */
+ 0x00000005, /* MC_EMEM_ARB_TIMING_RC */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_RAS */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_FAW */
+ 0x00000003, /* MC_EMEM_ARB_TIMING_RRD */
+ 0x00000004, /* MC_EMEM_ARB_TIMING_RAP2PRE */
+ 0x0000000f, /* MC_EMEM_ARB_TIMING_WAP2PRE */
+ 0x00000006, /* MC_EMEM_ARB_TIMING_R2R */
+ 0x00000005, /* MC_EMEM_ARB_TIMING_W2W */
+ 0x00000007, /* MC_EMEM_ARB_TIMING_R2W */
+ 0x0000000f, /* MC_EMEM_ARB_TIMING_W2R */
+ 0x0f070506, /* MC_EMEM_ARB_DA_TURNS */
+ 0x00140905, /* MC_EMEM_ARB_DA_COVERS */
+ 0x78430506, /* MC_EMEM_ARB_MISC0 */
+ 0x001f0001, /* MC_EMEM_ARB_RING1_THROTTLE */
+ },
+ 0x00000040, /* EMC_ZCAL_WAIT_CNT after clock change */
+ 0x001fffff, /* EMC_AUTO_CAL_INTERVAL */
+ 0x00000000, /* EMC_CFG.PERIODIC_QRST */
+ 0x00001221, /* Mode Register 0 */
+ 0x00100003, /* Mode Register 1 */
+ 0x00200008, /* Mode Register 2 */
+ },
+ {
+ 0x30, /* Rev 3.0 */
+ 108000, /* SDRAM frquency */
+ {
+ 0x00000005, /* EMC_RC */
+ 0x00000011, /* EMC_RFC */
+ 0x00000003, /* EMC_RAS */
+ 0x00000001, /* EMC_RP */
+ 0x00000002, /* EMC_R2W */
+ 0x0000000a, /* EMC_W2R */
+ 0x00000003, /* EMC_R2P */
+ 0x0000000b, /* EMC_W2P */
+ 0x00000001, /* EMC_RD_RCD */
+ 0x00000001, /* EMC_WR_RCD */
+ 0x00000003, /* EMC_RRD */
+ 0x00000001, /* EMC_REXT */
+ 0x00000000, /* EMC_WEXT */
+ 0x00000005, /* EMC_WDV */
+ 0x00000005, /* EMC_QUSE */
+ 0x00000004, /* EMC_QRST */
+ 0x00000007, /* EMC_QSAFE */
+ 0x0000000d, /* EMC_RDV */
+ 0x00000330, /* EMC_REFRESH */
+ 0x00000000, /* EMC_BURST_REFRESH_NUM */
+ 0x000000cc, /* EMC_PRE_REFRESH_REQ_CNT */
+ 0x00000002, /* EMC_PDEX2WR */
+ 0x00000002, /* EMC_PDEX2RD */
+ 0x00000001, /* EMC_PCHG2PDEN */
+ 0x00000000, /* EMC_ACT2PDEN */
+ 0x00000007, /* EMC_AR2PDEN */
+ 0x0000000f, /* EMC_RW2PDEN */
+ 0x00000013, /* EMC_TXSR */
+ 0x00000013, /* EMC_TXSRDLL */
+ 0x00000004, /* EMC_TCKE */
+ 0x00000004, /* EMC_TFAW */
+ 0x00000000, /* EMC_TRPAB */
+ 0x00000004, /* EMC_TCLKSTABLE */
+ 0x00000005, /* EMC_TCLKSTOP */
+ 0x0000034b, /* EMC_TREFBW */
+ 0x00000000, /* EMC_QUSE_EXTRA */
+ 0x00000004, /* EMC_FBIO_CFG6 */
+ 0x00000000, /* EMC_ODT_WRITE */
+ 0x00000000, /* EMC_ODT_READ */
+ 0x00006288, /* EMC_FBIO_CFG5 */
+ 0xd0780421, /* EMC_CFG_DIG_DLL */
+ 0x00008000, /* EMC_CFG_DIG_DLL_PERIOD */
+ 0x00080000, /* EMC_DLL_XFORM_DQS0 */
+ 0x00080000, /* EMC_DLL_XFORM_DQS1 */
+ 0x00080000, /* EMC_DLL_XFORM_DQS2 */
+ 0x00080000, /* EMC_DLL_XFORM_DQS3 */
+ 0x00080000, /* EMC_DLL_XFORM_DQS4 */
+ 0x00080000, /* EMC_DLL_XFORM_DQS5 */
+ 0x00080000, /* EMC_DLL_XFORM_DQS6 */
+ 0x00080000, /* EMC_DLL_XFORM_DQS7 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE0 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE1 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE2 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE3 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE4 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE5 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE6 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE7 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS0 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS1 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS2 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS3 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS4 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS5 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS6 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS7 */
+ 0x00080000, /* EMC_DLL_XFORM_DQ0 */
+ 0x00080000, /* EMC_DLL_XFORM_DQ1 */
+ 0x00080000, /* EMC_DLL_XFORM_DQ2 */
+ 0x00080000, /* EMC_DLL_XFORM_DQ3 */
+ 0x000003e0, /* EMC_XM2CMDPADCTRL */
+ 0x0800211d, /* EMC_XM2DQSPADCTRL2 */
+ 0x00000000, /* EMC_XM2DQPADCTRL2 */
+ 0x77ffc084, /* EMC_XM2CLKPADCTRL */
+ 0x01f1f108, /* EMC_XM2COMPPADCTRL */
+ 0x07075504, /* EMC_XM2VTTGENPADCTRL */
+ 0x00000007, /* EMC_XM2VTTGENPADCTRL2 */
+ 0x0800012d, /* EMC_XM2QUSEPADCTRL */
+ 0x08000000, /* EMC_XM2DQSPADCTRL3 */
+ 0x00000802, /* EMC_CTT_TERM_CTRL */
+ 0x00000000, /* EMC_ZCAL_INTERVAL */
+ 0x00000040, /* EMC_ZCAL_WAIT_CNT */
+ 0x000c000c, /* EMC_MRS_WAIT_CNT */
+ 0xa0f10000, /* EMC_AUTO_CAL_CONFIG */
+ 0x00000000, /* EMC_CTT */
+ 0x00000000, /* EMC_CTT_DURATION */
+ 0x8000076e, /* EMC_DYN_SELF_REF_CONTROL */
+ 0x00000003, /* MC_EMEM_ARB_CFG */
+ 0x80000027, /* MC_EMEM_ARB_OUTSTANDING_REQ */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_RCD */
+ 0x00000004, /* MC_EMEM_ARB_TIMING_RP */
+ 0x00000006, /* MC_EMEM_ARB_TIMING_RC */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_RAS */
+ 0x00000003, /* MC_EMEM_ARB_TIMING_FAW */
+ 0x00000003, /* MC_EMEM_ARB_TIMING_RRD */
+ 0x00000004, /* MC_EMEM_ARB_TIMING_RAP2PRE */
+ 0x0000000f, /* MC_EMEM_ARB_TIMING_WAP2PRE */
+ 0x00000006, /* MC_EMEM_ARB_TIMING_R2R */
+ 0x00000005, /* MC_EMEM_ARB_TIMING_W2W */
+ 0x00000007, /* MC_EMEM_ARB_TIMING_R2W */
+ 0x0000000f, /* MC_EMEM_ARB_TIMING_W2R */
+ 0x0f070506, /* MC_EMEM_ARB_DA_TURNS */
+ 0x00140906, /* MC_EMEM_ARB_DA_COVERS */
+ 0x78440a07, /* MC_EMEM_ARB_MISC0 */
+ 0x001f0001, /* MC_EMEM_ARB_RING1_THROTTLE */
+ },
+ 0x00000040, /* EMC_ZCAL_WAIT_CNT after clock change */
+ 0x001fffff, /* EMC_AUTO_CAL_INTERVAL */
+ 0x00000000, /* EMC_CFG.PERIODIC_QRST */
+ 0x00001221, /* Mode Register 0 */
+ 0x00100003, /* Mode Register 1 */
+ 0x00200008, /* Mode Register 2 */
+ },
+ {
+ 0x30, /* Rev 3.0 */
+ 416000, /* SDRAM frequency */
+ {
+ 0x00000013, /* EMC_RC */
+ 0x00000041, /* EMC_RFC */
+ 0x0000000d, /* EMC_RAS */
+ 0x00000004, /* EMC_RP */
+ 0x00000002, /* EMC_R2W */
+ 0x00000009, /* EMC_W2R */
+ 0x00000002, /* EMC_R2P */
+ 0x0000000c, /* EMC_W2P */
+ 0x00000004, /* EMC_RD_RCD */
+ 0x00000004, /* EMC_WR_RCD */
+ 0x00000002, /* EMC_RRD */
+ 0x00000001, /* EMC_REXT */
+ 0x00000000, /* EMC_WEXT */
+ 0x00000005, /* EMC_WDV */
+ 0x00000008, /* EMC_QUSE */
+ 0x00000006, /* EMC_QRST */
+ 0x00000008, /* EMC_QSAFE */
+ 0x00000010, /* EMC_RDV */
+ 0x00000c6c, /* EMC_REFRESH */
+ 0x00000000, /* EMC_BURST_REFRESH_NUM */
+ 0x0000031b, /* EMC_PRE_REFRESH_REQ_CNT */
+ 0x00000001, /* EMC_PDEX2WR */
+ 0x00000001, /* EMC_PDEX2RD */
+ 0x00000001, /* EMC_PCHG2PDEN */
+ 0x00000000, /* EMC_ACT2PDEN */
+ 0x00000008, /* EMC_AR2PDEN */
+ 0x00000011, /* EMC_RW2PDEN */
+ 0x00000047, /* EMC_TXSR */
+ 0x00000200, /* EMC_TXSRDLL */
+ 0x00000004, /* EMC_TCKE */
+ 0x0000000d, /* EMC_TFAW */
+ 0x00000000, /* EMC_TRPAB */
+ 0x00000004, /* EMC_TCLKSTABLE */
+ 0x00000005, /* EMC_TCLKSTOP */
+ 0x00000cad, /* EMC_TREFBW */
+ 0x00000000, /* EMC_QUSE_EXTRA */
+ 0x00000006, /* EMC_FBIO_CFG6 */
+ 0x00000000, /* EMC_ODT_WRITE */
+ 0x00000000, /* EMC_ODT_READ */
+ 0x00007088, /* EMC_FBIO_CFG5 */
+ 0xf0120441, /* EMC_CFG_DIG_DLL */
+ 0x00008000, /* EMC_CFG_DIG_DLL_PERIOD */
+ 0x00010000, /* EMC_DLL_XFORM_DQS0 */
+ 0x00010000, /* EMC_DLL_XFORM_DQS1 */
+ 0x00010000, /* EMC_DLL_XFORM_DQS2 */
+ 0x00010000, /* EMC_DLL_XFORM_DQS3 */
+ 0x00010000, /* EMC_DLL_XFORM_DQS4 */
+ 0x00010000, /* EMC_DLL_XFORM_DQS5 */
+ 0x00010000, /* EMC_DLL_XFORM_DQS6 */
+ 0x00010000, /* EMC_DLL_XFORM_DQS7 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE0 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE1 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE2 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE3 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE4 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE5 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE6 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE7 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS0 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS1 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS2 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS3 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS4 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS5 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS6 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS7 */
+ 0x00020000, /* EMC_DLL_XFORM_DQ0 */
+ 0x00020000, /* EMC_DLL_XFORM_DQ1 */
+ 0x00020000, /* EMC_DLL_XFORM_DQ2 */
+ 0x00020000, /* EMC_DLL_XFORM_DQ3 */
+ 0x000006a0, /* EMC_XM2CMDPADCTRL */
+ 0x0800013d, /* EMC_XM2DQSPADCTRL2 */
+ 0x00000000, /* EMC_XM2DQPADCTRL2 */
+ 0x77ffc084, /* EMC_XM2CLKPADCTRL */
+ 0x01f1f50f, /* EMC_XM2COMPPADCTRL */
+ 0x07077404, /* EMC_XM2VTTGENPADCTRL */
+ 0x00000007, /* EMC_XM2VTTGENPADCTRL2 */
+ 0x0800011d, /* EMC_XM2QUSEPADCTRL */
+ 0x08000021, /* EMC_XM2DQSPADCTRL3 */
+ 0x00000802, /* EMC_CTT_TERM_CTRL */
+ 0x00000000, /* EMC_ZCAL_INTERVAL */
+ 0x00000040, /* EMC_ZCAL_WAIT_CNT */
+ 0x01be000c, /* EMC_MRS_WAIT_CNT */
+ 0xa0f10404, /* EMC_AUTO_CAL_CONFIG */
+ 0x00000000, /* EMC_CTT */
+ 0x00000000, /* EMC_CTT_DURATION */
+ 0x000020ae, /* EMC_DYN_SELF_REF_CONTROL */
+ 0x00000006, /* MC_EMEM_ARB_CFG */
+ 0x8000004b, /* MC_EMEM_ARB_OUTSTANDING_REQ */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_RCD */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_RP */
+ 0x0000000a, /* MC_EMEM_ARB_TIMING_RC */
+ 0x00000006, /* MC_EMEM_ARB_TIMING_RAS */
+ 0x00000006, /* MC_EMEM_ARB_TIMING_FAW */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_RRD */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_RAP2PRE */
+ 0x00000009, /* MC_EMEM_ARB_TIMING_WAP2PRE */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_R2R */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_W2W */
+ 0x00000003, /* MC_EMEM_ARB_TIMING_R2W */
+ 0x00000006, /* MC_EMEM_ARB_TIMING_W2R */
+ 0x06030202, /* MC_EMEM_ARB_DA_TURNS */
+ 0x000e070a, /* MC_EMEM_ARB_DA_COVERS */
+ 0x7027130b, /* MC_EMEM_ARB_MISC0 */
+ 0x001f0000, /* MC_EMEM_ARB_RING1_THROTTLE */
+ },
+ 0x00000040, /* EMC_ZCAL_WAIT_CNT after clock change */
+ 0x00000010, /* EMC_AUTO_CAL_INTERVAL */
+ 0x00000000, /* EMC_CFG.PERIODIC_QRST */
+ 0x00001941, /* Mode Register 0 */
+ 0x00100002, /* Mode Register 1 */
+ 0x00200008, /* Mode Register 2 */
+ },
+ {
+ 0x30, /* Rev 3.0 */
+ 533000, /* SDRAM frquency */
+ {
+ 0x00000018, /* EMC_RC */
+ 0x00000054, /* EMC_RFC */
+ 0x00000011, /* EMC_RAS */
+ 0x00000006, /* EMC_RP */
+ 0x00000003, /* EMC_R2W */
+ 0x00000009, /* EMC_W2R */
+ 0x00000002, /* EMC_R2P */
+ 0x0000000d, /* EMC_W2P */
+ 0x00000006, /* EMC_RD_RCD */
+ 0x00000006, /* EMC_WR_RCD */
+ 0x00000002, /* EMC_RRD */
+ 0x00000001, /* EMC_REXT */
+ 0x00000000, /* EMC_WEXT */
+ 0x00000005, /* EMC_WDV */
+ 0x00000008, /* EMC_QUSE */
+ 0x00000006, /* EMC_QRST */
+ 0x00000008, /* EMC_QSAFE */
+ 0x00000010, /* EMC_RDV */
+ 0x00000ffd, /* EMC_REFRESH */
+ 0x00000000, /* EMC_BURST_REFRESH_NUM */
+ 0x000003ff, /* EMC_PRE_REFRESH_REQ_CNT */
+ 0x00000002, /* EMC_PDEX2WR */
+ 0x00000002, /* EMC_PDEX2RD */
+ 0x00000001, /* EMC_PCHG2PDEN */
+ 0x00000000, /* EMC_ACT2PDEN */
+ 0x0000000a, /* EMC_AR2PDEN */
+ 0x00000012, /* EMC_RW2PDEN */
+ 0x0000005b, /* EMC_TXSR */
+ 0x00000200, /* EMC_TXSRDLL */
+ 0x00000004, /* EMC_TCKE */
+ 0x00000010, /* EMC_TFAW */
+ 0x00000000, /* EMC_TRPAB */
+ 0x00000005, /* EMC_TCLKSTABLE */
+ 0x00000006, /* EMC_TCLKSTOP */
+ 0x0000103e, /* EMC_TREFBW */
+ 0x00000000, /* EMC_QUSE_EXTRA */
+ 0x00000006, /* EMC_FBIO_CFG6 */
+ 0x00000000, /* EMC_ODT_WRITE */
+ 0x00000000, /* EMC_ODT_READ */
+ 0x00007088, /* EMC_FBIO_CFG5 */
+ 0xf0120441, /* EMC_CFG_DIG_DLL */
+ 0x00008000, /* EMC_CFG_DIG_DLL_PERIOD */
+ 0x00010000, /* EMC_DLL_XFORM_DQS0 */
+ 0x00010000, /* EMC_DLL_XFORM_DQS1 */
+ 0x00010000, /* EMC_DLL_XFORM_DQS2 */
+ 0x00010000, /* EMC_DLL_XFORM_DQS3 */
+ 0x00010000, /* EMC_DLL_XFORM_DQS4 */
+ 0x00010000, /* EMC_DLL_XFORM_DQS5 */
+ 0x00010000, /* EMC_DLL_XFORM_DQS6 */
+ 0x00010000, /* EMC_DLL_XFORM_DQS7 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE0 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE1 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE2 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE3 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE4 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE5 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE6 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE7 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS0 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS1 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS2 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS3 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS4 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS5 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS6 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS7 */
+ 0x00020000, /* EMC_DLL_XFORM_DQ0 */
+ 0x00020000, /* EMC_DLL_XFORM_DQ1 */
+ 0x00020000, /* EMC_DLL_XFORM_DQ2 */
+ 0x00020000, /* EMC_DLL_XFORM_DQ3 */
+ 0x000006a0, /* EMC_XM2CMDPADCTRL */
+ 0x0800013d, /* EMC_XM2DQSPADCTRL2 */
+ 0x00000000, /* EMC_XM2DQPADCTRL2 */
+ 0x77ffc084, /* EMC_XM2CLKPADCTRL */
+ 0x01f1f50f, /* EMC_XM2COMPPADCTRL */
+ 0x07077404, /* EMC_XM2VTTGENPADCTRL */
+ 0x00000007, /* EMC_XM2VTTGENPADCTRL2 */
+ 0x0800011d, /* EMC_XM2QUSEPADCTRL */
+ 0x08000021, /* EMC_XM2DQSPADCTRL3 */
+ 0x00000802, /* EMC_CTT_TERM_CTRL */
+ 0x00000000, /* EMC_ZCAL_INTERVAL */
+ 0x00000040, /* EMC_ZCAL_WAIT_CNT */
+ 0x01ab000c, /* EMC_MRS_WAIT_CNT */
+ 0xa0f10404, /* EMC_AUTO_CAL_CONFIG */
+ 0x00000000, /* EMC_CTT */
+ 0x00000000, /* EMC_CTT_DURATION */
+ 0x000020ae, /* EMC_DYN_SELF_REF_CONTROL */
+ 0x00000008, /* MC_EMEM_ARB_CFG */
+ 0x80000060, /* MC_EMEM_ARB_OUTSTANDING_REQ */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_RCD */
+ 0x00000003, /* MC_EMEM_ARB_TIMING_RP */
+ 0x0000000d, /* MC_EMEM_ARB_TIMING_RC */
+ 0x00000008, /* MC_EMEM_ARB_TIMING_RAS */
+ 0x00000007, /* MC_EMEM_ARB_TIMING_FAW */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_RRD */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_RAP2PRE */
+ 0x00000009, /* MC_EMEM_ARB_TIMING_WAP2PRE */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_R2R */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_W2W */
+ 0x00000003, /* MC_EMEM_ARB_TIMING_R2W */
+ 0x00000006, /* MC_EMEM_ARB_TIMING_W2R */
+ 0x06030202, /* MC_EMEM_ARB_DA_TURNS */
+ 0x0010090d, /* MC_EMEM_ARB_DA_COVERS */
+ 0x7028180e, /* MC_EMEM_ARB_MISC0 */
+ 0x001f0000, /* MC_EMEM_ARB_RING1_THROTTLE */
+ },
+ 0x00000040, /* EMC_ZCAL_WAIT_CNT after clock change */
+ 0x00000010, /* EMC_AUTO_CAL_INTERVAL */
+ 0x00000000, /* EMC_CFG.PERIODIC_QRST */
+ 0x00001941, /* Mode Register 0 */
+ 0x00100002, /* Mode Register 1 */
+ 0x00200008, /* Mode Register 2 */
+ },
+};
+
+static const struct tegra_emc_table cardhu_emc_tables_h5tc2g_a2[] = {
+ {
+ 0x32, /* Rev 3.2 */
+ 25500, /* SDRAM frequency */
+ {
+ 0x00000001, /* EMC_RC */
+ 0x00000003, /* EMC_RFC */
+ 0x00000000, /* EMC_RAS */
+ 0x00000000, /* EMC_RP */
+ 0x00000002, /* EMC_R2W */
+ 0x0000000a, /* EMC_W2R */
+ 0x00000003, /* EMC_R2P */
+ 0x0000000b, /* EMC_W2P */
+ 0x00000000, /* EMC_RD_RCD */
+ 0x00000000, /* EMC_WR_RCD */
+ 0x00000003, /* EMC_RRD */
+ 0x00000001, /* EMC_REXT */
+ 0x00000000, /* EMC_WEXT */
+ 0x00000005, /* EMC_WDV */
+ 0x00000005, /* EMC_QUSE */
+ 0x00000004, /* EMC_QRST */
+ 0x00000007, /* EMC_QSAFE */
+ 0x0000000c, /* EMC_RDV */
+ 0x000000bd, /* EMC_REFRESH */
+ 0x00000000, /* EMC_BURST_REFRESH_NUM */
+ 0x0000002f, /* EMC_PRE_REFRESH_REQ_CNT */
+ 0x00000002, /* EMC_PDEX2WR */
+ 0x00000002, /* EMC_PDEX2RD */
+ 0x00000001, /* EMC_PCHG2PDEN */
+ 0x00000000, /* EMC_ACT2PDEN */
+ 0x00000007, /* EMC_AR2PDEN */
+ 0x0000000f, /* EMC_RW2PDEN */
+ 0x00000005, /* EMC_TXSR */
+ 0x00000005, /* EMC_TXSRDLL */
+ 0x00000004, /* EMC_TCKE */
+ 0x00000001, /* EMC_TFAW */
+ 0x00000000, /* EMC_TRPAB */
+ 0x00000004, /* EMC_TCLKSTABLE */
+ 0x00000005, /* EMC_TCLKSTOP */
+ 0x000000c3, /* EMC_TREFBW */
+ 0x00000000, /* EMC_QUSE_EXTRA */
+ 0x00000004, /* EMC_FBIO_CFG6 */
+ 0x00000000, /* EMC_ODT_WRITE */
+ 0x00000000, /* EMC_ODT_READ */
+ 0x00006288, /* EMC_FBIO_CFG5 */
+ 0x007800a4, /* EMC_CFG_DIG_DLL */
+ 0x00008000, /* EMC_CFG_DIG_DLL_PERIOD */
+ 0x00080000, /* EMC_DLL_XFORM_DQS0 */
+ 0x00080000, /* EMC_DLL_XFORM_DQS1 */
+ 0x00080000, /* EMC_DLL_XFORM_DQS2 */
+ 0x00080000, /* EMC_DLL_XFORM_DQS3 */
+ 0x00080000, /* EMC_DLL_XFORM_DQS4 */
+ 0x00080000, /* EMC_DLL_XFORM_DQS5 */
+ 0x00080000, /* EMC_DLL_XFORM_DQS6 */
+ 0x00080000, /* EMC_DLL_XFORM_DQS7 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE0 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE1 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE2 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE3 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE4 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE5 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE6 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE7 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS0 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS1 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS2 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS3 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS4 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS5 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS6 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS7 */
+ 0x00080000, /* EMC_DLL_XFORM_DQ0 */
+ 0x00080000, /* EMC_DLL_XFORM_DQ1 */
+ 0x00080000, /* EMC_DLL_XFORM_DQ2 */
+ 0x00080000, /* EMC_DLL_XFORM_DQ3 */
+ 0x000002a0, /* EMC_XM2CMDPADCTRL */
+ 0x0800211c, /* EMC_XM2DQSPADCTRL2 */
+ 0x00000000, /* EMC_XM2DQPADCTRL2 */
+ 0x77ffc084, /* EMC_XM2CLKPADCTRL */
+ 0x01f1f108, /* EMC_XM2COMPPADCTRL */
+ 0x05057404, /* EMC_XM2VTTGENPADCTRL */
+ 0x54000007, /* EMC_XM2VTTGENPADCTRL2 */
+ 0x08000168, /* EMC_XM2QUSEPADCTRL */
+ 0x08000000, /* EMC_XM2DQSPADCTRL3 */
+ 0x00000802, /* EMC_CTT_TERM_CTRL */
+ 0x00000000, /* EMC_ZCAL_INTERVAL */
+ 0x00000040, /* EMC_ZCAL_WAIT_CNT */
+ 0x000c000c, /* EMC_MRS_WAIT_CNT */
+ 0xa0f10000, /* EMC_AUTO_CAL_CONFIG */
+ 0x00000000, /* EMC_CTT */
+ 0x00000000, /* EMC_CTT_DURATION */
+ 0x80000280, /* EMC_DYN_SELF_REF_CONTROL */
+ 0x00020001, /* MC_EMEM_ARB_CFG */
+ 0xc0000008, /* MC_EMEM_ARB_OUTSTANDING_REQ */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_RCD */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_RP */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_RC */
+ 0x00000000, /* MC_EMEM_ARB_TIMING_RAS */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_FAW */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_RRD */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_RAP2PRE */
+ 0x00000008, /* MC_EMEM_ARB_TIMING_WAP2PRE */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_R2R */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_W2W */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_R2W */
+ 0x00000006, /* MC_EMEM_ARB_TIMING_W2R */
+ 0x06020102, /* MC_EMEM_ARB_DA_TURNS */
+ 0x000a0402, /* MC_EMEM_ARB_DA_COVERS */
+ 0x74430303, /* MC_EMEM_ARB_MISC0 */
+ 0x001f0000, /* MC_EMEM_ARB_RING1_THROTTLE */
+ 0xd8000000, /* EMC_FBIO_SPARE */
+ 0xff00ff00, /* EMC_CFG_RSV */
+ },
+ 0x00000040, /* EMC_ZCAL_WAIT_CNT after clock change */
+ 0x001fffff, /* EMC_AUTO_CAL_INTERVAL */
+ 0x00000000, /* EMC_CFG.PERIODIC_QRST */
+ 0x80001221, /* Mode Register 0 */
+ 0x80100003, /* Mode Register 1 */
+ 0x80200008, /* Mode Register 2 */
+ 0x00000001, /* EMC_CFG.DYN_SELF_REF */
+ },
+ {
+ 0x32, /* Rev 3.2 */
+ 51000, /* SDRAM frequency */
+ {
+ 0x00000002, /* EMC_RC */
+ 0x00000008, /* EMC_RFC */
+ 0x00000001, /* EMC_RAS */
+ 0x00000000, /* EMC_RP */
+ 0x00000002, /* EMC_R2W */
+ 0x0000000a, /* EMC_W2R */
+ 0x00000003, /* EMC_R2P */
+ 0x0000000b, /* EMC_W2P */
+ 0x00000000, /* EMC_RD_RCD */
+ 0x00000000, /* EMC_WR_RCD */
+ 0x00000003, /* EMC_RRD */
+ 0x00000001, /* EMC_REXT */
+ 0x00000000, /* EMC_WEXT */
+ 0x00000005, /* EMC_WDV */
+ 0x00000005, /* EMC_QUSE */
+ 0x00000004, /* EMC_QRST */
+ 0x00000007, /* EMC_QSAFE */
+ 0x0000000c, /* EMC_RDV */
+ 0x00000181, /* EMC_REFRESH */
+ 0x00000000, /* EMC_BURST_REFRESH_NUM */
+ 0x00000060, /* EMC_PRE_REFRESH_REQ_CNT */
+ 0x00000002, /* EMC_PDEX2WR */
+ 0x00000002, /* EMC_PDEX2RD */
+ 0x00000001, /* EMC_PCHG2PDEN */
+ 0x00000000, /* EMC_ACT2PDEN */
+ 0x00000007, /* EMC_AR2PDEN */
+ 0x0000000f, /* EMC_RW2PDEN */
+ 0x00000009, /* EMC_TXSR */
+ 0x00000009, /* EMC_TXSRDLL */
+ 0x00000004, /* EMC_TCKE */
+ 0x00000002, /* EMC_TFAW */
+ 0x00000000, /* EMC_TRPAB */
+ 0x00000004, /* EMC_TCLKSTABLE */
+ 0x00000005, /* EMC_TCLKSTOP */
+ 0x0000018e, /* EMC_TREFBW */
+ 0x00000000, /* EMC_QUSE_EXTRA */
+ 0x00000004, /* EMC_FBIO_CFG6 */
+ 0x00000000, /* EMC_ODT_WRITE */
+ 0x00000000, /* EMC_ODT_READ */
+ 0x00006288, /* EMC_FBIO_CFG5 */
+ 0x007800a4, /* EMC_CFG_DIG_DLL */
+ 0x00008000, /* EMC_CFG_DIG_DLL_PERIOD */
+ 0x00080000, /* EMC_DLL_XFORM_DQS0 */
+ 0x00080000, /* EMC_DLL_XFORM_DQS1 */
+ 0x00080000, /* EMC_DLL_XFORM_DQS2 */
+ 0x00080000, /* EMC_DLL_XFORM_DQS3 */
+ 0x00080000, /* EMC_DLL_XFORM_DQS4 */
+ 0x00080000, /* EMC_DLL_XFORM_DQS5 */
+ 0x00080000, /* EMC_DLL_XFORM_DQS6 */
+ 0x00080000, /* EMC_DLL_XFORM_DQS7 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE0 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE1 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE2 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE3 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE4 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE5 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE6 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE7 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS0 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS1 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS2 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS3 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS4 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS5 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS6 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS7 */
+ 0x00080000, /* EMC_DLL_XFORM_DQ0 */
+ 0x00080000, /* EMC_DLL_XFORM_DQ1 */
+ 0x00080000, /* EMC_DLL_XFORM_DQ2 */
+ 0x00080000, /* EMC_DLL_XFORM_DQ3 */
+ 0x000002a0, /* EMC_XM2CMDPADCTRL */
+ 0x0800211c, /* EMC_XM2DQSPADCTRL2 */
+ 0x00000000, /* EMC_XM2DQPADCTRL2 */
+ 0x77ffc084, /* EMC_XM2CLKPADCTRL */
+ 0x01f1f108, /* EMC_XM2COMPPADCTRL */
+ 0x05057404, /* EMC_XM2VTTGENPADCTRL */
+ 0x54000007, /* EMC_XM2VTTGENPADCTRL2 */
+ 0x08000168, /* EMC_XM2QUSEPADCTRL */
+ 0x08000000, /* EMC_XM2DQSPADCTRL3 */
+ 0x00000802, /* EMC_CTT_TERM_CTRL */
+ 0x00000000, /* EMC_ZCAL_INTERVAL */
+ 0x00000040, /* EMC_ZCAL_WAIT_CNT */
+ 0x000c000c, /* EMC_MRS_WAIT_CNT */
+ 0xa0f10000, /* EMC_AUTO_CAL_CONFIG */
+ 0x00000000, /* EMC_CTT */
+ 0x00000000, /* EMC_CTT_DURATION */
+ 0x8000040b, /* EMC_DYN_SELF_REF_CONTROL */
+ 0x00000001, /* MC_EMEM_ARB_CFG */
+ 0xc000000a, /* MC_EMEM_ARB_OUTSTANDING_REQ */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_RCD */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_RP */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_RC */
+ 0x00000000, /* MC_EMEM_ARB_TIMING_RAS */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_FAW */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_RRD */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_RAP2PRE */
+ 0x00000008, /* MC_EMEM_ARB_TIMING_WAP2PRE */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_R2R */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_W2W */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_R2W */
+ 0x00000006, /* MC_EMEM_ARB_TIMING_W2R */
+ 0x06020102, /* MC_EMEM_ARB_DA_TURNS */
+ 0x000a0402, /* MC_EMEM_ARB_DA_COVERS */
+ 0x73430303, /* MC_EMEM_ARB_MISC0 */
+ 0x001f0000, /* MC_EMEM_ARB_RING1_THROTTLE */
+ 0xd8000000, /* EMC_FBIO_SPARE */
+ 0xff00ff00, /* EMC_CFG_RSV */
+ },
+ 0x00000040, /* EMC_ZCAL_WAIT_CNT after clock change */
+ 0x001fffff, /* EMC_AUTO_CAL_INTERVAL */
+ 0x00000000, /* EMC_CFG.PERIODIC_QRST */
+ 0x80001221, /* Mode Register 0 */
+ 0x80100003, /* Mode Register 1 */
+ 0x80200008, /* Mode Register 2 */
+ 0x00000001, /* EMC_CFG.DYN_SELF_REF */
+ },
+ {
+ 0x32, /* Rev 3.2 */
+ 102000, /* SDRAM frequency */
+ {
+ 0x00000004, /* EMC_RC */
+ 0x00000010, /* EMC_RFC */
+ 0x00000003, /* EMC_RAS */
+ 0x00000001, /* EMC_RP */
+ 0x00000002, /* EMC_R2W */
+ 0x0000000a, /* EMC_W2R */
+ 0x00000003, /* EMC_R2P */
+ 0x0000000b, /* EMC_W2P */
+ 0x00000001, /* EMC_RD_RCD */
+ 0x00000001, /* EMC_WR_RCD */
+ 0x00000003, /* EMC_RRD */
+ 0x00000001, /* EMC_REXT */
+ 0x00000000, /* EMC_WEXT */
+ 0x00000005, /* EMC_WDV */
+ 0x00000005, /* EMC_QUSE */
+ 0x00000004, /* EMC_QRST */
+ 0x00000007, /* EMC_QSAFE */
+ 0x0000000c, /* EMC_RDV */
+ 0x00000303, /* EMC_REFRESH */
+ 0x00000000, /* EMC_BURST_REFRESH_NUM */
+ 0x000000c0, /* EMC_PRE_REFRESH_REQ_CNT */
+ 0x00000002, /* EMC_PDEX2WR */
+ 0x00000002, /* EMC_PDEX2RD */
+ 0x00000001, /* EMC_PCHG2PDEN */
+ 0x00000000, /* EMC_ACT2PDEN */
+ 0x00000007, /* EMC_AR2PDEN */
+ 0x0000000f, /* EMC_RW2PDEN */
+ 0x00000012, /* EMC_TXSR */
+ 0x00000012, /* EMC_TXSRDLL */
+ 0x00000004, /* EMC_TCKE */
+ 0x00000004, /* EMC_TFAW */
+ 0x00000000, /* EMC_TRPAB */
+ 0x00000004, /* EMC_TCLKSTABLE */
+ 0x00000005, /* EMC_TCLKSTOP */
+ 0x0000031c, /* EMC_TREFBW */
+ 0x00000000, /* EMC_QUSE_EXTRA */
+ 0x00000004, /* EMC_FBIO_CFG6 */
+ 0x00000000, /* EMC_ODT_WRITE */
+ 0x00000000, /* EMC_ODT_READ */
+ 0x00006288, /* EMC_FBIO_CFG5 */
+ 0x007800a4, /* EMC_CFG_DIG_DLL */
+ 0x00008000, /* EMC_CFG_DIG_DLL_PERIOD */
+ 0x00080000, /* EMC_DLL_XFORM_DQS0 */
+ 0x00080000, /* EMC_DLL_XFORM_DQS1 */
+ 0x00080000, /* EMC_DLL_XFORM_DQS2 */
+ 0x00080000, /* EMC_DLL_XFORM_DQS3 */
+ 0x00080000, /* EMC_DLL_XFORM_DQS4 */
+ 0x00080000, /* EMC_DLL_XFORM_DQS5 */
+ 0x00080000, /* EMC_DLL_XFORM_DQS6 */
+ 0x00080000, /* EMC_DLL_XFORM_DQS7 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE0 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE1 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE2 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE3 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE4 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE5 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE6 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE7 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS0 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS1 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS2 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS3 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS4 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS5 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS6 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS7 */
+ 0x00080000, /* EMC_DLL_XFORM_DQ0 */
+ 0x00080000, /* EMC_DLL_XFORM_DQ1 */
+ 0x00080000, /* EMC_DLL_XFORM_DQ2 */
+ 0x00080000, /* EMC_DLL_XFORM_DQ3 */
+ 0x000002a0, /* EMC_XM2CMDPADCTRL */
+ 0x0800211c, /* EMC_XM2DQSPADCTRL2 */
+ 0x00000000, /* EMC_XM2DQPADCTRL2 */
+ 0x77ffc084, /* EMC_XM2CLKPADCTRL */
+ 0x01f1f108, /* EMC_XM2COMPPADCTRL */
+ 0x05057404, /* EMC_XM2VTTGENPADCTRL */
+ 0x54000007, /* EMC_XM2VTTGENPADCTRL2 */
+ 0x08000168, /* EMC_XM2QUSEPADCTRL */
+ 0x08000000, /* EMC_XM2DQSPADCTRL3 */
+ 0x00000802, /* EMC_CTT_TERM_CTRL */
+ 0x00000000, /* EMC_ZCAL_INTERVAL */
+ 0x00000040, /* EMC_ZCAL_WAIT_CNT */
+ 0x000c000c, /* EMC_MRS_WAIT_CNT */
+ 0xa0f10000, /* EMC_AUTO_CAL_CONFIG */
+ 0x00000000, /* EMC_CTT */
+ 0x00000000, /* EMC_CTT_DURATION */
+ 0x80000713, /* EMC_DYN_SELF_REF_CONTROL */
+ 0x00000001, /* MC_EMEM_ARB_CFG */
+ 0xc0000013, /* MC_EMEM_ARB_OUTSTANDING_REQ */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_RCD */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_RP */
+ 0x00000003, /* MC_EMEM_ARB_TIMING_RC */
+ 0x00000000, /* MC_EMEM_ARB_TIMING_RAS */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_FAW */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_RRD */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_RAP2PRE */
+ 0x00000008, /* MC_EMEM_ARB_TIMING_WAP2PRE */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_R2R */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_W2W */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_R2W */
+ 0x00000006, /* MC_EMEM_ARB_TIMING_W2R */
+ 0x06020102, /* MC_EMEM_ARB_DA_TURNS */
+ 0x000a0403, /* MC_EMEM_ARB_DA_COVERS */
+ 0x72830504, /* MC_EMEM_ARB_MISC0 */
+ 0x001f0000, /* MC_EMEM_ARB_RING1_THROTTLE */
+ 0xd8000000, /* EMC_FBIO_SPARE */
+ 0xff00ff00, /* EMC_CFG_RSV */
+ },
+ 0x00000040, /* EMC_ZCAL_WAIT_CNT after clock change */
+ 0x001fffff, /* EMC_AUTO_CAL_INTERVAL */
+ 0x00000000, /* EMC_CFG.PERIODIC_QRST */
+ 0x80001221, /* Mode Register 0 */
+ 0x80100003, /* Mode Register 1 */
+ 0x80200008, /* Mode Register 2 */
+ 0x00000001, /* EMC_CFG.DYN_SELF_REF */
+ },
+ {
+ 0x32, /* Rev 3.2 */
+ 375000, /* SDRAM frequency */
+ {
+ 0x00000011, /* EMC_RC */
+ 0x0000003a, /* EMC_RFC */
+ 0x0000000c, /* EMC_RAS */
+ 0x00000004, /* EMC_RP */
+ 0x00000003, /* EMC_R2W */
+ 0x00000008, /* EMC_W2R */
+ 0x00000002, /* EMC_R2P */
+ 0x0000000a, /* EMC_W2P */
+ 0x00000004, /* EMC_RD_RCD */
+ 0x00000004, /* EMC_WR_RCD */
+ 0x00000002, /* EMC_RRD */
+ 0x00000001, /* EMC_REXT */
+ 0x00000000, /* EMC_WEXT */
+ 0x00000004, /* EMC_WDV */
+ 0x00000006, /* EMC_QUSE */
+ 0x00000004, /* EMC_QRST */
+ 0x00000008, /* EMC_QSAFE */
+ 0x0000000d, /* EMC_RDV */
+ 0x00000b2d, /* EMC_REFRESH */
+ 0x00000000, /* EMC_BURST_REFRESH_NUM */
+ 0x000002cb, /* EMC_PRE_REFRESH_REQ_CNT */
+ 0x00000008, /* EMC_PDEX2WR */
+ 0x00000008, /* EMC_PDEX2RD */
+ 0x00000001, /* EMC_PCHG2PDEN */
+ 0x00000000, /* EMC_ACT2PDEN */
+ 0x00000007, /* EMC_AR2PDEN */
+ 0x0000000f, /* EMC_RW2PDEN */
+ 0x00000040, /* EMC_TXSR */
+ 0x00000200, /* EMC_TXSRDLL */
+ 0x00000009, /* EMC_TCKE */
+ 0x0000000c, /* EMC_TFAW */
+ 0x00000000, /* EMC_TRPAB */
+ 0x00000004, /* EMC_TCLKSTABLE */
+ 0x00000005, /* EMC_TCLKSTOP */
+ 0x00000b6d, /* EMC_TREFBW */
+ 0x00000000, /* EMC_QUSE_EXTRA */
+ 0x00000006, /* EMC_FBIO_CFG6 */
+ 0x00000000, /* EMC_ODT_WRITE */
+ 0x00000000, /* EMC_ODT_READ */
+ 0x00007088, /* EMC_FBIO_CFG5 */
+ 0x00200084, /* EMC_CFG_DIG_DLL */
+ 0x00008000, /* EMC_CFG_DIG_DLL_PERIOD */
+ 0x0003c000, /* EMC_DLL_XFORM_DQS0 */
+ 0x0003c000, /* EMC_DLL_XFORM_DQS1 */
+ 0x0003c000, /* EMC_DLL_XFORM_DQS2 */
+ 0x0003c000, /* EMC_DLL_XFORM_DQS3 */
+ 0x0003c000, /* EMC_DLL_XFORM_DQS4 */
+ 0x0003c000, /* EMC_DLL_XFORM_DQS5 */
+ 0x0003c000, /* EMC_DLL_XFORM_DQS6 */
+ 0x0003c000, /* EMC_DLL_XFORM_DQS7 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE0 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE1 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE2 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE3 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE4 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE5 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE6 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE7 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS0 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS1 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS2 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS3 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS4 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS5 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS6 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS7 */
+ 0x00040000, /* EMC_DLL_XFORM_DQ0 */
+ 0x00040000, /* EMC_DLL_XFORM_DQ1 */
+ 0x00040000, /* EMC_DLL_XFORM_DQ2 */
+ 0x00040000, /* EMC_DLL_XFORM_DQ3 */
+ 0x000002a0, /* EMC_XM2CMDPADCTRL */
+ 0x0800013d, /* EMC_XM2DQSPADCTRL2 */
+ 0x00000000, /* EMC_XM2DQPADCTRL2 */
+ 0x77fff884, /* EMC_XM2CLKPADCTRL */
+ 0x01f1f508, /* EMC_XM2COMPPADCTRL */
+ 0x05057404, /* EMC_XM2VTTGENPADCTRL */
+ 0x54000007, /* EMC_XM2VTTGENPADCTRL2 */
+ 0x080001e8, /* EMC_XM2QUSEPADCTRL */
+ 0x08000021, /* EMC_XM2DQSPADCTRL3 */
+ 0x00000802, /* EMC_CTT_TERM_CTRL */
+ 0x00020000, /* EMC_ZCAL_INTERVAL */
+ 0x00000100, /* EMC_ZCAL_WAIT_CNT */
+ 0x0184000c, /* EMC_MRS_WAIT_CNT */
+ 0xa0f10000, /* EMC_AUTO_CAL_CONFIG */
+ 0x00000000, /* EMC_CTT */
+ 0x00000000, /* EMC_CTT_DURATION */
+ 0x8000174b, /* EMC_DYN_SELF_REF_CONTROL */
+ 0x00000005, /* MC_EMEM_ARB_CFG */
+ 0x80000044, /* MC_EMEM_ARB_OUTSTANDING_REQ */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_RCD */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_RP */
+ 0x00000009, /* MC_EMEM_ARB_TIMING_RC */
+ 0x00000005, /* MC_EMEM_ARB_TIMING_RAS */
+ 0x00000005, /* MC_EMEM_ARB_TIMING_FAW */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_RRD */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_RAP2PRE */
+ 0x00000008, /* MC_EMEM_ARB_TIMING_WAP2PRE */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_R2R */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_W2W */
+ 0x00000003, /* MC_EMEM_ARB_TIMING_R2W */
+ 0x00000006, /* MC_EMEM_ARB_TIMING_W2R */
+ 0x06030202, /* MC_EMEM_ARB_DA_TURNS */
+ 0x000d0709, /* MC_EMEM_ARB_DA_COVERS */
+ 0x75c6110a, /* MC_EMEM_ARB_MISC0 */
+ 0x001f0000, /* MC_EMEM_ARB_RING1_THROTTLE */
+ 0x58000000, /* EMC_FBIO_SPARE */
+ 0xff00ff88, /* EMC_CFG_RSV */
+ },
+ 0x00000040, /* EMC_ZCAL_WAIT_CNT after clock change */
+ 0x001fffff, /* EMC_AUTO_CAL_INTERVAL */
+ 0x00000000, /* EMC_CFG.PERIODIC_QRST */
+ 0x80000521, /* Mode Register 0 */
+ 0x80100002, /* Mode Register 1 */
+ 0x80200000, /* Mode Register 2 */
+ 0x00000000, /* EMC_CFG.DYN_SELF_REF */
+ },
+ {
+ 0x32, /* Rev 3.2 */
+ 400000, /* SDRAM frequency */
+ {
+ 0x00000012, /* EMC_RC */
+ 0x00000040, /* EMC_RFC */
+ 0x0000000d, /* EMC_RAS */
+ 0x00000004, /* EMC_RP */
+ 0x00000002, /* EMC_R2W */
+ 0x00000009, /* EMC_W2R */
+ 0x00000002, /* EMC_R2P */
+ 0x0000000c, /* EMC_W2P */
+ 0x00000004, /* EMC_RD_RCD */
+ 0x00000004, /* EMC_WR_RCD */
+ 0x00000002, /* EMC_RRD */
+ 0x00000001, /* EMC_REXT */
+ 0x00000000, /* EMC_WEXT */
+ 0x00000005, /* EMC_WDV */
+ 0x00000007, /* EMC_QUSE */
+ 0x00000005, /* EMC_QRST */
+ 0x00000008, /* EMC_QSAFE */
+ 0x0000000e, /* EMC_RDV */
+ 0x00000c2e, /* EMC_REFRESH */
+ 0x00000000, /* EMC_BURST_REFRESH_NUM */
+ 0x0000030b, /* EMC_PRE_REFRESH_REQ_CNT */
+ 0x00000008, /* EMC_PDEX2WR */
+ 0x00000008, /* EMC_PDEX2RD */
+ 0x00000001, /* EMC_PCHG2PDEN */
+ 0x00000000, /* EMC_ACT2PDEN */
+ 0x00000008, /* EMC_AR2PDEN */
+ 0x00000011, /* EMC_RW2PDEN */
+ 0x00000046, /* EMC_TXSR */
+ 0x00000200, /* EMC_TXSRDLL */
+ 0x0000000a, /* EMC_TCKE */
+ 0x0000000d, /* EMC_TFAW */
+ 0x00000000, /* EMC_TRPAB */
+ 0x00000004, /* EMC_TCLKSTABLE */
+ 0x00000005, /* EMC_TCLKSTOP */
+ 0x00000c6f, /* EMC_TREFBW */
+ 0x00000000, /* EMC_QUSE_EXTRA */
+ 0x00000006, /* EMC_FBIO_CFG6 */
+ 0x00000000, /* EMC_ODT_WRITE */
+ 0x00000000, /* EMC_ODT_READ */
+ 0x00007088, /* EMC_FBIO_CFG5 */
+ 0x001c0084, /* EMC_CFG_DIG_DLL */
+ 0x00008000, /* EMC_CFG_DIG_DLL_PERIOD */
+ 0x00034000, /* EMC_DLL_XFORM_DQS0 */
+ 0x00034000, /* EMC_DLL_XFORM_DQS1 */
+ 0x00034000, /* EMC_DLL_XFORM_DQS2 */
+ 0x00034000, /* EMC_DLL_XFORM_DQS3 */
+ 0x00034000, /* EMC_DLL_XFORM_DQS4 */
+ 0x00034000, /* EMC_DLL_XFORM_DQS5 */
+ 0x00034000, /* EMC_DLL_XFORM_DQS6 */
+ 0x00034000, /* EMC_DLL_XFORM_DQS7 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE0 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE1 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE2 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE3 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE4 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE5 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE6 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE7 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS0 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS1 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS2 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS3 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS4 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS5 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS6 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS7 */
+ 0x00040000, /* EMC_DLL_XFORM_DQ0 */
+ 0x00040000, /* EMC_DLL_XFORM_DQ1 */
+ 0x00040000, /* EMC_DLL_XFORM_DQ2 */
+ 0x00040000, /* EMC_DLL_XFORM_DQ3 */
+ 0x000002a0, /* EMC_XM2CMDPADCTRL */
+ 0x0800013d, /* EMC_XM2DQSPADCTRL2 */
+ 0x00000000, /* EMC_XM2DQPADCTRL2 */
+ 0x77fff884, /* EMC_XM2CLKPADCTRL */
+ 0x01f1f508, /* EMC_XM2COMPPADCTRL */
+ 0x05057404, /* EMC_XM2VTTGENPADCTRL */
+ 0x54000007, /* EMC_XM2VTTGENPADCTRL2 */
+ 0x080001e8, /* EMC_XM2QUSEPADCTRL */
+ 0x08000021, /* EMC_XM2DQSPADCTRL3 */
+ 0x00000802, /* EMC_CTT_TERM_CTRL */
+ 0x00020000, /* EMC_ZCAL_INTERVAL */
+ 0x00000100, /* EMC_ZCAL_WAIT_CNT */
+ 0x017f000c, /* EMC_MRS_WAIT_CNT */
+ 0xa0f10000, /* EMC_AUTO_CAL_CONFIG */
+ 0x00000000, /* EMC_CTT */
+ 0x00000000, /* EMC_CTT_DURATION */
+ 0x80001941, /* EMC_DYN_SELF_REF_CONTROL */
+ 0x00000006, /* MC_EMEM_ARB_CFG */
+ 0x8000004a, /* MC_EMEM_ARB_OUTSTANDING_REQ */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_RCD */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_RP */
+ 0x0000000a, /* MC_EMEM_ARB_TIMING_RC */
+ 0x00000006, /* MC_EMEM_ARB_TIMING_RAS */
+ 0x00000006, /* MC_EMEM_ARB_TIMING_FAW */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_RRD */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_RAP2PRE */
+ 0x00000009, /* MC_EMEM_ARB_TIMING_WAP2PRE */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_R2R */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_W2W */
+ 0x00000003, /* MC_EMEM_ARB_TIMING_R2W */
+ 0x00000006, /* MC_EMEM_ARB_TIMING_W2R */
+ 0x06030202, /* MC_EMEM_ARB_DA_TURNS */
+ 0x000e070a, /* MC_EMEM_ARB_DA_COVERS */
+ 0x7547130b, /* MC_EMEM_ARB_MISC0 */
+ 0x001f0000, /* MC_EMEM_ARB_RING1_THROTTLE */
+ 0x58000000, /* EMC_FBIO_SPARE */
+ 0xff00ff88, /* EMC_CFG_RSV */
+ },
+ 0x00000040, /* EMC_ZCAL_WAIT_CNT after clock change */
+ 0x001fffff, /* EMC_AUTO_CAL_INTERVAL */
+ 0x00000000, /* EMC_CFG.PERIODIC_QRST */
+ 0x80000731, /* Mode Register 0 */
+ 0x80100002, /* Mode Register 1 */
+ 0x80200008, /* Mode Register 2 */
+ 0x00000000, /* EMC_CFG.DYN_SELF_REF */
+ },
+ {
+ 0x32, /* Rev 3.2 */
+ 533000, /* SDRAM frequency */
+ {
+ 0x00000018, /* EMC_RC */
+ 0x00000054, /* EMC_RFC */
+ 0x00000011, /* EMC_RAS */
+ 0x00000006, /* EMC_RP */
+ 0x00000003, /* EMC_R2W */
+ 0x00000009, /* EMC_W2R */
+ 0x00000002, /* EMC_R2P */
+ 0x0000000d, /* EMC_W2P */
+ 0x00000006, /* EMC_RD_RCD */
+ 0x00000006, /* EMC_WR_RCD */
+ 0x00000002, /* EMC_RRD */
+ 0x00000001, /* EMC_REXT */
+ 0x00000000, /* EMC_WEXT */
+ 0x00000005, /* EMC_WDV */
+ 0x00000008, /* EMC_QUSE */
+ 0x00000006, /* EMC_QRST */
+ 0x00000008, /* EMC_QSAFE */
+ 0x00000010, /* EMC_RDV */
+ 0x00000ffd, /* EMC_REFRESH */
+ 0x00000000, /* EMC_BURST_REFRESH_NUM */
+ 0x000003ff, /* EMC_PRE_REFRESH_REQ_CNT */
+ 0x0000000b, /* EMC_PDEX2WR */
+ 0x0000000b, /* EMC_PDEX2RD */
+ 0x00000001, /* EMC_PCHG2PDEN */
+ 0x00000000, /* EMC_ACT2PDEN */
+ 0x0000000a, /* EMC_AR2PDEN */
+ 0x00000012, /* EMC_RW2PDEN */
+ 0x0000005b, /* EMC_TXSR */
+ 0x00000200, /* EMC_TXSRDLL */
+ 0x0000000d, /* EMC_TCKE */
+ 0x00000010, /* EMC_TFAW */
+ 0x00000000, /* EMC_TRPAB */
+ 0x00000005, /* EMC_TCLKSTABLE */
+ 0x00000006, /* EMC_TCLKSTOP */
+ 0x0000103e, /* EMC_TREFBW */
+ 0x00000000, /* EMC_QUSE_EXTRA */
+ 0x00000006, /* EMC_FBIO_CFG6 */
+ 0x00000000, /* EMC_ODT_WRITE */
+ 0x00000000, /* EMC_ODT_READ */
+ 0x00007088, /* EMC_FBIO_CFG5 */
+ 0x00120084, /* EMC_CFG_DIG_DLL */
+ 0x00008000, /* EMC_CFG_DIG_DLL_PERIOD */
+ 0x00010000, /* EMC_DLL_XFORM_DQS0 */
+ 0x00010000, /* EMC_DLL_XFORM_DQS1 */
+ 0x00010000, /* EMC_DLL_XFORM_DQS2 */
+ 0x00010000, /* EMC_DLL_XFORM_DQS3 */
+ 0x00010000, /* EMC_DLL_XFORM_DQS4 */
+ 0x00010000, /* EMC_DLL_XFORM_DQS5 */
+ 0x00010000, /* EMC_DLL_XFORM_DQS6 */
+ 0x00010000, /* EMC_DLL_XFORM_DQS7 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE0 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE1 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE2 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE3 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE4 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE5 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE6 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE7 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS0 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS1 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS2 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS3 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS4 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS5 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS6 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS7 */
+ 0x00020000, /* EMC_DLL_XFORM_DQ0 */
+ 0x00020000, /* EMC_DLL_XFORM_DQ1 */
+ 0x00020000, /* EMC_DLL_XFORM_DQ2 */
+ 0x00020000, /* EMC_DLL_XFORM_DQ3 */
+ 0x000006a0, /* EMC_XM2CMDPADCTRL */
+ 0x0800013d, /* EMC_XM2DQSPADCTRL2 */
+ 0x00000000, /* EMC_XM2DQPADCTRL2 */
+ 0x77ffc084, /* EMC_XM2CLKPADCTRL */
+ 0x01f1f508, /* EMC_XM2COMPPADCTRL */
+ 0x05057404, /* EMC_XM2VTTGENPADCTRL */
+ 0x54000007, /* EMC_XM2VTTGENPADCTRL2 */
+ 0x08000168, /* EMC_XM2QUSEPADCTRL */
+ 0x08000021, /* EMC_XM2DQSPADCTRL3 */
+ 0x00000802, /* EMC_CTT_TERM_CTRL */
+ 0x00000000, /* EMC_ZCAL_INTERVAL */
+ 0x00000040, /* EMC_ZCAL_WAIT_CNT */
+ 0x01ab000c, /* EMC_MRS_WAIT_CNT */
+ 0xa0f10404, /* EMC_AUTO_CAL_CONFIG */
+ 0x00000000, /* EMC_CTT */
+ 0x00000000, /* EMC_CTT_DURATION */
+ 0x800020ae, /* EMC_DYN_SELF_REF_CONTROL */
+ 0x00000008, /* MC_EMEM_ARB_CFG */
+ 0x80000060, /* MC_EMEM_ARB_OUTSTANDING_REQ */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_RCD */
+ 0x00000003, /* MC_EMEM_ARB_TIMING_RP */
+ 0x0000000d, /* MC_EMEM_ARB_TIMING_RC */
+ 0x00000008, /* MC_EMEM_ARB_TIMING_RAS */
+ 0x00000007, /* MC_EMEM_ARB_TIMING_FAW */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_RRD */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_RAP2PRE */
+ 0x00000009, /* MC_EMEM_ARB_TIMING_WAP2PRE */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_R2R */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_W2W */
+ 0x00000003, /* MC_EMEM_ARB_TIMING_R2W */
+ 0x00000006, /* MC_EMEM_ARB_TIMING_W2R */
+ 0x06030202, /* MC_EMEM_ARB_DA_TURNS */
+ 0x0010090d, /* MC_EMEM_ARB_DA_COVERS */
+ 0x7028180e, /* MC_EMEM_ARB_MISC0 */
+ 0x001f0000, /* MC_EMEM_ARB_RING1_THROTTLE */
+ 0x00000000, /* EMC_FBIO_SPARE */
+ 0xff00ff00, /* EMC_CFG_RSV */
+ },
+ 0x00000040, /* EMC_ZCAL_WAIT_CNT after clock change */
+ 0x001fffff, /* EMC_AUTO_CAL_INTERVAL */
+ 0x00000000, /* EMC_CFG.PERIODIC_QRST */
+ 0x80000941, /* Mode Register 0 */
+ 0x80100002, /* Mode Register 1 */
+ 0x80200008, /* Mode Register 2 */
+ 0x00000000, /* EMC_CFG.DYN_SELF_REF */
+ },
+ {
+ 0x32, /* Rev 3.2 */
+ 750000, /* SDRAM frequency */
+ {
+ 0x00000025, /* EMC_RC */
+ 0x0000007e, /* EMC_RFC */
+ 0x0000001a, /* EMC_RAS */
+ 0x00000009, /* EMC_RP */
+ 0x00000004, /* EMC_R2W */
+ 0x0000000d, /* EMC_W2R */
+ 0x00000004, /* EMC_R2P */
+ 0x00000013, /* EMC_W2P */
+ 0x00000009, /* EMC_RD_RCD */
+ 0x00000009, /* EMC_WR_RCD */
+ 0x00000003, /* EMC_RRD */
+ 0x00000001, /* EMC_REXT */
+ 0x00000000, /* EMC_WEXT */
+ 0x00000007, /* EMC_WDV */
+ 0x0000000b, /* EMC_QUSE */
+ 0x00000009, /* EMC_QRST */
+ 0x0000000c, /* EMC_QSAFE */
+ 0x00000011, /* EMC_RDV */
+ 0x0000169a, /* EMC_REFRESH */
+ 0x00000000, /* EMC_BURST_REFRESH_NUM */
+ 0x00000608, /* EMC_PRE_REFRESH_REQ_CNT */
+ 0x00000012, /* EMC_PDEX2WR */
+ 0x00000012, /* EMC_PDEX2RD */
+ 0x00000001, /* EMC_PCHG2PDEN */
+ 0x00000000, /* EMC_ACT2PDEN */
+ 0x0000000f, /* EMC_AR2PDEN */
+ 0x00000018, /* EMC_RW2PDEN */
+ 0x00000088, /* EMC_TXSR */
+ 0x00000200, /* EMC_TXSRDLL */
+ 0x00000014, /* EMC_TCKE */
+ 0x00000018, /* EMC_TFAW */
+ 0x00000000, /* EMC_TRPAB */
+ 0x00000007, /* EMC_TCLKSTABLE */
+ 0x00000008, /* EMC_TCLKSTOP */
+ 0x00001860, /* EMC_TREFBW */
+ 0x0000000c, /* EMC_QUSE_EXTRA */
+ 0x00000004, /* EMC_FBIO_CFG6 */
+ 0x00000000, /* EMC_ODT_WRITE */
+ 0x00000000, /* EMC_ODT_READ */
+ 0x00005088, /* EMC_FBIO_CFG5 */
+ 0xf0080191, /* EMC_CFG_DIG_DLL */
+ 0x00008000, /* EMC_CFG_DIG_DLL_PERIOD */
+ 0x00000008, /* EMC_DLL_XFORM_DQS0 */
+ 0x00000008, /* EMC_DLL_XFORM_DQS1 */
+ 0x00000008, /* EMC_DLL_XFORM_DQS2 */
+ 0x00000008, /* EMC_DLL_XFORM_DQS3 */
+ 0x00000008, /* EMC_DLL_XFORM_DQS4 */
+ 0x00000008, /* EMC_DLL_XFORM_DQS5 */
+ 0x00000008, /* EMC_DLL_XFORM_DQS6 */
+ 0x00000008, /* EMC_DLL_XFORM_DQS7 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE0 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE1 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE2 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE3 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE4 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE5 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE6 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE7 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS0 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS1 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS2 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS3 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS4 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS5 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS6 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS7 */
+ 0x0000000c, /* EMC_DLL_XFORM_DQ0 */
+ 0x0000000c, /* EMC_DLL_XFORM_DQ1 */
+ 0x0000000c, /* EMC_DLL_XFORM_DQ2 */
+ 0x0000000c, /* EMC_DLL_XFORM_DQ3 */
+ 0x000002a0, /* EMC_XM2CMDPADCTRL */
+ 0x0600013d, /* EMC_XM2DQSPADCTRL2 */
+ 0x22220000, /* EMC_XM2DQPADCTRL2 */
+ 0x77fff884, /* EMC_XM2CLKPADCTRL */
+ 0x01f1f501, /* EMC_XM2COMPPADCTRL */
+ 0x07077404, /* EMC_XM2VTTGENPADCTRL */
+ 0x54000000, /* EMC_XM2VTTGENPADCTRL2 */
+ 0x080001e8, /* EMC_XM2QUSEPADCTRL */
+ 0x07000021, /* EMC_XM2DQSPADCTRL3 */
+ 0x00000802, /* EMC_CTT_TERM_CTRL */
+ 0x00020000, /* EMC_ZCAL_INTERVAL */
+ 0x00000100, /* EMC_ZCAL_WAIT_CNT */
+ 0x0180000c, /* EMC_MRS_WAIT_CNT */
+ 0xa0f10000, /* EMC_AUTO_CAL_CONFIG */
+ 0x00000000, /* EMC_CTT */
+ 0x00000000, /* EMC_CTT_DURATION */
+ 0x8000308c, /* EMC_DYN_SELF_REF_CONTROL */
+ 0x0000000c, /* MC_EMEM_ARB_CFG */
+ 0x80000090, /* MC_EMEM_ARB_OUTSTANDING_REQ */
+ 0x00000004, /* MC_EMEM_ARB_TIMING_RCD */
+ 0x00000005, /* MC_EMEM_ARB_TIMING_RP */
+ 0x00000013, /* MC_EMEM_ARB_TIMING_RC */
+ 0x0000000c, /* MC_EMEM_ARB_TIMING_RAS */
+ 0x0000000b, /* MC_EMEM_ARB_TIMING_FAW */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_RRD */
+ 0x00000003, /* MC_EMEM_ARB_TIMING_RAP2PRE */
+ 0x0000000c, /* MC_EMEM_ARB_TIMING_WAP2PRE */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_R2R */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_W2W */
+ 0x00000004, /* MC_EMEM_ARB_TIMING_R2W */
+ 0x00000008, /* MC_EMEM_ARB_TIMING_W2R */
+ 0x08040202, /* MC_EMEM_ARB_DA_TURNS */
+ 0x00160d13, /* MC_EMEM_ARB_DA_COVERS */
+ 0x72ac2414, /* MC_EMEM_ARB_MISC0 */
+ 0x001f0000, /* MC_EMEM_ARB_RING1_THROTTLE */
+ 0xf8000000, /* EMC_FBIO_SPARE */
+ 0xff00ff49, /* EMC_CFG_RSV */
+ },
+ 0x00000040, /* EMC_ZCAL_WAIT_CNT after clock change */
+ 0x001fffff, /* EMC_AUTO_CAL_INTERVAL */
+ 0x00000001, /* EMC_CFG.PERIODIC_QRST */
+ 0x80000d71, /* Mode Register 0 */
+ 0x80100002, /* Mode Register 1 */
+ 0x80200018, /* Mode Register 2 */
+ 0x00000000, /* EMC_CFG.DYN_SELF_REF */
+ },
+ {
+ 0x32, /* Rev 3.2 */
+ 800000, /* SDRAM frequency */
+ {
+ 0x00000025, /* EMC_RC */
+ 0x0000007e, /* EMC_RFC */
+ 0x0000001a, /* EMC_RAS */
+ 0x00000009, /* EMC_RP */
+ 0x00000004, /* EMC_R2W */
+ 0x0000000d, /* EMC_W2R */
+ 0x00000004, /* EMC_R2P */
+ 0x00000013, /* EMC_W2P */
+ 0x00000009, /* EMC_RD_RCD */
+ 0x00000009, /* EMC_WR_RCD */
+ 0x00000003, /* EMC_RRD */
+ 0x00000001, /* EMC_REXT */
+ 0x00000000, /* EMC_WEXT */
+ 0x00000007, /* EMC_WDV */
+ 0x0000000b, /* EMC_QUSE */
+ 0x00000009, /* EMC_QRST */
+ 0x0000000c, /* EMC_QSAFE */
+ 0x00000011, /* EMC_RDV */
+ 0x00001820, /* EMC_REFRESH */
+ 0x00000000, /* EMC_BURST_REFRESH_NUM */
+ 0x00000608, /* EMC_PRE_REFRESH_REQ_CNT */
+ 0x00000012, /* EMC_PDEX2WR */
+ 0x00000012, /* EMC_PDEX2RD */
+ 0x00000001, /* EMC_PCHG2PDEN */
+ 0x00000000, /* EMC_ACT2PDEN */
+ 0x0000000f, /* EMC_AR2PDEN */
+ 0x00000018, /* EMC_RW2PDEN */
+ 0x00000088, /* EMC_TXSR */
+ 0x00000200, /* EMC_TXSRDLL */
+ 0x00000014, /* EMC_TCKE */
+ 0x00000018, /* EMC_TFAW */
+ 0x00000000, /* EMC_TRPAB */
+ 0x00000007, /* EMC_TCLKSTABLE */
+ 0x00000008, /* EMC_TCLKSTOP */
+ 0x00001860, /* EMC_TREFBW */
+ 0x0000000c, /* EMC_QUSE_EXTRA */
+ 0x00000004, /* EMC_FBIO_CFG6 */
+ 0x00000000, /* EMC_ODT_WRITE */
+ 0x00000000, /* EMC_ODT_READ */
+ 0x00005088, /* EMC_FBIO_CFG5 */
+ 0xf0070191, /* EMC_CFG_DIG_DLL */
+ 0x00008000, /* EMC_CFG_DIG_DLL_PERIOD */
+ 0x0000000a, /* EMC_DLL_XFORM_DQS0 */
+ 0x0000000a, /* EMC_DLL_XFORM_DQS1 */
+ 0x0000000a, /* EMC_DLL_XFORM_DQS2 */
+ 0x0000000a, /* EMC_DLL_XFORM_DQS3 */
+ 0x0000000a, /* EMC_DLL_XFORM_DQS4 */
+ 0x0000000a, /* EMC_DLL_XFORM_DQS5 */
+ 0x0000000a, /* EMC_DLL_XFORM_DQS6 */
+ 0x0000000a, /* EMC_DLL_XFORM_DQS7 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE0 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE1 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE2 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE3 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE4 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE5 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE6 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE7 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS0 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS1 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS2 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS3 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS4 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS5 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS6 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS7 */
+ 0x0000000a, /* EMC_DLL_XFORM_DQ0 */
+ 0x0000000a, /* EMC_DLL_XFORM_DQ1 */
+ 0x0000000a, /* EMC_DLL_XFORM_DQ2 */
+ 0x0000000a, /* EMC_DLL_XFORM_DQ3 */
+ 0x000002a0, /* EMC_XM2CMDPADCTRL */
+ 0x0600013d, /* EMC_XM2DQSPADCTRL2 */
+ 0x22220000, /* EMC_XM2DQPADCTRL2 */
+ 0x77fff884, /* EMC_XM2CLKPADCTRL */
+ 0x01f1f501, /* EMC_XM2COMPPADCTRL */
+ 0x07077404, /* EMC_XM2VTTGENPADCTRL */
+ 0x54000000, /* EMC_XM2VTTGENPADCTRL2 */
+ 0x080001e8, /* EMC_XM2QUSEPADCTRL */
+ 0x07000021, /* EMC_XM2DQSPADCTRL3 */
+ 0x00000802, /* EMC_CTT_TERM_CTRL */
+ 0x00020000, /* EMC_ZCAL_INTERVAL */
+ 0x00000100, /* EMC_ZCAL_WAIT_CNT */
+ 0x0180000c, /* EMC_MRS_WAIT_CNT */
+ 0xa0f10000, /* EMC_AUTO_CAL_CONFIG */
+ 0x00000000, /* EMC_CTT */
+ 0x00000000, /* EMC_CTT_DURATION */
+ 0x8000308c, /* EMC_DYN_SELF_REF_CONTROL */
+ 0x0000000c, /* MC_EMEM_ARB_CFG */
+ 0x80000090, /* MC_EMEM_ARB_OUTSTANDING_REQ */
+ 0x00000004, /* MC_EMEM_ARB_TIMING_RCD */
+ 0x00000005, /* MC_EMEM_ARB_TIMING_RP */
+ 0x00000013, /* MC_EMEM_ARB_TIMING_RC */
+ 0x0000000c, /* MC_EMEM_ARB_TIMING_RAS */
+ 0x0000000b, /* MC_EMEM_ARB_TIMING_FAW */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_RRD */
+ 0x00000003, /* MC_EMEM_ARB_TIMING_RAP2PRE */
+ 0x0000000c, /* MC_EMEM_ARB_TIMING_WAP2PRE */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_R2R */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_W2W */
+ 0x00000004, /* MC_EMEM_ARB_TIMING_R2W */
+ 0x00000008, /* MC_EMEM_ARB_TIMING_W2R */
+ 0x08040202, /* MC_EMEM_ARB_DA_TURNS */
+ 0x00160d13, /* MC_EMEM_ARB_DA_COVERS */
+ 0x72ac2414, /* MC_EMEM_ARB_MISC0 */
+ 0x001f0000, /* MC_EMEM_ARB_RING1_THROTTLE */
+ 0xf8000000, /* EMC_FBIO_SPARE */
+ 0xff00ff49, /* EMC_CFG_RSV */
+ },
+ 0x00000040, /* EMC_ZCAL_WAIT_CNT after clock change */
+ 0x001fffff, /* EMC_AUTO_CAL_INTERVAL */
+ 0x00000001, /* EMC_CFG.PERIODIC_QRST */
+ 0x80000d71, /* Mode Register 0 */
+ 0x80100002, /* Mode Register 1 */
+ 0x80200018, /* Mode Register 2 */
+ 0x00000000, /* EMC_CFG.DYN_SELF_REF */
+ },
+};
+
+static const struct tegra_emc_table cardhu_emc_tables_k4p8g304eb[] = {
+ {
+ 0x32, /* Rev 3.2 */
+ 25500, /* SDRAM frequency */
+ {
+ 0x00000001, /* EMC_RC */
+ 0x00000003, /* EMC_RFC */
+ 0x00000002, /* EMC_RAS */
+ 0x00000002, /* EMC_RP */
+ 0x00000004, /* EMC_R2W */
+ 0x00000004, /* EMC_W2R */
+ 0x00000001, /* EMC_R2P */
+ 0x00000005, /* EMC_W2P */
+ 0x00000002, /* EMC_RD_RCD */
+ 0x00000002, /* EMC_WR_RCD */
+ 0x00000001, /* EMC_RRD */
+ 0x00000001, /* EMC_REXT */
+ 0x00000000, /* EMC_WEXT */
+ 0x00000001, /* EMC_WDV */
+ 0x00000003, /* EMC_QUSE */
+ 0x00000001, /* EMC_QRST */
+ 0x00000009, /* EMC_QSAFE */
+ 0x0000000a, /* EMC_RDV */
+ 0x0000005e, /* EMC_REFRESH */
+ 0x00000000, /* EMC_BURST_REFRESH_NUM */
+ 0x00000017, /* EMC_PRE_REFRESH_REQ_CNT */
+ 0x00000001, /* EMC_PDEX2WR */
+ 0x00000001, /* EMC_PDEX2RD */
+ 0x00000002, /* EMC_PCHG2PDEN */
+ 0x00000000, /* EMC_ACT2PDEN */
+ 0x00000001, /* EMC_AR2PDEN */
+ 0x00000007, /* EMC_RW2PDEN */
+ 0x00000004, /* EMC_TXSR */
+ 0x00000004, /* EMC_TXSRDLL */
+ 0x00000003, /* EMC_TCKE */
+ 0x00000008, /* EMC_TFAW */
+ 0x00000004, /* EMC_TRPAB */
+ 0x00000004, /* EMC_TCLKSTABLE */
+ 0x00000002, /* EMC_TCLKSTOP */
+ 0x00000068, /* EMC_TREFBW */
+ 0x00000004, /* EMC_QUSE_EXTRA */
+ 0x00000004, /* EMC_FBIO_CFG6 */
+ 0x00000000, /* EMC_ODT_WRITE */
+ 0x00000000, /* EMC_ODT_READ */
+ 0x00004282, /* EMC_FBIO_CFG5 */
+ 0x007800a4, /* EMC_CFG_DIG_DLL */
+ 0x00008000, /* EMC_CFG_DIG_DLL_PERIOD */
+ 0x00098000, /* EMC_DLL_XFORM_DQS0 */
+ 0x00098000, /* EMC_DLL_XFORM_DQS1 */
+ 0x00098000, /* EMC_DLL_XFORM_DQS2 */
+ 0x00098000, /* EMC_DLL_XFORM_DQS3 */
+ 0x00000010, /* EMC_DLL_XFORM_DQS4 */
+ 0x00000010, /* EMC_DLL_XFORM_DQS5 */
+ 0x00000010, /* EMC_DLL_XFORM_DQS6 */
+ 0x00000010, /* EMC_DLL_XFORM_DQS7 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE0 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE1 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE2 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE3 */
+ 0x00000008, /* EMC_DLL_XFORM_QUSE4 */
+ 0x00000008, /* EMC_DLL_XFORM_QUSE5 */
+ 0x00000008, /* EMC_DLL_XFORM_QUSE6 */
+ 0x00000008, /* EMC_DLL_XFORM_QUSE7 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS0 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS1 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS2 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS3 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS4 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS5 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS6 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS7 */
+ 0x00080000, /* EMC_DLL_XFORM_DQ0 */
+ 0x00080000, /* EMC_DLL_XFORM_DQ1 */
+ 0x00080000, /* EMC_DLL_XFORM_DQ2 */
+ 0x00080000, /* EMC_DLL_XFORM_DQ3 */
+ 0x00100220, /* EMC_XM2CMDPADCTRL */
+ 0x0800201c, /* EMC_XM2DQSPADCTRL2 */
+ 0x00000000, /* EMC_XM2DQPADCTRL2 */
+ 0x77ffc004, /* EMC_XM2CLKPADCTRL */
+ 0x01f1f008, /* EMC_XM2COMPPADCTRL */
+ 0x00000000, /* EMC_XM2VTTGENPADCTRL */
+ 0x00000007, /* EMC_XM2VTTGENPADCTRL2 */
+ 0x08000068, /* EMC_XM2QUSEPADCTRL */
+ 0x08000000, /* EMC_XM2DQSPADCTRL3 */
+ 0x00000802, /* EMC_CTT_TERM_CTRL */
+ 0x00064000, /* EMC_ZCAL_INTERVAL */
+ 0x0000000a, /* EMC_ZCAL_WAIT_CNT */
+ 0x00090009, /* EMC_MRS_WAIT_CNT */
+ 0xa0f10000, /* EMC_AUTO_CAL_CONFIG */
+ 0x00000000, /* EMC_CTT */
+ 0x00000000, /* EMC_CTT_DURATION */
+ 0x800001c2, /* EMC_DYN_SELF_REF_CONTROL */
+ 0x00020001, /* MC_EMEM_ARB_CFG */
+ 0xc0000008, /* MC_EMEM_ARB_OUTSTANDING_REQ */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_RCD */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_RP */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_RC */
+ 0x00000000, /* MC_EMEM_ARB_TIMING_RAS */
+ 0x00000003, /* MC_EMEM_ARB_TIMING_FAW */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_RRD */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_RAP2PRE */
+ 0x00000004, /* MC_EMEM_ARB_TIMING_WAP2PRE */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_R2R */
+ 0x00000000, /* MC_EMEM_ARB_TIMING_W2W */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_R2W */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_W2R */
+ 0x02020001, /* MC_EMEM_ARB_DA_TURNS */
+ 0x00060402, /* MC_EMEM_ARB_DA_COVERS */
+ 0x74030303, /* MC_EMEM_ARB_MISC0 */
+ 0x001f0000, /* MC_EMEM_ARB_RING1_THROTTLE */
+ 0x50000000, /* EMC_FBIO_SPARE */
+ 0xff00ff00, /* EMC_CFG_RSV */
+ },
+ 0x00000009, /* EMC_ZCAL_WAIT_CNT after clock change */
+ 0x001fffff, /* EMC_AUTO_CAL_INTERVAL */
+ 0x00000001, /* EMC_CFG.PERIODIC_QRST */
+ 0x00000000, /* Mode Register 0 */
+ 0x00010022, /* Mode Register 1 */
+ 0x00020001, /* Mode Register 2 */
+ 0x00000001, /* EMC_CFG.DYN_SELF_REF */
+ },
+ {
+ 0x32, /* Rev 3.2 */
+ 51000, /* SDRAM frequency */
+ {
+ 0x00000003, /* EMC_RC */
+ 0x00000006, /* EMC_RFC */
+ 0x00000002, /* EMC_RAS */
+ 0x00000002, /* EMC_RP */
+ 0x00000004, /* EMC_R2W */
+ 0x00000004, /* EMC_W2R */
+ 0x00000001, /* EMC_R2P */
+ 0x00000005, /* EMC_W2P */
+ 0x00000002, /* EMC_RD_RCD */
+ 0x00000002, /* EMC_WR_RCD */
+ 0x00000001, /* EMC_RRD */
+ 0x00000001, /* EMC_REXT */
+ 0x00000000, /* EMC_WEXT */
+ 0x00000001, /* EMC_WDV */
+ 0x00000003, /* EMC_QUSE */
+ 0x00000001, /* EMC_QRST */
+ 0x00000009, /* EMC_QSAFE */
+ 0x0000000a, /* EMC_RDV */
+ 0x000000c0, /* EMC_REFRESH */
+ 0x00000000, /* EMC_BURST_REFRESH_NUM */
+ 0x00000030, /* EMC_PRE_REFRESH_REQ_CNT */
+ 0x00000001, /* EMC_PDEX2WR */
+ 0x00000001, /* EMC_PDEX2RD */
+ 0x00000002, /* EMC_PCHG2PDEN */
+ 0x00000000, /* EMC_ACT2PDEN */
+ 0x00000001, /* EMC_AR2PDEN */
+ 0x00000007, /* EMC_RW2PDEN */
+ 0x00000008, /* EMC_TXSR */
+ 0x00000008, /* EMC_TXSRDLL */
+ 0x00000003, /* EMC_TCKE */
+ 0x00000008, /* EMC_TFAW */
+ 0x00000004, /* EMC_TRPAB */
+ 0x00000004, /* EMC_TCLKSTABLE */
+ 0x00000002, /* EMC_TCLKSTOP */
+ 0x000000d5, /* EMC_TREFBW */
+ 0x00000004, /* EMC_QUSE_EXTRA */
+ 0x00000004, /* EMC_FBIO_CFG6 */
+ 0x00000000, /* EMC_ODT_WRITE */
+ 0x00000000, /* EMC_ODT_READ */
+ 0x00004282, /* EMC_FBIO_CFG5 */
+ 0x007800a4, /* EMC_CFG_DIG_DLL */
+ 0x00008000, /* EMC_CFG_DIG_DLL_PERIOD */
+ 0x000a0000, /* EMC_DLL_XFORM_DQS0 */
+ 0x000a0000, /* EMC_DLL_XFORM_DQS1 */
+ 0x000a0000, /* EMC_DLL_XFORM_DQS2 */
+ 0x000a0000, /* EMC_DLL_XFORM_DQS3 */
+ 0x00000010, /* EMC_DLL_XFORM_DQS4 */
+ 0x00000010, /* EMC_DLL_XFORM_DQS5 */
+ 0x00000010, /* EMC_DLL_XFORM_DQS6 */
+ 0x00000010, /* EMC_DLL_XFORM_DQS7 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE0 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE1 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE2 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE3 */
+ 0x00000018, /* EMC_DLL_XFORM_QUSE4 */
+ 0x00000018, /* EMC_DLL_XFORM_QUSE5 */
+ 0x00000018, /* EMC_DLL_XFORM_QUSE6 */
+ 0x00000018, /* EMC_DLL_XFORM_QUSE7 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS0 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS1 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS2 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS3 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS4 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS5 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS6 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS7 */
+ 0x00080000, /* EMC_DLL_XFORM_DQ0 */
+ 0x00080000, /* EMC_DLL_XFORM_DQ1 */
+ 0x00080000, /* EMC_DLL_XFORM_DQ2 */
+ 0x00080000, /* EMC_DLL_XFORM_DQ3 */
+ 0x00100220, /* EMC_XM2CMDPADCTRL */
+ 0x0800201c, /* EMC_XM2DQSPADCTRL2 */
+ 0x00000000, /* EMC_XM2DQPADCTRL2 */
+ 0x77ffc004, /* EMC_XM2CLKPADCTRL */
+ 0x01f1f008, /* EMC_XM2COMPPADCTRL */
+ 0x00000000, /* EMC_XM2VTTGENPADCTRL */
+ 0x00000007, /* EMC_XM2VTTGENPADCTRL2 */
+ 0x08000068, /* EMC_XM2QUSEPADCTRL */
+ 0x08000000, /* EMC_XM2DQSPADCTRL3 */
+ 0x00000802, /* EMC_CTT_TERM_CTRL */
+ 0x00064000, /* EMC_ZCAL_INTERVAL */
+ 0x00000013, /* EMC_ZCAL_WAIT_CNT */
+ 0x00090009, /* EMC_MRS_WAIT_CNT */
+ 0xa0f10000, /* EMC_AUTO_CAL_CONFIG */
+ 0x00000000, /* EMC_CTT */
+ 0x00000000, /* EMC_CTT_DURATION */
+ 0x80000287, /* EMC_DYN_SELF_REF_CONTROL */
+ 0x00010001, /* MC_EMEM_ARB_CFG */
+ 0xc000000a, /* MC_EMEM_ARB_OUTSTANDING_REQ */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_RCD */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_RP */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_RC */
+ 0x00000000, /* MC_EMEM_ARB_TIMING_RAS */
+ 0x00000003, /* MC_EMEM_ARB_TIMING_FAW */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_RRD */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_RAP2PRE */
+ 0x00000004, /* MC_EMEM_ARB_TIMING_WAP2PRE */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_R2R */
+ 0x00000000, /* MC_EMEM_ARB_TIMING_W2W */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_R2W */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_W2R */
+ 0x02020001, /* MC_EMEM_ARB_DA_TURNS */
+ 0x00060402, /* MC_EMEM_ARB_DA_COVERS */
+ 0x72c30303, /* MC_EMEM_ARB_MISC0 */
+ 0x001f0000, /* MC_EMEM_ARB_RING1_THROTTLE */
+ 0x50000000, /* EMC_FBIO_SPARE */
+ 0xff00ff00, /* EMC_CFG_RSV */
+ },
+ 0x00000009, /* EMC_ZCAL_WAIT_CNT after clock change */
+ 0x001fffff, /* EMC_AUTO_CAL_INTERVAL */
+ 0x00000001, /* EMC_CFG.PERIODIC_QRST */
+ 0x00000000, /* Mode Register 0 */
+ 0x00010022, /* Mode Register 1 */
+ 0x00020001, /* Mode Register 2 */
+ 0x00000001, /* EMC_CFG.DYN_SELF_REF */
+ },
+ {
+ 0x32, /* Rev 3.2 */
+ 102000, /* SDRAM frequency */
+ {
+ 0x00000006, /* EMC_RC */
+ 0x0000000d, /* EMC_RFC */
+ 0x00000004, /* EMC_RAS */
+ 0x00000002, /* EMC_RP */
+ 0x00000004, /* EMC_R2W */
+ 0x00000004, /* EMC_W2R */
+ 0x00000001, /* EMC_R2P */
+ 0x00000005, /* EMC_W2P */
+ 0x00000002, /* EMC_RD_RCD */
+ 0x00000002, /* EMC_WR_RCD */
+ 0x00000001, /* EMC_RRD */
+ 0x00000001, /* EMC_REXT */
+ 0x00000000, /* EMC_WEXT */
+ 0x00000001, /* EMC_WDV */
+ 0x00000003, /* EMC_QUSE */
+ 0x00000001, /* EMC_QRST */
+ 0x00000009, /* EMC_QSAFE */
+ 0x00000009, /* EMC_RDV */
+ 0x00000181, /* EMC_REFRESH */
+ 0x00000000, /* EMC_BURST_REFRESH_NUM */
+ 0x00000060, /* EMC_PRE_REFRESH_REQ_CNT */
+ 0x00000001, /* EMC_PDEX2WR */
+ 0x00000001, /* EMC_PDEX2RD */
+ 0x00000002, /* EMC_PCHG2PDEN */
+ 0x00000000, /* EMC_ACT2PDEN */
+ 0x00000001, /* EMC_AR2PDEN */
+ 0x00000007, /* EMC_RW2PDEN */
+ 0x0000000f, /* EMC_TXSR */
+ 0x0000000f, /* EMC_TXSRDLL */
+ 0x00000003, /* EMC_TCKE */
+ 0x00000008, /* EMC_TFAW */
+ 0x00000004, /* EMC_TRPAB */
+ 0x00000004, /* EMC_TCLKSTABLE */
+ 0x00000002, /* EMC_TCLKSTOP */
+ 0x000001a9, /* EMC_TREFBW */
+ 0x00000004, /* EMC_QUSE_EXTRA */
+ 0x00000004, /* EMC_FBIO_CFG6 */
+ 0x00000000, /* EMC_ODT_WRITE */
+ 0x00000000, /* EMC_ODT_READ */
+ 0x00004282, /* EMC_FBIO_CFG5 */
+ 0x007800a4, /* EMC_CFG_DIG_DLL */
+ 0x00008000, /* EMC_CFG_DIG_DLL_PERIOD */
+ 0x000a0000, /* EMC_DLL_XFORM_DQS0 */
+ 0x000a0000, /* EMC_DLL_XFORM_DQS1 */
+ 0x000a0000, /* EMC_DLL_XFORM_DQS2 */
+ 0x000a0000, /* EMC_DLL_XFORM_DQS3 */
+ 0x00000010, /* EMC_DLL_XFORM_DQS4 */
+ 0x00000010, /* EMC_DLL_XFORM_DQS5 */
+ 0x00000010, /* EMC_DLL_XFORM_DQS6 */
+ 0x00000010, /* EMC_DLL_XFORM_DQS7 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE0 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE1 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE2 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE3 */
+ 0x00000008, /* EMC_DLL_XFORM_QUSE4 */
+ 0x00000008, /* EMC_DLL_XFORM_QUSE5 */
+ 0x00000008, /* EMC_DLL_XFORM_QUSE6 */
+ 0x00000008, /* EMC_DLL_XFORM_QUSE7 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS0 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS1 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS2 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS3 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS4 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS5 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS6 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS7 */
+ 0x00080000, /* EMC_DLL_XFORM_DQ0 */
+ 0x00080000, /* EMC_DLL_XFORM_DQ1 */
+ 0x00080000, /* EMC_DLL_XFORM_DQ2 */
+ 0x00080000, /* EMC_DLL_XFORM_DQ3 */
+ 0x00120220, /* EMC_XM2CMDPADCTRL */
+ 0x0800201c, /* EMC_XM2DQSPADCTRL2 */
+ 0x00000000, /* EMC_XM2DQPADCTRL2 */
+ 0x77ffc004, /* EMC_XM2CLKPADCTRL */
+ 0x01f1f008, /* EMC_XM2COMPPADCTRL */
+ 0x00000000, /* EMC_XM2VTTGENPADCTRL */
+ 0x00000007, /* EMC_XM2VTTGENPADCTRL2 */
+ 0x08000068, /* EMC_XM2QUSEPADCTRL */
+ 0x08000000, /* EMC_XM2DQSPADCTRL3 */
+ 0x00000802, /* EMC_CTT_TERM_CTRL */
+ 0x00064000, /* EMC_ZCAL_INTERVAL */
+ 0x00000025, /* EMC_ZCAL_WAIT_CNT */
+ 0x00090009, /* EMC_MRS_WAIT_CNT */
+ 0xa0f10000, /* EMC_AUTO_CAL_CONFIG */
+ 0x00000000, /* EMC_CTT */
+ 0x00000000, /* EMC_CTT_DURATION */
+ 0x8000040b, /* EMC_DYN_SELF_REF_CONTROL */
+ 0x00000001, /* MC_EMEM_ARB_CFG */
+ 0xc0000013, /* MC_EMEM_ARB_OUTSTANDING_REQ */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_RCD */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_RP */
+ 0x00000003, /* MC_EMEM_ARB_TIMING_RC */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_RAS */
+ 0x00000003, /* MC_EMEM_ARB_TIMING_FAW */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_RRD */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_RAP2PRE */
+ 0x00000004, /* MC_EMEM_ARB_TIMING_WAP2PRE */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_R2R */
+ 0x00000000, /* MC_EMEM_ARB_TIMING_W2W */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_R2W */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_W2R */
+ 0x02020001, /* MC_EMEM_ARB_DA_TURNS */
+ 0x00060403, /* MC_EMEM_ARB_DA_COVERS */
+ 0x72430504, /* MC_EMEM_ARB_MISC0 */
+ 0x001f0000, /* MC_EMEM_ARB_RING1_THROTTLE */
+ 0x10000000, /* EMC_FBIO_SPARE */
+ 0xff00ff00, /* EMC_CFG_RSV */
+ },
+ 0x0000000a, /* EMC_ZCAL_WAIT_CNT after clock change */
+ 0x001fffff, /* EMC_AUTO_CAL_INTERVAL */
+ 0x00000001, /* EMC_CFG.PERIODIC_QRST */
+ 0x00000000, /* Mode Register 0 */
+ 0x00010022, /* Mode Register 1 */
+ 0x00020001, /* Mode Register 2 */
+ 0x00000001, /* EMC_CFG.DYN_SELF_REF */
+ },
+ {
+ 0x32, /* Rev 3.2 */
+ 204000, /* SDRAM frequency */
+ {
+ 0x0000000c, /* EMC_RC */
+ 0x0000001a, /* EMC_RFC */
+ 0x00000008, /* EMC_RAS */
+ 0x00000003, /* EMC_RP */
+ 0x00000005, /* EMC_R2W */
+ 0x00000004, /* EMC_W2R */
+ 0x00000001, /* EMC_R2P */
+ 0x00000006, /* EMC_W2P */
+ 0x00000003, /* EMC_RD_RCD */
+ 0x00000003, /* EMC_WR_RCD */
+ 0x00000002, /* EMC_RRD */
+ 0x00000002, /* EMC_REXT */
+ 0x00000000, /* EMC_WEXT */
+ 0x00000001, /* EMC_WDV */
+ 0x00000003, /* EMC_QUSE */
+ 0x00000001, /* EMC_QRST */
+ 0x0000000a, /* EMC_QSAFE */
+ 0x0000000a, /* EMC_RDV */
+ 0x00000303, /* EMC_REFRESH */
+ 0x00000000, /* EMC_BURST_REFRESH_NUM */
+ 0x000000c0, /* EMC_PRE_REFRESH_REQ_CNT */
+ 0x00000001, /* EMC_PDEX2WR */
+ 0x00000001, /* EMC_PDEX2RD */
+ 0x00000003, /* EMC_PCHG2PDEN */
+ 0x00000000, /* EMC_ACT2PDEN */
+ 0x00000001, /* EMC_AR2PDEN */
+ 0x00000007, /* EMC_RW2PDEN */
+ 0x0000001d, /* EMC_TXSR */
+ 0x0000001d, /* EMC_TXSRDLL */
+ 0x00000004, /* EMC_TCKE */
+ 0x0000000b, /* EMC_TFAW */
+ 0x00000005, /* EMC_TRPAB */
+ 0x00000004, /* EMC_TCLKSTABLE */
+ 0x00000002, /* EMC_TCLKSTOP */
+ 0x00000351, /* EMC_TREFBW */
+ 0x00000004, /* EMC_QUSE_EXTRA */
+ 0x00000006, /* EMC_FBIO_CFG6 */
+ 0x00000000, /* EMC_ODT_WRITE */
+ 0x00000000, /* EMC_ODT_READ */
+ 0x00004282, /* EMC_FBIO_CFG5 */
+ 0x004400a4, /* EMC_CFG_DIG_DLL */
+ 0x00008000, /* EMC_CFG_DIG_DLL_PERIOD */
+ 0x00074000, /* EMC_DLL_XFORM_DQS0 */
+ 0x00074000, /* EMC_DLL_XFORM_DQS1 */
+ 0x00074000, /* EMC_DLL_XFORM_DQS2 */
+ 0x00074000, /* EMC_DLL_XFORM_DQS3 */
+ 0x00000010, /* EMC_DLL_XFORM_DQS4 */
+ 0x00000010, /* EMC_DLL_XFORM_DQS5 */
+ 0x00000010, /* EMC_DLL_XFORM_DQS6 */
+ 0x00000010, /* EMC_DLL_XFORM_DQS7 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE0 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE1 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE2 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE3 */
+ 0x00000018, /* EMC_DLL_XFORM_QUSE4 */
+ 0x00000018, /* EMC_DLL_XFORM_QUSE5 */
+ 0x00000018, /* EMC_DLL_XFORM_QUSE6 */
+ 0x00000018, /* EMC_DLL_XFORM_QUSE7 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS0 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS1 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS2 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS3 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS4 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS5 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS6 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS7 */
+ 0x00078000, /* EMC_DLL_XFORM_DQ0 */
+ 0x00078000, /* EMC_DLL_XFORM_DQ1 */
+ 0x00078000, /* EMC_DLL_XFORM_DQ2 */
+ 0x00078000, /* EMC_DLL_XFORM_DQ3 */
+ 0x00100220, /* EMC_XM2CMDPADCTRL */
+ 0x0800201c, /* EMC_XM2DQSPADCTRL2 */
+ 0x00000000, /* EMC_XM2DQPADCTRL2 */
+ 0x77ffc004, /* EMC_XM2CLKPADCTRL */
+ 0x01f1f008, /* EMC_XM2COMPPADCTRL */
+ 0x00000000, /* EMC_XM2VTTGENPADCTRL */
+ 0x00000007, /* EMC_XM2VTTGENPADCTRL2 */
+ 0x08000068, /* EMC_XM2QUSEPADCTRL */
+ 0x08000000, /* EMC_XM2DQSPADCTRL3 */
+ 0x00000802, /* EMC_CTT_TERM_CTRL */
+ 0x00064000, /* EMC_ZCAL_INTERVAL */
+ 0x0000004a, /* EMC_ZCAL_WAIT_CNT */
+ 0x00090009, /* EMC_MRS_WAIT_CNT */
+ 0xa0f10000, /* EMC_AUTO_CAL_CONFIG */
+ 0x00000000, /* EMC_CTT */
+ 0x00000000, /* EMC_CTT_DURATION */
+ 0x80000713, /* EMC_DYN_SELF_REF_CONTROL */
+ 0x00000003, /* MC_EMEM_ARB_CFG */
+ 0xc0000025, /* MC_EMEM_ARB_OUTSTANDING_REQ */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_RCD */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_RP */
+ 0x00000006, /* MC_EMEM_ARB_TIMING_RC */
+ 0x00000003, /* MC_EMEM_ARB_TIMING_RAS */
+ 0x00000005, /* MC_EMEM_ARB_TIMING_FAW */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_RRD */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_RAP2PRE */
+ 0x00000004, /* MC_EMEM_ARB_TIMING_WAP2PRE */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_R2R */
+ 0x00000000, /* MC_EMEM_ARB_TIMING_W2W */
+ 0x00000003, /* MC_EMEM_ARB_TIMING_R2W */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_W2R */
+ 0x02030001, /* MC_EMEM_ARB_DA_TURNS */
+ 0x00070506, /* MC_EMEM_ARB_DA_COVERS */
+ 0x71e40a07, /* MC_EMEM_ARB_MISC0 */
+ 0x001f0000, /* MC_EMEM_ARB_RING1_THROTTLE */
+ 0x50000000, /* EMC_FBIO_SPARE */
+ 0xff00ff00, /* EMC_CFG_RSV */
+ },
+ 0x00000013, /* EMC_ZCAL_WAIT_CNT after clock change */
+ 0x001fffff, /* EMC_AUTO_CAL_INTERVAL */
+ 0x00000001, /* EMC_CFG.PERIODIC_QRST */
+ 0x00000000, /* Mode Register 0 */
+ 0x00010042, /* Mode Register 1 */
+ 0x00020001, /* Mode Register 2 */
+ 0x00000001, /* EMC_CFG.DYN_SELF_REF */
+ },
+ {
+ 0x32, /* Rev 3.2 */
+ 533000, /* SDRAM frequency */
+ {
+ 0x0000001f, /* EMC_RC */
+ 0x00000045, /* EMC_RFC */
+ 0x00000016, /* EMC_RAS */
+ 0x00000009, /* EMC_RP */
+ 0x00000008, /* EMC_R2W */
+ 0x00000009, /* EMC_W2R */
+ 0x00000003, /* EMC_R2P */
+ 0x0000000d, /* EMC_W2P */
+ 0x00000009, /* EMC_RD_RCD */
+ 0x00000009, /* EMC_WR_RCD */
+ 0x00000005, /* EMC_RRD */
+ 0x00000003, /* EMC_REXT */
+ 0x00000000, /* EMC_WEXT */
+ 0x00000004, /* EMC_WDV */
+ 0x00000009, /* EMC_QUSE */
+ 0x00000006, /* EMC_QRST */
+ 0x0000000c, /* EMC_QSAFE */
+ 0x00000010, /* EMC_RDV */
+ 0x000007df, /* EMC_REFRESH */
+ 0x00000000, /* EMC_BURST_REFRESH_NUM */
+ 0x000001f7, /* EMC_PRE_REFRESH_REQ_CNT */
+ 0x00000003, /* EMC_PDEX2WR */
+ 0x00000003, /* EMC_PDEX2RD */
+ 0x00000009, /* EMC_PCHG2PDEN */
+ 0x00000000, /* EMC_ACT2PDEN */
+ 0x00000001, /* EMC_AR2PDEN */
+ 0x0000000f, /* EMC_RW2PDEN */
+ 0x0000004b, /* EMC_TXSR */
+ 0x0000004b, /* EMC_TXSRDLL */
+ 0x00000008, /* EMC_TCKE */
+ 0x0000001b, /* EMC_TFAW */
+ 0x0000000c, /* EMC_TRPAB */
+ 0x00000004, /* EMC_TCLKSTABLE */
+ 0x00000002, /* EMC_TCLKSTOP */
+ 0x000008aa, /* EMC_TREFBW */
+ 0x00000000, /* EMC_QUSE_EXTRA */
+ 0x00000006, /* EMC_FBIO_CFG6 */
+ 0x00000000, /* EMC_ODT_WRITE */
+ 0x00000000, /* EMC_ODT_READ */
+ 0x00006282, /* EMC_FBIO_CFG5 */
+ 0xf0120091, /* EMC_CFG_DIG_DLL */
+ 0x00008000, /* EMC_CFG_DIG_DLL_PERIOD */
+ 0x0000000a, /* EMC_DLL_XFORM_DQS0 */
+ 0x0000000a, /* EMC_DLL_XFORM_DQS1 */
+ 0x0000000a, /* EMC_DLL_XFORM_DQS2 */
+ 0x0000000a, /* EMC_DLL_XFORM_DQS3 */
+ 0x00000010, /* EMC_DLL_XFORM_DQS4 */
+ 0x00000010, /* EMC_DLL_XFORM_DQS5 */
+ 0x00000010, /* EMC_DLL_XFORM_DQS6 */
+ 0x00000010, /* EMC_DLL_XFORM_DQS7 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE0 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE1 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE2 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE3 */
+ 0x00000008, /* EMC_DLL_XFORM_QUSE4 */
+ 0x00000008, /* EMC_DLL_XFORM_QUSE5 */
+ 0x00000008, /* EMC_DLL_XFORM_QUSE6 */
+ 0x00000008, /* EMC_DLL_XFORM_QUSE7 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS0 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS1 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS2 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS3 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS4 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS5 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS6 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS7 */
+ 0x0000000c, /* EMC_DLL_XFORM_DQ0 */
+ 0x0000000c, /* EMC_DLL_XFORM_DQ1 */
+ 0x0000000c, /* EMC_DLL_XFORM_DQ2 */
+ 0x0000000c, /* EMC_DLL_XFORM_DQ3 */
+ 0x000b0220, /* EMC_XM2CMDPADCTRL */
+ 0x0800003d, /* EMC_XM2DQSPADCTRL2 */
+ 0x00000000, /* EMC_XM2DQPADCTRL2 */
+ 0x77ffc004, /* EMC_XM2CLKPADCTRL */
+ 0x01f1f408, /* EMC_XM2COMPPADCTRL */
+ 0x00000000, /* EMC_XM2VTTGENPADCTRL */
+ 0x00000007, /* EMC_XM2VTTGENPADCTRL2 */
+ 0x08000068, /* EMC_XM2QUSEPADCTRL */
+ 0x08000000, /* EMC_XM2DQSPADCTRL3 */
+ 0x00000802, /* EMC_CTT_TERM_CTRL */
+ 0x00064000, /* EMC_ZCAL_INTERVAL */
+ 0x000000c0, /* EMC_ZCAL_WAIT_CNT */
+ 0x000e000e, /* EMC_MRS_WAIT_CNT */
+ 0xa0f10202, /* EMC_AUTO_CAL_CONFIG */
+ 0x00000000, /* EMC_CTT */
+ 0x00000000, /* EMC_CTT_DURATION */
+ 0x800010d9, /* EMC_DYN_SELF_REF_CONTROL */
+ 0x00000008, /* MC_EMEM_ARB_CFG */
+ 0x80000060, /* MC_EMEM_ARB_OUTSTANDING_REQ */
+ 0x00000003, /* MC_EMEM_ARB_TIMING_RCD */
+ 0x00000004, /* MC_EMEM_ARB_TIMING_RP */
+ 0x00000010, /* MC_EMEM_ARB_TIMING_RC */
+ 0x0000000a, /* MC_EMEM_ARB_TIMING_RAS */
+ 0x0000000d, /* MC_EMEM_ARB_TIMING_FAW */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_RRD */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_RAP2PRE */
+ 0x00000008, /* MC_EMEM_ARB_TIMING_WAP2PRE */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_R2R */
+ 0x00000000, /* MC_EMEM_ARB_TIMING_W2W */
+ 0x00000004, /* MC_EMEM_ARB_TIMING_R2W */
+ 0x00000005, /* MC_EMEM_ARB_TIMING_W2R */
+ 0x05040002, /* MC_EMEM_ARB_DA_TURNS */
+ 0x00110b10, /* MC_EMEM_ARB_DA_COVERS */
+ 0x71c81811, /* MC_EMEM_ARB_MISC0 */
+ 0x001f0000, /* MC_EMEM_ARB_RING1_THROTTLE */
+ 0xe0000000, /* EMC_FBIO_SPARE */
+ 0xff00ff88, /* EMC_CFG_RSV */
+ },
+ 0x00000030, /* EMC_ZCAL_WAIT_CNT after clock change */
+ 0x001fffff, /* EMC_AUTO_CAL_INTERVAL */
+ 0x00000001, /* EMC_CFG.PERIODIC_QRST */
+ 0x00000000, /* Mode Register 0 */
+ 0x000100c2, /* Mode Register 1 */
+ 0x00020006, /* Mode Register 2 */
+ 0x00000000, /* EMC_CFG.DYN_SELF_REF */
+ },
+};
+
+static const struct tegra_emc_table cardhu_emc_tables_edb8132b2ma[] = {
+ {
+ 0x32, /* Rev 3.2 */
+ 25500, /* SDRAM frequency */
+ {
+ 0x00000001, /* EMC_RC */
+ 0x00000003, /* EMC_RFC */
+ 0x00000002, /* EMC_RAS */
+ 0x00000002, /* EMC_RP */
+ 0x00000004, /* EMC_R2W */
+ 0x00000004, /* EMC_W2R */
+ 0x00000001, /* EMC_R2P */
+ 0x00000005, /* EMC_W2P */
+ 0x00000002, /* EMC_RD_RCD */
+ 0x00000002, /* EMC_WR_RCD */
+ 0x00000001, /* EMC_RRD */
+ 0x00000001, /* EMC_REXT */
+ 0x00000000, /* EMC_WEXT */
+ 0x00000001, /* EMC_WDV */
+ 0x00000003, /* EMC_QUSE */
+ 0x00000001, /* EMC_QRST */
+ 0x00000009, /* EMC_QSAFE */
+ 0x0000000a, /* EMC_RDV */
+ 0x00000060, /* EMC_REFRESH */
+ 0x00000000, /* EMC_BURST_REFRESH_NUM */
+ 0x00000018, /* EMC_PRE_REFRESH_REQ_CNT */
+ 0x00000001, /* EMC_PDEX2WR */
+ 0x00000001, /* EMC_PDEX2RD */
+ 0x00000002, /* EMC_PCHG2PDEN */
+ 0x00000000, /* EMC_ACT2PDEN */
+ 0x00000001, /* EMC_AR2PDEN */
+ 0x00000007, /* EMC_RW2PDEN */
+ 0x00000004, /* EMC_TXSR */
+ 0x00000004, /* EMC_TXSRDLL */
+ 0x00000003, /* EMC_TCKE */
+ 0x00000008, /* EMC_TFAW */
+ 0x00000004, /* EMC_TRPAB */
+ 0x00000004, /* EMC_TCLKSTABLE */
+ 0x00000002, /* EMC_TCLKSTOP */
+ 0x0000006b, /* EMC_TREFBW */
+ 0x00000004, /* EMC_QUSE_EXTRA */
+ 0x00000006, /* EMC_FBIO_CFG6 */
+ 0x00000000, /* EMC_ODT_WRITE */
+ 0x00000000, /* EMC_ODT_READ */
+ 0x00004282, /* EMC_FBIO_CFG5 */
+ 0x007800a4, /* EMC_CFG_DIG_DLL */
+ 0x00008000, /* EMC_CFG_DIG_DLL_PERIOD */
+ 0x000a0000, /* EMC_DLL_XFORM_DQS0 */
+ 0x000a0000, /* EMC_DLL_XFORM_DQS1 */
+ 0x000a0000, /* EMC_DLL_XFORM_DQS2 */
+ 0x000a0000, /* EMC_DLL_XFORM_DQS3 */
+ 0x00000010, /* EMC_DLL_XFORM_DQS4 */
+ 0x00000010, /* EMC_DLL_XFORM_DQS5 */
+ 0x00000010, /* EMC_DLL_XFORM_DQS6 */
+ 0x00000010, /* EMC_DLL_XFORM_DQS7 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE0 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE1 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE2 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE3 */
+ 0x00000008, /* EMC_DLL_XFORM_QUSE4 */
+ 0x00000008, /* EMC_DLL_XFORM_QUSE5 */
+ 0x00000008, /* EMC_DLL_XFORM_QUSE6 */
+ 0x00000008, /* EMC_DLL_XFORM_QUSE7 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS0 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS1 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS2 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS3 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS4 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS5 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS6 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS7 */
+ 0x00080000, /* EMC_DLL_XFORM_DQ0 */
+ 0x00080000, /* EMC_DLL_XFORM_DQ1 */
+ 0x00080000, /* EMC_DLL_XFORM_DQ2 */
+ 0x00080000, /* EMC_DLL_XFORM_DQ3 */
+ 0x00120220, /* EMC_XM2CMDPADCTRL */
+ 0x0800201c, /* EMC_XM2DQSPADCTRL2 */
+ 0x00000000, /* EMC_XM2DQPADCTRL2 */
+ 0x77ffc004, /* EMC_XM2CLKPADCTRL */
+ 0x01f1f008, /* EMC_XM2COMPPADCTRL */
+ 0x00000000, /* EMC_XM2VTTGENPADCTRL */
+ 0x00000007, /* EMC_XM2VTTGENPADCTRL2 */
+ 0x08000068, /* EMC_XM2QUSEPADCTRL */
+ 0x08000000, /* EMC_XM2DQSPADCTRL3 */
+ 0x00000802, /* EMC_CTT_TERM_CTRL */
+ 0x00064000, /* EMC_ZCAL_INTERVAL */
+ 0x0000000a, /* EMC_ZCAL_WAIT_CNT */
+ 0x00090009, /* EMC_MRS_WAIT_CNT */
+ 0xa0f10000, /* EMC_AUTO_CAL_CONFIG */
+ 0x00000000, /* EMC_CTT */
+ 0x00000000, /* EMC_CTT_DURATION */
+ 0x800001c5, /* EMC_DYN_SELF_REF_CONTROL */
+ 0x00020001, /* MC_EMEM_ARB_CFG */
+ 0xc0000008, /* MC_EMEM_ARB_OUTSTANDING_REQ */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_RCD */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_RP */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_RC */
+ 0x00000000, /* MC_EMEM_ARB_TIMING_RAS */
+ 0x00000003, /* MC_EMEM_ARB_TIMING_FAW */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_RRD */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_RAP2PRE */
+ 0x00000004, /* MC_EMEM_ARB_TIMING_WAP2PRE */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_R2R */
+ 0x00000000, /* MC_EMEM_ARB_TIMING_W2W */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_R2W */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_W2R */
+ 0x02020001, /* MC_EMEM_ARB_DA_TURNS */
+ 0x00060402, /* MC_EMEM_ARB_DA_COVERS */
+ 0x73e30303, /* MC_EMEM_ARB_MISC0 */
+ 0x001f0000, /* MC_EMEM_ARB_RING1_THROTTLE */
+ 0x50000000, /* EMC_FBIO_SPARE */
+ 0xff00ff00, /* EMC_CFG_RSV */
+ },
+ 0x00000009, /* EMC_ZCAL_WAIT_CNT after clock change */
+ 0x001fffff, /* EMC_AUTO_CAL_INTERVAL */
+ 0x00000001, /* EMC_CFG.PERIODIC_QRST */
+ 0x00000000, /* Mode Register 0 */
+ 0x00010022, /* Mode Register 1 */
+ 0x00020001, /* Mode Register 2 */
+ 0x00000001, /* EMC_CFG.DYN_SELF_REF */
+ },
+ {
+ 0x32, /* Rev 3.2 */
+ 51000, /* SDRAM frequency */
+ {
+ 0x00000003, /* EMC_RC */
+ 0x00000006, /* EMC_RFC */
+ 0x00000002, /* EMC_RAS */
+ 0x00000002, /* EMC_RP */
+ 0x00000004, /* EMC_R2W */
+ 0x00000004, /* EMC_W2R */
+ 0x00000001, /* EMC_R2P */
+ 0x00000005, /* EMC_W2P */
+ 0x00000002, /* EMC_RD_RCD */
+ 0x00000002, /* EMC_WR_RCD */
+ 0x00000001, /* EMC_RRD */
+ 0x00000001, /* EMC_REXT */
+ 0x00000000, /* EMC_WEXT */
+ 0x00000001, /* EMC_WDV */
+ 0x00000003, /* EMC_QUSE */
+ 0x00000001, /* EMC_QRST */
+ 0x00000009, /* EMC_QSAFE */
+ 0x0000000a, /* EMC_RDV */
+ 0x000000c0, /* EMC_REFRESH */
+ 0x00000000, /* EMC_BURST_REFRESH_NUM */
+ 0x00000030, /* EMC_PRE_REFRESH_REQ_CNT */
+ 0x00000001, /* EMC_PDEX2WR */
+ 0x00000001, /* EMC_PDEX2RD */
+ 0x00000002, /* EMC_PCHG2PDEN */
+ 0x00000000, /* EMC_ACT2PDEN */
+ 0x00000001, /* EMC_AR2PDEN */
+ 0x00000007, /* EMC_RW2PDEN */
+ 0x00000008, /* EMC_TXSR */
+ 0x00000008, /* EMC_TXSRDLL */
+ 0x00000003, /* EMC_TCKE */
+ 0x00000008, /* EMC_TFAW */
+ 0x00000004, /* EMC_TRPAB */
+ 0x00000004, /* EMC_TCLKSTABLE */
+ 0x00000002, /* EMC_TCLKSTOP */
+ 0x000000d5, /* EMC_TREFBW */
+ 0x00000004, /* EMC_QUSE_EXTRA */
+ 0x00000006, /* EMC_FBIO_CFG6 */
+ 0x00000000, /* EMC_ODT_WRITE */
+ 0x00000000, /* EMC_ODT_READ */
+ 0x00004282, /* EMC_FBIO_CFG5 */
+ 0x007800a4, /* EMC_CFG_DIG_DLL */
+ 0x00008000, /* EMC_CFG_DIG_DLL_PERIOD */
+ 0x000a0000, /* EMC_DLL_XFORM_DQS0 */
+ 0x000a0000, /* EMC_DLL_XFORM_DQS1 */
+ 0x000a0000, /* EMC_DLL_XFORM_DQS2 */
+ 0x000a0000, /* EMC_DLL_XFORM_DQS3 */
+ 0x00000010, /* EMC_DLL_XFORM_DQS4 */
+ 0x00000010, /* EMC_DLL_XFORM_DQS5 */
+ 0x00000010, /* EMC_DLL_XFORM_DQS6 */
+ 0x00000010, /* EMC_DLL_XFORM_DQS7 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE0 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE1 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE2 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE3 */
+ 0x00000018, /* EMC_DLL_XFORM_QUSE4 */
+ 0x00000018, /* EMC_DLL_XFORM_QUSE5 */
+ 0x00000018, /* EMC_DLL_XFORM_QUSE6 */
+ 0x00000018, /* EMC_DLL_XFORM_QUSE7 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS0 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS1 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS2 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS3 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS4 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS5 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS6 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS7 */
+ 0x00080000, /* EMC_DLL_XFORM_DQ0 */
+ 0x00080000, /* EMC_DLL_XFORM_DQ1 */
+ 0x00080000, /* EMC_DLL_XFORM_DQ2 */
+ 0x00080000, /* EMC_DLL_XFORM_DQ3 */
+ 0x00120220, /* EMC_XM2CMDPADCTRL */
+ 0x0800201c, /* EMC_XM2DQSPADCTRL2 */
+ 0x00000000, /* EMC_XM2DQPADCTRL2 */
+ 0x77ffc004, /* EMC_XM2CLKPADCTRL */
+ 0x01f1f008, /* EMC_XM2COMPPADCTRL */
+ 0x00000000, /* EMC_XM2VTTGENPADCTRL */
+ 0x00000007, /* EMC_XM2VTTGENPADCTRL2 */
+ 0x08000068, /* EMC_XM2QUSEPADCTRL */
+ 0x08000000, /* EMC_XM2DQSPADCTRL3 */
+ 0x00000802, /* EMC_CTT_TERM_CTRL */
+ 0x00064000, /* EMC_ZCAL_INTERVAL */
+ 0x00000013, /* EMC_ZCAL_WAIT_CNT */
+ 0x00090009, /* EMC_MRS_WAIT_CNT */
+ 0xa0f10000, /* EMC_AUTO_CAL_CONFIG */
+ 0x00000000, /* EMC_CTT */
+ 0x00000000, /* EMC_CTT_DURATION */
+ 0x80000287, /* EMC_DYN_SELF_REF_CONTROL */
+ 0x00010001, /* MC_EMEM_ARB_CFG */
+ 0xc000000a, /* MC_EMEM_ARB_OUTSTANDING_REQ */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_RCD */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_RP */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_RC */
+ 0x00000000, /* MC_EMEM_ARB_TIMING_RAS */
+ 0x00000003, /* MC_EMEM_ARB_TIMING_FAW */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_RRD */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_RAP2PRE */
+ 0x00000004, /* MC_EMEM_ARB_TIMING_WAP2PRE */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_R2R */
+ 0x00000000, /* MC_EMEM_ARB_TIMING_W2W */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_R2W */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_W2R */
+ 0x02020001, /* MC_EMEM_ARB_DA_TURNS */
+ 0x00060402, /* MC_EMEM_ARB_DA_COVERS */
+ 0x72c30303, /* MC_EMEM_ARB_MISC0 */
+ 0x001f0000, /* MC_EMEM_ARB_RING1_THROTTLE */
+ 0x50000000, /* EMC_FBIO_SPARE */
+ 0xff00ff00, /* EMC_CFG_RSV */
+ },
+ 0x00000009, /* EMC_ZCAL_WAIT_CNT after clock change */
+ 0x001fffff, /* EMC_AUTO_CAL_INTERVAL */
+ 0x00000001, /* EMC_CFG.PERIODIC_QRST */
+ 0x00000000, /* Mode Register 0 */
+ 0x00010022, /* Mode Register 1 */
+ 0x00020001, /* Mode Register 2 */
+ 0x00000001, /* EMC_CFG.DYN_SELF_REF */
+ },
+ {
+ 0x32, /* Rev 3.2 */
+ 102000, /* SDRAM frequency */
+ {
+ 0x00000006, /* EMC_RC */
+ 0x0000000d, /* EMC_RFC */
+ 0x00000004, /* EMC_RAS */
+ 0x00000002, /* EMC_RP */
+ 0x00000004, /* EMC_R2W */
+ 0x00000004, /* EMC_W2R */
+ 0x00000001, /* EMC_R2P */
+ 0x00000005, /* EMC_W2P */
+ 0x00000002, /* EMC_RD_RCD */
+ 0x00000002, /* EMC_WR_RCD */
+ 0x00000001, /* EMC_RRD */
+ 0x00000001, /* EMC_REXT */
+ 0x00000000, /* EMC_WEXT */
+ 0x00000001, /* EMC_WDV */
+ 0x00000003, /* EMC_QUSE */
+ 0x00000001, /* EMC_QRST */
+ 0x00000009, /* EMC_QSAFE */
+ 0x0000000a, /* EMC_RDV */
+ 0x00000181, /* EMC_REFRESH */
+ 0x00000000, /* EMC_BURST_REFRESH_NUM */
+ 0x00000060, /* EMC_PRE_REFRESH_REQ_CNT */
+ 0x00000001, /* EMC_PDEX2WR */
+ 0x00000001, /* EMC_PDEX2RD */
+ 0x00000002, /* EMC_PCHG2PDEN */
+ 0x00000000, /* EMC_ACT2PDEN */
+ 0x00000001, /* EMC_AR2PDEN */
+ 0x00000007, /* EMC_RW2PDEN */
+ 0x0000000f, /* EMC_TXSR */
+ 0x0000000f, /* EMC_TXSRDLL */
+ 0x00000003, /* EMC_TCKE */
+ 0x00000008, /* EMC_TFAW */
+ 0x00000004, /* EMC_TRPAB */
+ 0x00000004, /* EMC_TCLKSTABLE */
+ 0x00000002, /* EMC_TCLKSTOP */
+ 0x000001a9, /* EMC_TREFBW */
+ 0x00000004, /* EMC_QUSE_EXTRA */
+ 0x00000006, /* EMC_FBIO_CFG6 */
+ 0x00000000, /* EMC_ODT_WRITE */
+ 0x00000000, /* EMC_ODT_READ */
+ 0x00004282, /* EMC_FBIO_CFG5 */
+ 0x007800a4, /* EMC_CFG_DIG_DLL */
+ 0x00008000, /* EMC_CFG_DIG_DLL_PERIOD */
+ 0x000a0000, /* EMC_DLL_XFORM_DQS0 */
+ 0x000a0000, /* EMC_DLL_XFORM_DQS1 */
+ 0x000a0000, /* EMC_DLL_XFORM_DQS2 */
+ 0x000a0000, /* EMC_DLL_XFORM_DQS3 */
+ 0x00000010, /* EMC_DLL_XFORM_DQS4 */
+ 0x00000010, /* EMC_DLL_XFORM_DQS5 */
+ 0x00000010, /* EMC_DLL_XFORM_DQS6 */
+ 0x00000010, /* EMC_DLL_XFORM_DQS7 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE0 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE1 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE2 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE3 */
+ 0x00000008, /* EMC_DLL_XFORM_QUSE4 */
+ 0x00000008, /* EMC_DLL_XFORM_QUSE5 */
+ 0x00000008, /* EMC_DLL_XFORM_QUSE6 */
+ 0x00000008, /* EMC_DLL_XFORM_QUSE7 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS0 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS1 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS2 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS3 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS4 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS5 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS6 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS7 */
+ 0x00080000, /* EMC_DLL_XFORM_DQ0 */
+ 0x00080000, /* EMC_DLL_XFORM_DQ1 */
+ 0x00080000, /* EMC_DLL_XFORM_DQ2 */
+ 0x00080000, /* EMC_DLL_XFORM_DQ3 */
+ 0x00120220, /* EMC_XM2CMDPADCTRL */
+ 0x0800201c, /* EMC_XM2DQSPADCTRL2 */
+ 0x00000000, /* EMC_XM2DQPADCTRL2 */
+ 0x77ffc004, /* EMC_XM2CLKPADCTRL */
+ 0x01f1f008, /* EMC_XM2COMPPADCTRL */
+ 0x00000000, /* EMC_XM2VTTGENPADCTRL */
+ 0x00000007, /* EMC_XM2VTTGENPADCTRL2 */
+ 0x08000068, /* EMC_XM2QUSEPADCTRL */
+ 0x08000000, /* EMC_XM2DQSPADCTRL3 */
+ 0x00000802, /* EMC_CTT_TERM_CTRL */
+ 0x00064000, /* EMC_ZCAL_INTERVAL */
+ 0x00000025, /* EMC_ZCAL_WAIT_CNT */
+ 0x00090009, /* EMC_MRS_WAIT_CNT */
+ 0xa0f10000, /* EMC_AUTO_CAL_CONFIG */
+ 0x00000000, /* EMC_CTT */
+ 0x00000000, /* EMC_CTT_DURATION */
+ 0x8000040b, /* EMC_DYN_SELF_REF_CONTROL */
+ 0x00000001, /* MC_EMEM_ARB_CFG */
+ 0xc0000013, /* MC_EMEM_ARB_OUTSTANDING_REQ */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_RCD */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_RP */
+ 0x00000003, /* MC_EMEM_ARB_TIMING_RC */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_RAS */
+ 0x00000003, /* MC_EMEM_ARB_TIMING_FAW */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_RRD */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_RAP2PRE */
+ 0x00000004, /* MC_EMEM_ARB_TIMING_WAP2PRE */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_R2R */
+ 0x00000000, /* MC_EMEM_ARB_TIMING_W2W */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_R2W */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_W2R */
+ 0x02020001, /* MC_EMEM_ARB_DA_TURNS */
+ 0x00060403, /* MC_EMEM_ARB_DA_COVERS */
+ 0x72430504, /* MC_EMEM_ARB_MISC0 */
+ 0x001f0000, /* MC_EMEM_ARB_RING1_THROTTLE */
+ 0x50000000, /* EMC_FBIO_SPARE */
+ 0xff00ff00, /* EMC_CFG_RSV */
+ },
+ 0x0000000a, /* EMC_ZCAL_WAIT_CNT after clock change */
+ 0x001fffff, /* EMC_AUTO_CAL_INTERVAL */
+ 0x00000001, /* EMC_CFG.PERIODIC_QRST */
+ 0x00000000, /* Mode Register 0 */
+ 0x00010022, /* Mode Register 1 */
+ 0x00020001, /* Mode Register 2 */
+ 0x00000001, /* EMC_CFG.DYN_SELF_REF */
+ },
+ {
+ 0x32, /* Rev 3.2 */
+ 204000, /* SDRAM frequency */
+ {
+ 0x0000000c, /* EMC_RC */
+ 0x0000001a, /* EMC_RFC */
+ 0x00000008, /* EMC_RAS */
+ 0x00000003, /* EMC_RP */
+ 0x00000005, /* EMC_R2W */
+ 0x00000004, /* EMC_W2R */
+ 0x00000001, /* EMC_R2P */
+ 0x00000006, /* EMC_W2P */
+ 0x00000003, /* EMC_RD_RCD */
+ 0x00000003, /* EMC_WR_RCD */
+ 0x00000002, /* EMC_RRD */
+ 0x00000002, /* EMC_REXT */
+ 0x00000000, /* EMC_WEXT */
+ 0x00000001, /* EMC_WDV */
+ 0x00000004, /* EMC_QUSE */
+ 0x00000001, /* EMC_QRST */
+ 0x0000000b, /* EMC_QSAFE */
+ 0x0000000a, /* EMC_RDV */
+ 0x00000303, /* EMC_REFRESH */
+ 0x00000000, /* EMC_BURST_REFRESH_NUM */
+ 0x000000c0, /* EMC_PRE_REFRESH_REQ_CNT */
+ 0x00000001, /* EMC_PDEX2WR */
+ 0x00000001, /* EMC_PDEX2RD */
+ 0x00000003, /* EMC_PCHG2PDEN */
+ 0x00000000, /* EMC_ACT2PDEN */
+ 0x00000001, /* EMC_AR2PDEN */
+ 0x00000007, /* EMC_RW2PDEN */
+ 0x0000001d, /* EMC_TXSR */
+ 0x0000001d, /* EMC_TXSRDLL */
+ 0x00000004, /* EMC_TCKE */
+ 0x0000000b, /* EMC_TFAW */
+ 0x00000005, /* EMC_TRPAB */
+ 0x00000004, /* EMC_TCLKSTABLE */
+ 0x00000002, /* EMC_TCLKSTOP */
+ 0x00000351, /* EMC_TREFBW */
+ 0x00000005, /* EMC_QUSE_EXTRA */
+ 0x00000004, /* EMC_FBIO_CFG6 */
+ 0x00000000, /* EMC_ODT_WRITE */
+ 0x00000000, /* EMC_ODT_READ */
+ 0x00004282, /* EMC_FBIO_CFG5 */
+ 0x004400a4, /* EMC_CFG_DIG_DLL */
+ 0x00008000, /* EMC_CFG_DIG_DLL_PERIOD */
+ 0x00070000, /* EMC_DLL_XFORM_DQS0 */
+ 0x00070000, /* EMC_DLL_XFORM_DQS1 */
+ 0x00070000, /* EMC_DLL_XFORM_DQS2 */
+ 0x00070000, /* EMC_DLL_XFORM_DQS3 */
+ 0x00000010, /* EMC_DLL_XFORM_DQS4 */
+ 0x00000010, /* EMC_DLL_XFORM_DQS5 */
+ 0x00000010, /* EMC_DLL_XFORM_DQS6 */
+ 0x00000010, /* EMC_DLL_XFORM_DQS7 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE0 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE1 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE2 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE3 */
+ 0x00000018, /* EMC_DLL_XFORM_QUSE4 */
+ 0x00000018, /* EMC_DLL_XFORM_QUSE5 */
+ 0x00000018, /* EMC_DLL_XFORM_QUSE6 */
+ 0x00000018, /* EMC_DLL_XFORM_QUSE7 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS0 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS1 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS2 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS3 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS4 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS5 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS6 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS7 */
+ 0x00078000, /* EMC_DLL_XFORM_DQ0 */
+ 0x00078000, /* EMC_DLL_XFORM_DQ1 */
+ 0x00078000, /* EMC_DLL_XFORM_DQ2 */
+ 0x00078000, /* EMC_DLL_XFORM_DQ3 */
+ 0x000d0220, /* EMC_XM2CMDPADCTRL */
+ 0x0800201c, /* EMC_XM2DQSPADCTRL2 */
+ 0x00000000, /* EMC_XM2DQPADCTRL2 */
+ 0x77ffc004, /* EMC_XM2CLKPADCTRL */
+ 0x01f1f008, /* EMC_XM2COMPPADCTRL */
+ 0x00000000, /* EMC_XM2VTTGENPADCTRL */
+ 0x00000007, /* EMC_XM2VTTGENPADCTRL2 */
+ 0x08000068, /* EMC_XM2QUSEPADCTRL */
+ 0x08000000, /* EMC_XM2DQSPADCTRL3 */
+ 0x00000802, /* EMC_CTT_TERM_CTRL */
+ 0x00064000, /* EMC_ZCAL_INTERVAL */
+ 0x0000004a, /* EMC_ZCAL_WAIT_CNT */
+ 0x00090009, /* EMC_MRS_WAIT_CNT */
+ 0xa0f10000, /* EMC_AUTO_CAL_CONFIG */
+ 0x00000000, /* EMC_CTT */
+ 0x00000000, /* EMC_CTT_DURATION */
+ 0x80000713, /* EMC_DYN_SELF_REF_CONTROL */
+ 0x00000003, /* MC_EMEM_ARB_CFG */
+ 0xc0000025, /* MC_EMEM_ARB_OUTSTANDING_REQ */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_RCD */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_RP */
+ 0x00000006, /* MC_EMEM_ARB_TIMING_RC */
+ 0x00000003, /* MC_EMEM_ARB_TIMING_RAS */
+ 0x00000005, /* MC_EMEM_ARB_TIMING_FAW */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_RRD */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_RAP2PRE */
+ 0x00000004, /* MC_EMEM_ARB_TIMING_WAP2PRE */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_R2R */
+ 0x00000000, /* MC_EMEM_ARB_TIMING_W2W */
+ 0x00000003, /* MC_EMEM_ARB_TIMING_R2W */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_W2R */
+ 0x02030001, /* MC_EMEM_ARB_DA_TURNS */
+ 0x00070506, /* MC_EMEM_ARB_DA_COVERS */
+ 0x71e40a07, /* MC_EMEM_ARB_MISC0 */
+ 0x001f0000, /* MC_EMEM_ARB_RING1_THROTTLE */
+ 0xd0000000, /* EMC_FBIO_SPARE */
+ 0xff00ff00, /* EMC_CFG_RSV */
+ },
+ 0x00000013, /* EMC_ZCAL_WAIT_CNT after clock change */
+ 0x001fffff, /* EMC_AUTO_CAL_INTERVAL */
+ 0x00000001, /* EMC_CFG.PERIODIC_QRST */
+ 0x00000000, /* Mode Register 0 */
+ 0x00010042, /* Mode Register 1 */
+ 0x00020001, /* Mode Register 2 */
+ 0x00000001, /* EMC_CFG.DYN_SELF_REF */
+ },
+ {
+ 0x32, /* Rev 3.2 */
+ 533000, /* SDRAM frequency */
+ {
+ 0x0000001f, /* EMC_RC */
+ 0x00000045, /* EMC_RFC */
+ 0x00000016, /* EMC_RAS */
+ 0x00000009, /* EMC_RP */
+ 0x00000008, /* EMC_R2W */
+ 0x00000009, /* EMC_W2R */
+ 0x00000003, /* EMC_R2P */
+ 0x0000000d, /* EMC_W2P */
+ 0x00000009, /* EMC_RD_RCD */
+ 0x00000009, /* EMC_WR_RCD */
+ 0x00000005, /* EMC_RRD */
+ 0x00000003, /* EMC_REXT */
+ 0x00000000, /* EMC_WEXT */
+ 0x00000004, /* EMC_WDV */
+ 0x00000009, /* EMC_QUSE */
+ 0x00000006, /* EMC_QRST */
+ 0x0000000c, /* EMC_QSAFE */
+ 0x00000010, /* EMC_RDV */
+ 0x000007df, /* EMC_REFRESH */
+ 0x00000000, /* EMC_BURST_REFRESH_NUM */
+ 0x000001f7, /* EMC_PRE_REFRESH_REQ_CNT */
+ 0x00000003, /* EMC_PDEX2WR */
+ 0x00000003, /* EMC_PDEX2RD */
+ 0x00000009, /* EMC_PCHG2PDEN */
+ 0x00000000, /* EMC_ACT2PDEN */
+ 0x00000001, /* EMC_AR2PDEN */
+ 0x0000000f, /* EMC_RW2PDEN */
+ 0x0000004b, /* EMC_TXSR */
+ 0x0000004b, /* EMC_TXSRDLL */
+ 0x00000008, /* EMC_TCKE */
+ 0x0000001b, /* EMC_TFAW */
+ 0x0000000c, /* EMC_TRPAB */
+ 0x00000004, /* EMC_TCLKSTABLE */
+ 0x00000002, /* EMC_TCLKSTOP */
+ 0x000008aa, /* EMC_TREFBW */
+ 0x00000000, /* EMC_QUSE_EXTRA */
+ 0x00000006, /* EMC_FBIO_CFG6 */
+ 0x00000000, /* EMC_ODT_WRITE */
+ 0x00000000, /* EMC_ODT_READ */
+ 0x00006282, /* EMC_FBIO_CFG5 */
+ 0xf0120091, /* EMC_CFG_DIG_DLL */
+ 0x00008000, /* EMC_CFG_DIG_DLL_PERIOD */
+ 0x0000000a, /* EMC_DLL_XFORM_DQS0 */
+ 0x0000000a, /* EMC_DLL_XFORM_DQS1 */
+ 0x0000000a, /* EMC_DLL_XFORM_DQS2 */
+ 0x0000000a, /* EMC_DLL_XFORM_DQS3 */
+ 0x00000010, /* EMC_DLL_XFORM_DQS4 */
+ 0x00000010, /* EMC_DLL_XFORM_DQS5 */
+ 0x00000010, /* EMC_DLL_XFORM_DQS6 */
+ 0x00000010, /* EMC_DLL_XFORM_DQS7 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE0 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE1 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE2 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE3 */
+ 0x00000008, /* EMC_DLL_XFORM_QUSE4 */
+ 0x00000008, /* EMC_DLL_XFORM_QUSE5 */
+ 0x00000008, /* EMC_DLL_XFORM_QUSE6 */
+ 0x00000008, /* EMC_DLL_XFORM_QUSE7 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS0 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS1 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS2 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS3 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS4 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS5 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS6 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS7 */
+ 0x0000000c, /* EMC_DLL_XFORM_DQ0 */
+ 0x0000000c, /* EMC_DLL_XFORM_DQ1 */
+ 0x0000000c, /* EMC_DLL_XFORM_DQ2 */
+ 0x0000000c, /* EMC_DLL_XFORM_DQ3 */
+ 0x00070220, /* EMC_XM2CMDPADCTRL */
+ 0x0400003d, /* EMC_XM2DQSPADCTRL2 */
+ 0x00000000, /* EMC_XM2DQPADCTRL2 */
+ 0x77ffc004, /* EMC_XM2CLKPADCTRL */
+ 0x01f1f408, /* EMC_XM2COMPPADCTRL */
+ 0x00000000, /* EMC_XM2VTTGENPADCTRL */
+ 0x00000007, /* EMC_XM2VTTGENPADCTRL2 */
+ 0x08000068, /* EMC_XM2QUSEPADCTRL */
+ 0x08000000, /* EMC_XM2DQSPADCTRL3 */
+ 0x00000802, /* EMC_CTT_TERM_CTRL */
+ 0x00064000, /* EMC_ZCAL_INTERVAL */
+ 0x000000c0, /* EMC_ZCAL_WAIT_CNT */
+ 0x000e000e, /* EMC_MRS_WAIT_CNT */
+ 0xa0f10202, /* EMC_AUTO_CAL_CONFIG */
+ 0x00000000, /* EMC_CTT */
+ 0x00000000, /* EMC_CTT_DURATION */
+ 0x800010d9, /* EMC_DYN_SELF_REF_CONTROL */
+ 0x00000008, /* MC_EMEM_ARB_CFG */
+ 0x80000060, /* MC_EMEM_ARB_OUTSTANDING_REQ */
+ 0x00000003, /* MC_EMEM_ARB_TIMING_RCD */
+ 0x00000004, /* MC_EMEM_ARB_TIMING_RP */
+ 0x00000010, /* MC_EMEM_ARB_TIMING_RC */
+ 0x0000000a, /* MC_EMEM_ARB_TIMING_RAS */
+ 0x0000000d, /* MC_EMEM_ARB_TIMING_FAW */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_RRD */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_RAP2PRE */
+ 0x00000008, /* MC_EMEM_ARB_TIMING_WAP2PRE */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_R2R */
+ 0x00000000, /* MC_EMEM_ARB_TIMING_W2W */
+ 0x00000004, /* MC_EMEM_ARB_TIMING_R2W */
+ 0x00000005, /* MC_EMEM_ARB_TIMING_W2R */
+ 0x05040002, /* MC_EMEM_ARB_DA_TURNS */
+ 0x00110b10, /* MC_EMEM_ARB_DA_COVERS */
+ 0x71c81811, /* MC_EMEM_ARB_MISC0 */
+ 0x001f0000, /* MC_EMEM_ARB_RING1_THROTTLE */
+ 0x60000000, /* EMC_FBIO_SPARE */
+ 0xff00ff88, /* EMC_CFG_RSV */
+ },
+ 0x00000030, /* EMC_ZCAL_WAIT_CNT after clock change */
+ 0x001fffff, /* EMC_AUTO_CAL_INTERVAL */
+ 0x00000001, /* EMC_CFG.PERIODIC_QRST */
+ 0x00000000, /* Mode Register 0 */
+ 0x000100c2, /* Mode Register 1 */
+ 0x00020006, /* Mode Register 2 */
+ 0x00000000, /* EMC_CFG.DYN_SELF_REF */
+ },
+};
+
+int cardhu_emc_init(void)
+{
+ struct board_info board;
+
+ tegra_get_board_info(&board);
+
+ switch (board.board_id) {
+ case BOARD_PM269:
+ case BOARD_E1257:
+ if (MEMORY_TYPE(board.sku) == SKU_MEMORY_ELPIDA)
+ tegra_init_emc(cardhu_emc_tables_edb8132b2ma,
+ ARRAY_SIZE(cardhu_emc_tables_edb8132b2ma));
+ else
+ tegra_init_emc(cardhu_emc_tables_k4p8g304eb,
+ ARRAY_SIZE(cardhu_emc_tables_k4p8g304eb));
+ break;
+
+ case BOARD_PM305:
+ case BOARD_PM311:
+ break;
+ default:
+ if (tegra_get_revision() == TEGRA_REVISION_A01)
+ tegra_init_emc(cardhu_emc_tables_h5tc2g,
+ ARRAY_SIZE(cardhu_emc_tables_h5tc2g));
+ else if (MEMORY_TYPE(board.sku) == SKU_MEMORY_CARDHU_1GB_1R)
+ tegra_init_emc(cardhu_emc_tables_h5tc2g_a2,
+ ARRAY_SIZE(cardhu_emc_tables_h5tc2g_a2));
+ break;
+ }
+
+ return 0;
+}
diff --git a/arch/arm/mach-tegra/board-cardhu-panel.c b/arch/arm/mach-tegra/board-cardhu-panel.c
new file mode 100644
index 000000000000..bb8e5c5de479
--- /dev/null
+++ b/arch/arm/mach-tegra/board-cardhu-panel.c
@@ -0,0 +1,1190 @@
+/*
+ * arch/arm/mach-tegra/board-cardhu-panel.c
+ *
+ * Copyright (c) 2010-2011, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/resource.h>
+#include <asm/mach-types.h>
+#include <linux/platform_device.h>
+#include <linux/earlysuspend.h>
+#include <linux/pwm_backlight.h>
+#include <asm/atomic.h>
+#include <linux/nvhost.h>
+#include <mach/nvmap.h>
+#include <mach/irqs.h>
+#include <mach/iomap.h>
+#include <mach/dc.h>
+#include <mach/fb.h>
+
+#include "board.h"
+#include "board-cardhu.h"
+#include "devices.h"
+#include "gpio-names.h"
+
+/* Select DSI panel to be used. */
+#define DSI_PANEL_219 0
+#define DSI_PANEL_218 1
+#define AVDD_LCD PMU_TCA6416_GPIO_PORT17
+#define DSI_PANEL_RESET 0
+
+/* Select LVDS panel resolution. 13X7 is default */
+#define PM313_LVDS_PANEL_19X12 1
+#define PM313_LVDS_PANEL_BPP 1 /* 0:24bpp, 1:18bpp */
+
+/* PM313 display board specific pins */
+#define pm313_R_FDE TEGRA_GPIO_PW0
+#define pm313_R_FB TEGRA_GPIO_PN4
+#define pm313_MODE0 TEGRA_GPIO_PZ4
+#define pm313_MODE1 TEGRA_GPIO_PW1
+#define pm313_BPP TEGRA_GPIO_PN6 /* 0:24bpp, 1:18bpp */
+#define pm313_lvds_shutdown TEGRA_GPIO_PH1
+
+/* E1247 reworked for pm269 pins */
+#define e1247_pm269_lvds_shutdown TEGRA_GPIO_PN6
+
+/* E1247 cardhu default display board pins */
+#define cardhu_lvds_shutdown TEGRA_GPIO_PL2
+
+/* common pins( backlight ) for all display boards */
+#define cardhu_bl_enb TEGRA_GPIO_PH2
+#define cardhu_bl_pwm TEGRA_GPIO_PH0
+#define cardhu_hdmi_hpd TEGRA_GPIO_PN7
+
+#if defined(DSI_PANEL_219) || defined(DSI_PANEL_218)
+#define cardhu_dsia_bl_enb TEGRA_GPIO_PW1
+#define cardhu_dsib_bl_enb TEGRA_GPIO_PW0
+#define cardhu_dsi_panel_reset TEGRA_GPIO_PD2
+#endif
+
+#ifdef CONFIG_TEGRA_DC
+static struct regulator *cardhu_hdmi_reg = NULL;
+static struct regulator *cardhu_hdmi_pll = NULL;
+static struct regulator *cardhu_hdmi_vddio = NULL;
+#endif
+
+static atomic_t sd_brightness = ATOMIC_INIT(255);
+
+#ifdef CONFIG_TEGRA_CARDHU_DSI
+static struct regulator *cardhu_dsi_reg = NULL;
+#else
+static struct regulator *cardhu_lvds_reg = NULL;
+static struct regulator *cardhu_lvds_vdd_bl = NULL;
+static struct regulator *cardhu_lvds_vdd_panel = NULL;
+#endif
+
+static struct board_info board_info;
+static struct board_info display_board_info;
+
+static tegra_dc_bl_output cardhu_bl_output_measured = {
+ 0, 1, 2, 3, 4, 5, 6, 7,
+ 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 49, 50, 51, 52, 53, 54,
+ 55, 56, 57, 58, 59, 60, 61, 62,
+ 63, 64, 65, 66, 67, 68, 69, 70,
+ 70, 72, 73, 74, 75, 76, 77, 78,
+ 79, 80, 81, 82, 83, 84, 85, 86,
+ 87, 88, 89, 90, 91, 92, 93, 94,
+ 95, 96, 97, 98, 99, 100, 101, 102,
+ 103, 104, 105, 106, 107, 108, 110, 111,
+ 112, 113, 114, 115, 116, 117, 118, 119,
+ 120, 121, 122, 123, 124, 124, 125, 126,
+ 127, 128, 129, 130, 131, 132, 133, 133,
+ 134, 135, 136, 137, 138, 139, 140, 141,
+ 142, 143, 144, 145, 146, 147, 148, 148,
+ 149, 150, 151, 152, 153, 154, 155, 156,
+ 157, 158, 159, 160, 161, 162, 163, 164,
+ 165, 166, 167, 168, 169, 170, 171, 172,
+ 173, 174, 175, 176, 177, 179, 180, 181,
+ 182, 184, 185, 186, 187, 188, 189, 190,
+ 191, 192, 193, 194, 195, 196, 197, 198,
+ 199, 200, 201, 202, 203, 204, 205, 206,
+ 207, 208, 209, 211, 212, 213, 214, 215,
+ 216, 217, 218, 219, 220, 221, 222, 223,
+ 224, 225, 226, 227, 228, 229, 230, 231,
+ 232, 233, 234, 235, 236, 237, 238, 239,
+ 240, 241, 242, 243, 244, 245, 246, 247,
+ 248, 249, 250, 251, 252, 253, 254, 255
+};
+
+static p_tegra_dc_bl_output bl_output;
+
+static int cardhu_backlight_init(struct device *dev) {
+ int ret;
+
+ bl_output = cardhu_bl_output_measured;
+
+ if (WARN_ON(ARRAY_SIZE(cardhu_bl_output_measured) != 256))
+ pr_err("bl_output array does not have 256 elements\n");
+
+#ifndef CONFIG_TEGRA_CARDHU_DSI
+ tegra_gpio_disable(cardhu_bl_pwm);
+
+ ret = gpio_request(cardhu_bl_enb, "backlight_enb");
+ if (ret < 0)
+ return ret;
+
+ ret = gpio_direction_output(cardhu_bl_enb, 1);
+ if (ret < 0)
+ gpio_free(cardhu_bl_enb);
+ else
+ tegra_gpio_enable(cardhu_bl_enb);
+
+ return ret;
+#endif
+
+#if DSI_PANEL_219 || DSI_PANEL_218
+ /* Enable back light for DSIa panel */
+ printk("cardhu_dsi_backlight_init\n");
+ ret = gpio_request(cardhu_dsia_bl_enb, "dsia_bl_enable");
+ if (ret < 0)
+ return ret;
+
+ ret = gpio_direction_output(cardhu_dsia_bl_enb, 1);
+ if (ret < 0)
+ gpio_free(cardhu_dsia_bl_enb);
+ else
+ tegra_gpio_enable(cardhu_dsia_bl_enb);
+
+ /* Enable back light for DSIb panel */
+ ret = gpio_request(cardhu_dsib_bl_enb, "dsib_bl_enable");
+ if (ret < 0)
+ return ret;
+
+ ret = gpio_direction_output(cardhu_dsib_bl_enb, 1);
+ if (ret < 0)
+ gpio_free(cardhu_dsib_bl_enb);
+ else
+ tegra_gpio_enable(cardhu_dsib_bl_enb);
+#endif
+
+ return ret;
+};
+
+static void cardhu_backlight_exit(struct device *dev) {
+#ifndef CONFIG_TEGRA_CARDHU_DSI
+ /* int ret; */
+ /*ret = gpio_request(cardhu_bl_enb, "backlight_enb");*/
+ gpio_set_value(cardhu_bl_enb, 0);
+ gpio_free(cardhu_bl_enb);
+ tegra_gpio_disable(cardhu_bl_enb);
+ return;
+#endif
+#if DSI_PANEL_219 || DSI_PANEL_218
+ /* Disable back light for DSIa panel */
+ gpio_set_value(cardhu_dsia_bl_enb, 0);
+ gpio_free(cardhu_dsia_bl_enb);
+ tegra_gpio_disable(cardhu_dsia_bl_enb);
+
+ /* Disable back light for DSIb panel */
+ gpio_set_value(cardhu_dsib_bl_enb, 0);
+ gpio_free(cardhu_dsib_bl_enb);
+ tegra_gpio_disable(cardhu_dsib_bl_enb);
+
+ gpio_set_value(cardhu_lvds_shutdown, 1);
+ mdelay(20);
+#endif
+}
+
+static int cardhu_backlight_notify(struct device *unused, int brightness)
+{
+ int cur_sd_brightness = atomic_read(&sd_brightness);
+
+#ifndef CONFIG_TEGRA_CARDHU_DSI
+ /* Set the backlight GPIO pin mode to 'backlight_enable' */
+ gpio_set_value(cardhu_bl_enb, !!brightness);
+#elif DSI_PANEL_219 || DSI_PANEL_218
+ /* DSIa */
+ gpio_set_value(cardhu_dsia_bl_enb, !!brightness);
+
+ /* DSIb */
+ gpio_set_value(cardhu_dsib_bl_enb, !!brightness);
+#endif
+
+ /* SD brightness is a percentage, 8-bit value. */
+ brightness = (brightness * cur_sd_brightness) / 255;
+
+ /* Apply any backlight response curve */
+ if (brightness > 255) {
+ pr_info("Error: Brightness > 255!\n");
+ } else {
+ /* This value depends on the panel.
+ Current 19X12 panel with PM313 gets
+ full brightness when the output is 0. */
+ if (display_board_info.board_id == BOARD_DISPLAY_PM313)
+ brightness = 255 - bl_output[brightness];
+ else
+ brightness = bl_output[brightness];
+ }
+
+ return brightness;
+}
+
+static int cardhu_disp1_check_fb(struct device *dev, struct fb_info *info);
+
+static struct platform_pwm_backlight_data cardhu_backlight_data = {
+ .pwm_id = 0,
+ .max_brightness = 255,
+ .dft_brightness = 224,
+ .pwm_period_ns = 1000000,
+ .init = cardhu_backlight_init,
+ .exit = cardhu_backlight_exit,
+ .notify = cardhu_backlight_notify,
+ /* Only toggle backlight on fb blank notifications for disp1 */
+ .check_fb = cardhu_disp1_check_fb,
+};
+
+static struct platform_device cardhu_backlight_device = {
+ .name = "pwm-backlight",
+ .id = -1,
+ .dev = {
+ .platform_data = &cardhu_backlight_data,
+ },
+};
+
+#ifndef CONFIG_TEGRA_CARDHU_DSI
+static int cardhu_panel_enable(void)
+{
+ if (cardhu_lvds_reg == NULL) {
+ cardhu_lvds_reg = regulator_get(NULL, "vdd_lvds");
+ if (WARN_ON(IS_ERR(cardhu_lvds_reg)))
+ pr_err("%s: couldn't get regulator vdd_lvds: %ld\n",
+ __func__, PTR_ERR(cardhu_lvds_reg));
+ else
+ regulator_enable(cardhu_lvds_reg);
+ }
+
+ if (cardhu_lvds_vdd_bl == NULL) {
+ cardhu_lvds_vdd_bl = regulator_get(NULL, "vdd_backlight");
+ if (WARN_ON(IS_ERR(cardhu_lvds_vdd_bl)))
+ pr_err("%s: couldn't get regulator vdd_backlight: %ld\n",
+ __func__, PTR_ERR(cardhu_lvds_vdd_bl));
+ else
+ regulator_enable(cardhu_lvds_vdd_bl);
+ }
+
+ if (cardhu_lvds_vdd_panel == NULL) {
+ cardhu_lvds_vdd_panel = regulator_get(NULL, "vdd_lcd_panel");
+ if (WARN_ON(IS_ERR(cardhu_lvds_vdd_panel)))
+ pr_err("%s: couldn't get regulator vdd_lcd_panel: %ld\n",
+ __func__, PTR_ERR(cardhu_lvds_vdd_panel));
+ else
+ regulator_enable(cardhu_lvds_vdd_panel);
+ }
+
+ if (display_board_info.board_id == BOARD_DISPLAY_PM313) {
+ /* lvds configuration */
+ gpio_set_value(pm313_R_FDE, 1);
+ gpio_set_value(pm313_R_FB, 1);
+ gpio_set_value(pm313_MODE0, 1);
+ gpio_set_value(pm313_MODE1, 0);
+ gpio_set_value(pm313_BPP, PM313_LVDS_PANEL_BPP);
+
+ /* FIXME : it may require more or less delay for latching
+ values correctly before enabling RGB2LVDS */
+ mdelay(100);
+ gpio_set_value(pm313_lvds_shutdown, 1);
+ } else if ((display_board_info.board_id == BOARD_DISPLAY_E1247 &&
+ board_info.board_id == BOARD_PM269) ||
+ (board_info.board_id == BOARD_E1257) ||
+ (board_info.board_id == BOARD_PM305) ||
+ (board_info.board_id == BOARD_PM311))
+ gpio_set_value(e1247_pm269_lvds_shutdown, 1);
+ else
+ gpio_set_value(cardhu_lvds_shutdown, 1);
+
+ return 0;
+}
+
+static int cardhu_panel_disable(void)
+{
+ regulator_disable(cardhu_lvds_reg);
+ regulator_put(cardhu_lvds_reg);
+ cardhu_lvds_reg = NULL;
+
+ regulator_disable(cardhu_lvds_vdd_bl);
+ regulator_put(cardhu_lvds_vdd_bl);
+ cardhu_lvds_vdd_bl = NULL;
+
+ regulator_disable(cardhu_lvds_vdd_panel);
+ regulator_put(cardhu_lvds_vdd_panel);
+ cardhu_lvds_vdd_panel= NULL;
+
+ if (display_board_info.board_id == BOARD_DISPLAY_PM313) {
+ gpio_set_value(pm313_lvds_shutdown, 0);
+ } else if ((display_board_info.board_id == BOARD_DISPLAY_E1247 &&
+ board_info.board_id == BOARD_PM269) ||
+ (board_info.board_id == BOARD_E1257) ||
+ (board_info.board_id == BOARD_PM305) ||
+ (board_info.board_id == BOARD_PM311)) {
+ gpio_set_value(e1247_pm269_lvds_shutdown, 0);
+ } else {
+ gpio_set_value(cardhu_lvds_shutdown, 0);
+ }
+ return 0;
+}
+#endif
+
+#ifdef CONFIG_TEGRA_DC
+static int cardhu_hdmi_vddio_enable(void)
+{
+ int ret;
+ if (!cardhu_hdmi_vddio) {
+ cardhu_hdmi_vddio = regulator_get(NULL, "vdd_hdmi_con");
+ if (IS_ERR_OR_NULL(cardhu_hdmi_vddio)) {
+ ret = PTR_ERR(cardhu_hdmi_vddio);
+ pr_err("hdmi: couldn't get regulator vdd_hdmi_con\n");
+ cardhu_hdmi_vddio = NULL;
+ return ret;
+ }
+ }
+ ret = regulator_enable(cardhu_hdmi_vddio);
+ if (ret < 0) {
+ pr_err("hdmi: couldn't enable regulator vdd_hdmi_con\n");
+ regulator_put(cardhu_hdmi_vddio);
+ cardhu_hdmi_vddio = NULL;
+ return ret;
+ }
+ return ret;
+}
+
+static int cardhu_hdmi_vddio_disable(void)
+{
+ if (cardhu_hdmi_vddio) {
+ regulator_disable(cardhu_hdmi_vddio);
+ regulator_put(cardhu_hdmi_vddio);
+ cardhu_hdmi_vddio = NULL;
+ }
+ return 0;
+}
+
+static int cardhu_hdmi_enable(void)
+{
+ int ret;
+ if (!cardhu_hdmi_reg) {
+ cardhu_hdmi_reg = regulator_get(NULL, "avdd_hdmi");
+ if (IS_ERR_OR_NULL(cardhu_hdmi_reg)) {
+ pr_err("hdmi: couldn't get regulator avdd_hdmi\n");
+ cardhu_hdmi_reg = NULL;
+ return PTR_ERR(cardhu_hdmi_reg);
+ }
+ }
+ ret = regulator_enable(cardhu_hdmi_reg);
+ if (ret < 0) {
+ pr_err("hdmi: couldn't enable regulator avdd_hdmi\n");
+ return ret;
+ }
+ if (!cardhu_hdmi_pll) {
+ cardhu_hdmi_pll = regulator_get(NULL, "avdd_hdmi_pll");
+ if (IS_ERR_OR_NULL(cardhu_hdmi_pll)) {
+ pr_err("hdmi: couldn't get regulator avdd_hdmi_pll\n");
+ cardhu_hdmi_pll = NULL;
+ regulator_put(cardhu_hdmi_reg);
+ cardhu_hdmi_reg = NULL;
+ return PTR_ERR(cardhu_hdmi_pll);
+ }
+ }
+ ret = regulator_enable(cardhu_hdmi_pll);
+ if (ret < 0) {
+ pr_err("hdmi: couldn't enable regulator avdd_hdmi_pll\n");
+ return ret;
+ }
+ return 0;
+}
+
+static int cardhu_hdmi_disable(void)
+{
+ regulator_disable(cardhu_hdmi_reg);
+ regulator_put(cardhu_hdmi_reg);
+ cardhu_hdmi_reg = NULL;
+
+ regulator_disable(cardhu_hdmi_pll);
+ regulator_put(cardhu_hdmi_pll);
+ cardhu_hdmi_pll = NULL;
+ return 0;
+}
+
+static struct resource cardhu_disp1_resources[] = {
+ {
+ .name = "irq",
+ .start = INT_DISPLAY_GENERAL,
+ .end = INT_DISPLAY_GENERAL,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "regs",
+ .start = TEGRA_DISPLAY_BASE,
+ .end = TEGRA_DISPLAY_BASE + TEGRA_DISPLAY_SIZE-1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .name = "fbmem",
+ .start = 0, /* Filled in by cardhu_panel_init() */
+ .end = 0, /* Filled in by cardhu_panel_init() */
+ .flags = IORESOURCE_MEM,
+ },
+#ifdef CONFIG_TEGRA_DSI_INSTANCE_1
+ {
+ .name = "dsi_regs",
+ .start = TEGRA_DSIB_BASE,
+ .end = TEGRA_DSIB_BASE + TEGRA_DSIB_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+#else
+ {
+ .name = "dsi_regs",
+ .start = TEGRA_DSI_BASE,
+ .end = TEGRA_DSI_BASE + TEGRA_DSI_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+#endif
+};
+
+static struct resource cardhu_disp2_resources[] = {
+ {
+ .name = "irq",
+ .start = INT_DISPLAY_B_GENERAL,
+ .end = INT_DISPLAY_B_GENERAL,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "regs",
+ .start = TEGRA_DISPLAY2_BASE,
+ .end = TEGRA_DISPLAY2_BASE + TEGRA_DISPLAY2_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .name = "fbmem",
+ .flags = IORESOURCE_MEM,
+ .start = 0,
+ .end = 0,
+ },
+ {
+ .name = "hdmi_regs",
+ .start = TEGRA_HDMI_BASE,
+ .end = TEGRA_HDMI_BASE + TEGRA_HDMI_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+#endif
+
+#ifndef CONFIG_TEGRA_CARDHU_DSI
+static struct tegra_dc_mode panel_19X12_modes[] = {
+ {
+ .pclk = 154000000,
+ .h_ref_to_sync = 11,
+ .v_ref_to_sync = 1,
+ .h_sync_width = 32,
+ .v_sync_width = 6,
+ .h_back_porch = 80,
+ .v_back_porch = 26,
+ .h_active = 1920,
+ .v_active = 1200,
+ .h_front_porch = 48,
+ .v_front_porch = 3,
+ },
+};
+
+static struct tegra_dc_mode cardhu_panel_modes[] = {
+ {
+ /* 1366x768@60Hz */
+ .pclk = 74180000,
+ .h_ref_to_sync = 1,
+ .v_ref_to_sync = 1,
+ .h_sync_width = 30,
+ .v_sync_width = 5,
+ .h_back_porch = 52,
+ .v_back_porch = 20,
+ .h_active = 1366,
+ .v_active = 768,
+ .h_front_porch = 64,
+ .v_front_porch = 25,
+ },
+};
+
+static struct tegra_dc_mode cardhu_panel_modes_55hz[] = {
+ {
+ /* 1366x768p 55Hz */
+ .pclk = 68000000,
+ .h_ref_to_sync = 0,
+ .v_ref_to_sync = 12,
+ .h_sync_width = 30,
+ .v_sync_width = 5,
+ .h_back_porch = 52,
+ .v_back_porch = 20,
+ .h_active = 1366,
+ .v_active = 768,
+ .h_front_porch = 64,
+ .v_front_porch = 25,
+ },
+};
+#endif
+
+static struct tegra_dc_sd_settings cardhu_sd_settings = {
+ .enable = 1, /* enabled by default. */
+ .use_auto_pwm = false,
+ .hw_update_delay = 0,
+ .bin_width = -1,
+ .aggressiveness = 1,
+ .phase_in_adjustments = true,
+ .use_vid_luma = false,
+ /* Default video coefficients */
+ .coeff = {5, 9, 2},
+ .fc = {0, 0},
+ /* Immediate backlight changes */
+ .blp = {1024, 255},
+ /* Gammas: R: 2.2 G: 2.2 B: 2.2 */
+ /* Default BL TF */
+ .bltf = {
+ {
+ {57, 65, 74, 83},
+ {93, 103, 114, 126},
+ {138, 151, 165, 179},
+ {194, 209, 225, 242},
+ },
+ {
+ {58, 66, 75, 84},
+ {94, 105, 116, 127},
+ {140, 153, 166, 181},
+ {196, 211, 227, 244},
+ },
+ {
+ {60, 68, 77, 87},
+ {97, 107, 119, 130},
+ {143, 156, 170, 184},
+ {199, 215, 231, 248},
+ },
+ {
+ {64, 73, 82, 91},
+ {102, 113, 124, 137},
+ {149, 163, 177, 192},
+ {207, 223, 240, 255},
+ },
+ },
+ /* Default LUT */
+ .lut = {
+ {
+ {250, 250, 250},
+ {194, 194, 194},
+ {149, 149, 149},
+ {113, 113, 113},
+ {82, 82, 82},
+ {56, 56, 56},
+ {34, 34, 34},
+ {15, 15, 15},
+ {0, 0, 0},
+ },
+ {
+ {246, 246, 246},
+ {191, 191, 191},
+ {147, 147, 147},
+ {111, 111, 111},
+ {80, 80, 80},
+ {55, 55, 55},
+ {33, 33, 33},
+ {14, 14, 14},
+ {0, 0, 0},
+ },
+ {
+ {239, 239, 239},
+ {185, 185, 185},
+ {142, 142, 142},
+ {107, 107, 107},
+ {77, 77, 77},
+ {52, 52, 52},
+ {30, 30, 30},
+ {12, 12, 12},
+ {0, 0, 0},
+ },
+ {
+ {224, 224, 224},
+ {173, 173, 173},
+ {133, 133, 133},
+ {99, 99, 99},
+ {70, 70, 70},
+ {46, 46, 46},
+ {25, 25, 25},
+ {7, 7, 7},
+ {0, 0, 0},
+ },
+ },
+ .sd_brightness = &sd_brightness,
+ .bl_device = &cardhu_backlight_device,
+};
+
+#ifdef CONFIG_TEGRA_DC
+#ifndef CONFIG_TEGRA_CARDHU_DSI
+static struct tegra_fb_data cardhu_fb_data = {
+ .win = 0,
+ .xres = 1366,
+ .yres = 768,
+ .bits_per_pixel = 32,
+ .flags = TEGRA_FB_FLIP_ON_PROBE,
+};
+#endif
+
+static struct tegra_fb_data cardhu_hdmi_fb_data = {
+ .win = 0,
+ .xres = 1366,
+ .yres = 768,
+ .bits_per_pixel = 32,
+ .flags = TEGRA_FB_FLIP_ON_PROBE,
+};
+
+static struct tegra_dc_out cardhu_disp2_out = {
+ .type = TEGRA_DC_OUT_HDMI,
+ .flags = TEGRA_DC_OUT_HOTPLUG_HIGH,
+
+ .dcc_bus = 3,
+ .hotplug_gpio = cardhu_hdmi_hpd,
+
+ .max_pixclock = KHZ2PICOS(148500),
+
+ .align = TEGRA_DC_ALIGN_MSB,
+ .order = TEGRA_DC_ORDER_RED_BLUE,
+
+ .enable = cardhu_hdmi_enable,
+ .disable = cardhu_hdmi_disable,
+
+ .postsuspend = cardhu_hdmi_vddio_disable,
+ .hotplug_init = cardhu_hdmi_vddio_enable,
+};
+
+static struct tegra_dc_platform_data cardhu_disp2_pdata = {
+ .flags = 0,
+ .default_out = &cardhu_disp2_out,
+ .fb = &cardhu_hdmi_fb_data,
+ .emc_clk_rate = 300000000,
+};
+#endif
+
+#ifdef CONFIG_TEGRA_CARDHU_DSI
+static int cardhu_dsi_panel_enable(void)
+{
+ int ret;
+
+ if (cardhu_dsi_reg == NULL) {
+ cardhu_dsi_reg = regulator_get(NULL, "avdd_dsi_csi");
+ if (IS_ERR_OR_NULL(cardhu_dsi_reg)) {
+ pr_err("dsi: Could not get regulator avdd_dsi_csi\n");
+ cardhu_dsi_reg = NULL;
+ return PTR_ERR(cardhu_dsi_reg);
+ }
+ }
+ regulator_enable(cardhu_dsi_reg);
+
+ ret = gpio_request(TEGRA_GPIO_PJ1, "DSI TE");
+ if (ret < 0)
+ return ret;
+
+ ret = gpio_direction_input(TEGRA_GPIO_PJ1);
+ if (ret < 0) {
+ gpio_free(TEGRA_GPIO_PJ1);
+ return ret;
+ }
+ tegra_gpio_enable(TEGRA_GPIO_PJ1);
+
+#if DSI_PANEL_219
+
+ ret = gpio_request(TEGRA_GPIO_PH0, "ph0");
+ if (ret < 0)
+ return ret;
+ ret = gpio_direction_output(TEGRA_GPIO_PH0, 0);
+ if (ret < 0) {
+ gpio_free(TEGRA_GPIO_PH0);
+ return ret;
+ }
+ else
+ tegra_gpio_enable(TEGRA_GPIO_PH0);
+
+ ret = gpio_request(TEGRA_GPIO_PH2, "ph2");
+ if (ret < 0)
+ return ret;
+ ret = gpio_direction_output(TEGRA_GPIO_PH2, 0);
+ if (ret < 0) {
+ gpio_free(TEGRA_GPIO_PH2);
+ return ret;
+ }
+ else
+ tegra_gpio_enable(TEGRA_GPIO_PH2);
+
+ ret = gpio_request(TEGRA_GPIO_PU2, "pu2");
+ if (ret < 0)
+ return ret;
+ ret = gpio_direction_output(TEGRA_GPIO_PU2, 0);
+ if (ret < 0) {
+ gpio_free(TEGRA_GPIO_PU2);
+ return ret;
+ }
+ else
+ tegra_gpio_enable(TEGRA_GPIO_PU2);
+
+ gpio_set_value(cardhu_lvds_shutdown, 1);
+ mdelay(20);
+ gpio_set_value(TEGRA_GPIO_PH0, 1);
+ mdelay(10);
+ gpio_set_value(TEGRA_GPIO_PH2, 1);
+ mdelay(15);
+ gpio_set_value(TEGRA_GPIO_PU2, 0);
+ gpio_set_value(TEGRA_GPIO_PU2, 1);
+ mdelay(10);
+ gpio_set_value(TEGRA_GPIO_PU2, 0);
+ mdelay(10);
+ gpio_set_value(TEGRA_GPIO_PU2, 1);
+ mdelay(15);
+#endif
+
+#if DSI_PANEL_218
+ printk("DSI_PANEL_218 is enabled\n");
+ ret = gpio_request(AVDD_LCD, "avdd_lcd");
+ if(ret < 0)
+ gpio_free(AVDD_LCD);
+ ret = gpio_direction_output(AVDD_LCD, 1);
+ if(ret < 0)
+ gpio_free(AVDD_LCD);
+ else
+ tegra_gpio_enable(AVDD_LCD);
+
+#if DSI_PANEL_RESET
+ ret = gpio_request(TEGRA_GPIO_PD2, "pd2");
+ if (ret < 0){
+ return ret;
+ }
+ ret = gpio_direction_output(TEGRA_GPIO_PD2, 0);
+ if (ret < 0) {
+ gpio_free(TEGRA_GPIO_PD2);
+ return ret;
+ }
+ else
+ tegra_gpio_enable(TEGRA_GPIO_PD2);
+
+ gpio_set_value(TEGRA_GPIO_PD2, 1);
+ gpio_set_value(TEGRA_GPIO_PD2, 0);
+ mdelay(2);
+ gpio_set_value(TEGRA_GPIO_PD2, 1);
+ mdelay(2);
+#endif
+#endif
+
+ return 0;
+}
+
+static int cardhu_dsi_panel_disable(void)
+{
+ int err;
+
+ err = 0;
+ printk(KERN_INFO "DSI panel disable\n");
+
+#if DSI_PANEL_219
+ tegra_gpio_disable(TEGRA_GPIO_PU2);
+ gpio_free(TEGRA_GPIO_PU2);
+ tegra_gpio_disable(TEGRA_GPIO_PH2);
+ gpio_free(TEGRA_GPIO_PH2);
+ tegra_gpio_disable(TEGRA_GPIO_PH0);
+ gpio_free(TEGRA_GPIO_PH0);
+ tegra_gpio_disable(TEGRA_GPIO_PL2);
+ gpio_free(TEGRA_GPIO_PL2);
+#endif
+
+#if DSI_PANEL_218
+ tegra_gpio_disable(TEGRA_GPIO_PD2);
+ gpio_free(TEGRA_GPIO_PD2);
+#endif
+
+ return err;
+}
+
+static int cardhu_dsi_panel_postsuspend(void)
+{
+ int err;
+
+ err = 0;
+ printk(KERN_INFO "DSI panel postsuspend\n");
+
+ if (cardhu_dsi_reg) {
+ err = regulator_disable(cardhu_dsi_reg);
+ if (err < 0)
+ printk(KERN_ERR
+ "DSI regulator avdd_dsi_csi disable failed\n");
+ regulator_put(cardhu_dsi_reg);
+ cardhu_dsi_reg = NULL;
+ }
+
+#if DSI_PANEL_218
+ tegra_gpio_disable(AVDD_LCD);
+ gpio_free(AVDD_LCD);
+#endif
+
+ return err;
+}
+
+static struct tegra_dsi_cmd dsi_init_cmd[]= {
+ DSI_CMD_SHORT(0x05, 0x11, 0x00),
+ DSI_DLY_MS(150),
+ DSI_CMD_SHORT(0x05, 0x29, 0x00),
+ DSI_DLY_MS(20),
+};
+
+static struct tegra_dsi_cmd dsi_suspend_cmd[] = {
+ DSI_CMD_SHORT(0x05, 0x28, 0x00),
+ DSI_DLY_MS(20),
+ DSI_CMD_SHORT(0x05, 0x10, 0x00),
+ DSI_DLY_MS(5),
+};
+
+struct tegra_dsi_out cardhu_dsi = {
+ .n_data_lanes = 2,
+ .pixel_format = TEGRA_DSI_PIXEL_FORMAT_24BIT_P,
+ .refresh_rate = 60,
+ .virtual_channel = TEGRA_DSI_VIRTUAL_CHANNEL_0,
+
+ .panel_has_frame_buffer = true,
+#ifdef CONFIG_TEGRA_DSI_INSTANCE_1
+ .dsi_instance = 1,
+#else
+ .dsi_instance = 0,
+#endif
+ .panel_reset = DSI_PANEL_RESET,
+
+ .n_init_cmd = ARRAY_SIZE(dsi_init_cmd),
+ .dsi_init_cmd = dsi_init_cmd,
+
+ .n_suspend_cmd = ARRAY_SIZE(dsi_suspend_cmd),
+ .dsi_suspend_cmd = dsi_suspend_cmd,
+
+ .video_data_type = TEGRA_DSI_VIDEO_TYPE_COMMAND_MODE,
+ .lp_cmd_mode_freq_khz = 430000,
+};
+
+static struct tegra_dc_mode cardhu_dsi_modes[] = {
+#if DSI_PANEL_219
+ {
+ .pclk = 10000000,
+ .h_ref_to_sync = 4,
+ .v_ref_to_sync = 1,
+ .h_sync_width = 16,
+ .v_sync_width = 1,
+ .h_back_porch = 32,
+ .v_back_porch = 1,
+ .h_active = 540,
+ .v_active = 960,
+ .h_front_porch = 32,
+ .v_front_porch = 2,
+ },
+#endif
+
+#if DSI_PANEL_218
+ {
+ .pclk = 323000000,
+ .h_ref_to_sync = 11,
+ .v_ref_to_sync = 1,
+ .h_sync_width = 16,
+ .v_sync_width = 4,
+ .h_back_porch = 16,
+ .v_back_porch = 4,
+ .h_active = 864,
+ .v_active = 480,
+ .h_front_porch = 16,
+ .v_front_porch = 4,
+ },
+#endif
+
+};
+
+
+static struct tegra_fb_data cardhu_dsi_fb_data = {
+#if DSI_PANEL_219
+ .win = 0,
+ .xres = 540,
+ .yres = 960,
+ .bits_per_pixel = 32,
+#endif
+
+#if DSI_PANEL_218
+ .win = 0,
+ .xres = 864,
+ .yres = 480,
+ .bits_per_pixel = 32,
+#endif
+ .flags = TEGRA_FB_FLIP_ON_PROBE,
+};
+#endif
+
+static struct tegra_dc_out cardhu_disp1_out = {
+ .align = TEGRA_DC_ALIGN_MSB,
+ .order = TEGRA_DC_ORDER_RED_BLUE,
+ .sd_settings = &cardhu_sd_settings,
+ .parent_clk = "pll_p",
+
+#ifndef CONFIG_TEGRA_CARDHU_DSI
+ .type = TEGRA_DC_OUT_RGB,
+ .depth = 18,
+ .dither = TEGRA_DC_ORDERED_DITHER,
+
+ .modes = cardhu_panel_modes,
+ .n_modes = ARRAY_SIZE(cardhu_panel_modes),
+
+ .enable = cardhu_panel_enable,
+ .disable = cardhu_panel_disable,
+#else
+ .type = TEGRA_DC_OUT_DSI,
+
+ .modes = cardhu_dsi_modes,
+ .n_modes = ARRAY_SIZE(cardhu_dsi_modes),
+
+ .dsi = &cardhu_dsi,
+
+ .enable = cardhu_dsi_panel_enable,
+ .disable = cardhu_dsi_panel_disable,
+ .postsuspend = cardhu_dsi_panel_postsuspend,
+#endif
+};
+
+#ifdef CONFIG_TEGRA_DC
+static struct tegra_dc_platform_data cardhu_disp1_pdata = {
+ .flags = TEGRA_DC_FLAG_ENABLED,
+ .default_out = &cardhu_disp1_out,
+ .emc_clk_rate = 300000000,
+#ifndef CONFIG_TEGRA_CARDHU_DSI
+ .fb = &cardhu_fb_data,
+#else
+ .fb = &cardhu_dsi_fb_data,
+#endif
+};
+
+static struct nvhost_device cardhu_disp1_device = {
+ .name = "tegradc",
+ .id = 0,
+ .resource = cardhu_disp1_resources,
+ .num_resources = ARRAY_SIZE(cardhu_disp1_resources),
+ .dev = {
+ .platform_data = &cardhu_disp1_pdata,
+ },
+};
+
+static int cardhu_disp1_check_fb(struct device *dev, struct fb_info *info)
+{
+ return info->device == &cardhu_disp1_device.dev;
+}
+
+static struct nvhost_device cardhu_disp2_device = {
+ .name = "tegradc",
+ .id = 1,
+ .resource = cardhu_disp2_resources,
+ .num_resources = ARRAY_SIZE(cardhu_disp2_resources),
+ .dev = {
+ .platform_data = &cardhu_disp2_pdata,
+ },
+};
+#else
+static int cardhu_disp1_check_fb(struct device *dev, struct fb_info *info)
+{
+ return 0;
+}
+#endif
+
+static struct nvmap_platform_carveout cardhu_carveouts[] = {
+ [0] = NVMAP_HEAP_CARVEOUT_IRAM_INIT,
+ [1] = {
+ .name = "generic-0",
+ .usage_mask = NVMAP_HEAP_CARVEOUT_GENERIC,
+ .base = 0, /* Filled in by cardhu_panel_init() */
+ .size = 0, /* Filled in by cardhu_panel_init() */
+ .buddy_size = SZ_32K,
+ },
+};
+
+static struct nvmap_platform_data cardhu_nvmap_data = {
+ .carveouts = cardhu_carveouts,
+ .nr_carveouts = ARRAY_SIZE(cardhu_carveouts),
+};
+
+static struct platform_device cardhu_nvmap_device = {
+ .name = "tegra-nvmap",
+ .id = -1,
+ .dev = {
+ .platform_data = &cardhu_nvmap_data,
+ },
+};
+
+
+static struct platform_device *cardhu_gfx_devices[] __initdata = {
+ &cardhu_nvmap_device,
+#ifdef CONFIG_TEGRA_GRHOST
+ &tegra_grhost_device,
+#endif
+ &tegra_pwfm0_device,
+ &cardhu_backlight_device,
+};
+
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+/* put early_suspend/late_resume handlers here for the display in order
+ * to keep the code out of the display driver, keeping it closer to upstream
+ */
+struct early_suspend cardhu_panel_early_suspender;
+
+static void cardhu_panel_early_suspend(struct early_suspend *h)
+{
+ unsigned i;
+
+ /* power down LCD, add use a black screen for HDMI */
+ if (num_registered_fb > 0)
+ fb_blank(registered_fb[0], FB_BLANK_POWERDOWN);
+ if (num_registered_fb > 1)
+ fb_blank(registered_fb[1], FB_BLANK_NORMAL);
+}
+
+static void cardhu_panel_late_resume(struct early_suspend *h)
+{
+ unsigned i;
+ for (i = 0; i < num_registered_fb; i++)
+ fb_blank(registered_fb[i], FB_BLANK_UNBLANK);
+}
+#endif
+
+int __init cardhu_panel_init(void)
+{
+ int err;
+ struct resource __maybe_unused *res;
+
+ tegra_get_board_info(&board_info);
+ tegra_get_display_board_info(&display_board_info);
+
+ cardhu_carveouts[1].base = tegra_carveout_start;
+ cardhu_carveouts[1].size = tegra_carveout_size;
+
+ if (board_info.board_id == BOARD_E1291 &&
+ ((board_info.sku & SKU_TOUCHSCREEN_MECH_FIX) == 0)) {
+ /* use 55Hz panel timings to reduce noise on sensitive touch */
+ printk("Using cardhu_panel_modes_55hz\n");
+ cardhu_disp1_out.modes = cardhu_panel_modes_55hz;
+ cardhu_disp1_out.n_modes = ARRAY_SIZE(cardhu_panel_modes_55hz);
+ }
+
+ if (display_board_info.board_id == BOARD_DISPLAY_PM313) {
+ /* initialize the values */
+#if defined(PM313_LVDS_PANEL_19X12)
+ cardhu_disp1_out.modes = panel_19X12_modes;
+ cardhu_disp1_out.n_modes = ARRAY_SIZE(panel_19X12_modes);
+ cardhu_disp1_out.parent_clk = "pll_d_out0";
+#if (PM313_LVDS_PANEL_BPP == 1)
+ cardhu_disp1_out.depth = 18;
+#else
+ cardhu_disp1_out.depth = 24;
+#endif
+ cardhu_fb_data.xres = 1920;
+ cardhu_fb_data.yres = 1200;
+
+ cardhu_disp2_out.parent_clk = "pll_d2_out0";
+ cardhu_hdmi_fb_data.xres = 1920;
+ cardhu_hdmi_fb_data.yres = 1200;
+#endif
+
+ /* lvds configuration */
+ err = gpio_request(pm313_R_FDE, "R_FDE");
+ err |= gpio_direction_output(pm313_R_FDE, 1);
+ tegra_gpio_enable(pm313_R_FDE);
+
+ err |= gpio_request(pm313_R_FB, "R_FB");
+ err |= gpio_direction_output(pm313_R_FB, 1);
+ tegra_gpio_enable(pm313_R_FB);
+
+ err |= gpio_request(pm313_MODE0, "MODE0");
+ err |= gpio_direction_output(pm313_MODE0, 1);
+ tegra_gpio_enable(pm313_MODE0);
+
+ err |= gpio_request(pm313_MODE1, "MODE1");
+ err |= gpio_direction_output(pm313_MODE1, 0);
+ tegra_gpio_enable(pm313_MODE1);
+
+ err |= gpio_request(pm313_BPP, "BPP");
+ err |= gpio_direction_output(pm313_BPP, PM313_LVDS_PANEL_BPP);
+ tegra_gpio_enable(pm313_BPP);
+
+ err = gpio_request(pm313_lvds_shutdown, "lvds_shutdown");
+ /* free ride provided by bootloader */
+ err |= gpio_direction_output(pm313_lvds_shutdown, 1);
+ tegra_gpio_enable(pm313_lvds_shutdown);
+
+ if (err)
+ printk(KERN_ERR "ERROR(s) in LVDS configuration\n");
+ } else if ((display_board_info.board_id == BOARD_DISPLAY_E1247 &&
+ board_info.board_id == BOARD_PM269) ||
+ (board_info.board_id == BOARD_E1257) ||
+ (board_info.board_id == BOARD_PM305) ||
+ (board_info.board_id == BOARD_PM311)) {
+ gpio_request(e1247_pm269_lvds_shutdown, "lvds_shutdown");
+ gpio_direction_output(e1247_pm269_lvds_shutdown, 1);
+ tegra_gpio_enable(e1247_pm269_lvds_shutdown);
+ } else {
+ gpio_request(cardhu_lvds_shutdown, "lvds_shutdown");
+ gpio_direction_output(cardhu_lvds_shutdown, 1);
+ tegra_gpio_enable(cardhu_lvds_shutdown);
+ }
+
+ tegra_gpio_enable(cardhu_hdmi_hpd);
+ gpio_request(cardhu_hdmi_hpd, "hdmi_hpd");
+ gpio_direction_input(cardhu_hdmi_hpd);
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ cardhu_panel_early_suspender.suspend = cardhu_panel_early_suspend;
+ cardhu_panel_early_suspender.resume = cardhu_panel_late_resume;
+ cardhu_panel_early_suspender.level = EARLY_SUSPEND_LEVEL_DISABLE_FB;
+ register_early_suspend(&cardhu_panel_early_suspender);
+#endif
+
+ err = platform_add_devices(cardhu_gfx_devices,
+ ARRAY_SIZE(cardhu_gfx_devices));
+
+#if defined(CONFIG_TEGRA_GRHOST) && defined(CONFIG_TEGRA_DC)
+ res = nvhost_get_resource_byname(&cardhu_disp1_device,
+ IORESOURCE_MEM, "fbmem");
+ res->start = tegra_fb_start;
+ res->end = tegra_fb_start + tegra_fb_size - 1;
+#endif
+
+ /* Copy the bootloader fb to the fb. */
+ tegra_move_framebuffer(tegra_fb_start, tegra_bootloader_fb_start,
+ min(tegra_fb_size, tegra_bootloader_fb_size));
+
+#if defined(CONFIG_TEGRA_GRHOST) && defined(CONFIG_TEGRA_DC)
+ if (!err)
+ err = nvhost_device_register(&cardhu_disp1_device);
+
+ res = nvhost_get_resource_byname(&cardhu_disp2_device,
+ IORESOURCE_MEM, "fbmem");
+ res->start = tegra_fb2_start;
+ res->end = tegra_fb2_start + tegra_fb2_size - 1;
+ if (!err)
+ err = nvhost_device_register(&cardhu_disp2_device);
+#endif
+
+#if defined(CONFIG_TEGRA_GRHOST) && defined(CONFIG_TEGRA_NVAVP)
+ if (!err)
+ err = nvhost_device_register(&nvavp_device);
+#endif
+ return err;
+}
diff --git a/arch/arm/mach-tegra/board-cardhu-pinmux.c b/arch/arm/mach-tegra/board-cardhu-pinmux.c
new file mode 100644
index 000000000000..1b736a40dccf
--- /dev/null
+++ b/arch/arm/mach-tegra/board-cardhu-pinmux.c
@@ -0,0 +1,781 @@
+/*
+ * arch/arm/mach-tegra/board-cardhu-pinmux.c
+ *
+ * Copyright (C) 2010-2011 NVIDIA Corporation
+ *
+ * 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/init.h>
+#include <mach/pinmux.h>
+#include "board.h"
+#include "board-cardhu.h"
+#include "gpio-names.h"
+
+#define DEFAULT_DRIVE(_name) \
+ { \
+ .pingroup = TEGRA_DRIVE_PINGROUP_##_name, \
+ .hsm = TEGRA_HSM_DISABLE, \
+ .schmitt = TEGRA_SCHMITT_ENABLE, \
+ .drive = TEGRA_DRIVE_DIV_1, \
+ .pull_down = TEGRA_PULL_31, \
+ .pull_up = TEGRA_PULL_31, \
+ .slew_rising = TEGRA_SLEW_SLOWEST, \
+ .slew_falling = TEGRA_SLEW_SLOWEST, \
+ }
+/* Setting the drive strength of pins
+ * hsm: Enable High speed mode (ENABLE/DISABLE)
+ * Schimit: Enable/disable schimit (ENABLE/DISABLE)
+ * drive: low power mode (DIV_1, DIV_2, DIV_4, DIV_8)
+ * pulldn_drive - drive down (falling edge) - Driver Output Pull-Down drive
+ * strength code. Value from 0 to 31.
+ * pullup_drive - drive up (rising edge) - Driver Output Pull-Up drive
+ * strength code. Value from 0 to 31.
+ * pulldn_slew - Driver Output Pull-Up slew control code - 2bit code
+ * code 11 is least slewing of signal. code 00 is highest
+ * slewing of the signal.
+ * Value - FASTEST, FAST, SLOW, SLOWEST
+ * pullup_slew - Driver Output Pull-Down slew control code -
+ * code 11 is least slewing of signal. code 00 is highest
+ * slewing of the signal.
+ * Value - FASTEST, FAST, SLOW, SLOWEST
+ */
+#define SET_DRIVE(_name, _hsm, _schmitt, _drive, _pulldn_drive, _pullup_drive, _pulldn_slew, _pullup_slew) \
+ { \
+ .pingroup = TEGRA_DRIVE_PINGROUP_##_name, \
+ .hsm = TEGRA_HSM_##_hsm, \
+ .schmitt = TEGRA_SCHMITT_##_schmitt, \
+ .drive = TEGRA_DRIVE_##_drive, \
+ .pull_down = TEGRA_PULL_##_pulldn_drive, \
+ .pull_up = TEGRA_PULL_##_pullup_drive, \
+ .slew_rising = TEGRA_SLEW_##_pulldn_slew, \
+ .slew_falling = TEGRA_SLEW_##_pullup_slew, \
+ }
+
+/* !!!FIXME!!!! POPULATE THIS TABLE */
+static __initdata struct tegra_drive_pingroup_config cardhu_drive_pinmux[] = {
+ /* DEFAULT_DRIVE(<pin_group>), */
+ /* SET_DRIVE(ATA, DISABLE, DISABLE, DIV_1, 31, 31, FAST, FAST) */
+ SET_DRIVE(DAP2, DISABLE, ENABLE, DIV_1, 31, 31, FASTEST, FASTEST),
+
+ /* All I2C pins are driven to maximum drive strength */
+ /* GEN1 I2C */
+ SET_DRIVE(DBG, DISABLE, ENABLE, DIV_1, 31, 31, FASTEST, FASTEST),
+
+ /* GEN2 I2C */
+ SET_DRIVE(AT5, DISABLE, ENABLE, DIV_1, 31, 31, FASTEST, FASTEST),
+
+ /* CAM I2C */
+ SET_DRIVE(GME, DISABLE, ENABLE, DIV_1, 31, 31, FASTEST, FASTEST),
+
+ /* DDC I2C */
+ SET_DRIVE(DDC, DISABLE, ENABLE, DIV_1, 31, 31, FASTEST, FASTEST),
+
+ /* PWR_I2C */
+ SET_DRIVE(AO1, DISABLE, ENABLE, DIV_1, 31, 31, FASTEST, FASTEST),
+
+ /* UART3 */
+ SET_DRIVE(UART3, DISABLE, ENABLE, DIV_1, 31, 31, FASTEST, FASTEST),
+
+ /* SDMMC1 */
+ SET_DRIVE(SDIO1, DISABLE, DISABLE, DIV_1, 46, 42, FAST, FAST),
+
+ /* SDMMC3 */
+ SET_DRIVE(SDIO3, DISABLE, DISABLE, DIV_1, 46, 42, FAST, FAST),
+
+ /* SDMMC4 */
+ SET_DRIVE(GMA, DISABLE, DISABLE, DIV_1, 9, 9, SLOWEST, SLOWEST),
+ SET_DRIVE(GMB, DISABLE, DISABLE, DIV_1, 9, 9, SLOWEST, SLOWEST),
+ SET_DRIVE(GMC, DISABLE, DISABLE, DIV_1, 9, 9, SLOWEST, SLOWEST),
+ SET_DRIVE(GMD, DISABLE, DISABLE, DIV_1, 9, 9, SLOWEST, SLOWEST),
+
+};
+
+#define DEFAULT_PINMUX(_pingroup, _mux, _pupd, _tri, _io) \
+ { \
+ .pingroup = TEGRA_PINGROUP_##_pingroup, \
+ .func = TEGRA_MUX_##_mux, \
+ .pupd = TEGRA_PUPD_##_pupd, \
+ .tristate = TEGRA_TRI_##_tri, \
+ .io = TEGRA_PIN_##_io, \
+ .lock = TEGRA_PIN_LOCK_DEFAULT, \
+ .od = TEGRA_PIN_OD_DEFAULT, \
+ .ioreset = TEGRA_PIN_IO_RESET_DEFAULT, \
+ }
+
+#define I2C_PINMUX(_pingroup, _mux, _pupd, _tri, _io, _lock, _od) \
+ { \
+ .pingroup = TEGRA_PINGROUP_##_pingroup, \
+ .func = TEGRA_MUX_##_mux, \
+ .pupd = TEGRA_PUPD_##_pupd, \
+ .tristate = TEGRA_TRI_##_tri, \
+ .io = TEGRA_PIN_##_io, \
+ .lock = TEGRA_PIN_LOCK_##_lock, \
+ .od = TEGRA_PIN_OD_##_od, \
+ .ioreset = TEGRA_PIN_IO_RESET_DEFAULT, \
+ }
+
+#define VI_PINMUX(_pingroup, _mux, _pupd, _tri, _io, _lock, _ioreset) \
+ { \
+ .pingroup = TEGRA_PINGROUP_##_pingroup, \
+ .func = TEGRA_MUX_##_mux, \
+ .pupd = TEGRA_PUPD_##_pupd, \
+ .tristate = TEGRA_TRI_##_tri, \
+ .io = TEGRA_PIN_##_io, \
+ .lock = TEGRA_PIN_LOCK_##_lock, \
+ .od = TEGRA_PIN_OD_DEFAULT, \
+ .ioreset = TEGRA_PIN_IO_RESET_##_ioreset \
+ }
+
+static __initdata struct tegra_pingroup_config cardhu_pinmux_common[] = {
+ /* SDMMC1 pinmux */
+ DEFAULT_PINMUX(SDMMC1_CLK, SDMMC1, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC1_CMD, SDMMC1, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC1_DAT3, SDMMC1, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC1_DAT2, SDMMC1, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC1_DAT1, SDMMC1, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC1_DAT0, SDMMC1, PULL_UP, NORMAL, INPUT),
+
+ /* SDMMC3 pinmux */
+ DEFAULT_PINMUX(SDMMC3_CLK, SDMMC3, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC3_CMD, SDMMC3, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC3_DAT0, SDMMC3, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC3_DAT1, SDMMC3, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC3_DAT2, SDMMC3, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC3_DAT3, SDMMC3, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC3_DAT6, SDMMC3, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC3_DAT7, SDMMC3, PULL_UP, NORMAL, INPUT),
+
+ /* SDMMC4 pinmux */
+ DEFAULT_PINMUX(SDMMC4_CLK, SDMMC4, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC4_CMD, SDMMC4, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC4_DAT0, SDMMC4, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC4_DAT1, SDMMC4, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC4_DAT2, SDMMC4, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC4_DAT3, SDMMC4, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC4_DAT4, SDMMC4, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC4_DAT5, SDMMC4, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC4_DAT6, SDMMC4, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC4_DAT7, SDMMC4, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC4_RST_N, RSVD1, PULL_DOWN, NORMAL, INPUT),
+
+ /* I2C1 pinmux */
+ I2C_PINMUX(GEN1_I2C_SCL, I2C1, NORMAL, NORMAL, INPUT, DISABLE, ENABLE),
+ I2C_PINMUX(GEN1_I2C_SDA, I2C1, NORMAL, NORMAL, INPUT, DISABLE, ENABLE),
+
+ /* I2C2 pinmux */
+ I2C_PINMUX(GEN2_I2C_SCL, I2C2, NORMAL, NORMAL, INPUT, DISABLE, ENABLE),
+ I2C_PINMUX(GEN2_I2C_SDA, I2C2, NORMAL, NORMAL, INPUT, DISABLE, ENABLE),
+
+ /* I2C3 pinmux */
+ I2C_PINMUX(CAM_I2C_SCL, I2C3, NORMAL, NORMAL, INPUT, DISABLE, ENABLE),
+ I2C_PINMUX(CAM_I2C_SDA, I2C3, NORMAL, NORMAL, INPUT, DISABLE, ENABLE),
+
+ /* I2C4 pinmux */
+ I2C_PINMUX(DDC_SCL, I2C4, NORMAL, NORMAL, INPUT, DISABLE, ENABLE),
+ I2C_PINMUX(DDC_SDA, I2C4, NORMAL, NORMAL, INPUT, DISABLE, ENABLE),
+
+ /* Power I2C pinmux */
+ I2C_PINMUX(PWR_I2C_SCL, I2CPWR, NORMAL, NORMAL, INPUT, DISABLE, ENABLE),
+ I2C_PINMUX(PWR_I2C_SDA, I2CPWR, NORMAL, NORMAL, INPUT, DISABLE, ENABLE),
+
+ DEFAULT_PINMUX(ULPI_DATA0, UARTA, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(ULPI_DATA1, UARTA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(ULPI_DATA2, UARTA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(ULPI_DATA3, UARTA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(ULPI_DATA4, UARTA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(ULPI_DATA5, UARTA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(ULPI_DATA6, UARTA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(ULPI_DATA7, UARTA, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(ULPI_CLK, UARTD, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(ULPI_DIR, UARTD, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(ULPI_NXT, UARTD, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(ULPI_STP, UARTD, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(DAP3_FS, I2S2, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(DAP3_DIN, I2S2, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(DAP3_DOUT, I2S2, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(DAP3_SCLK, I2S2, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GPIO_PV2, OWR, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(GPIO_PV3, RSVD1, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(CLK2_OUT, EXTPERIPH2, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(CLK2_REQ, DAP, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_PWR1, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_PWR2, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_SDIN, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_SDOUT, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_WR_N, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_DC0, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_PWR0, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_PCLK, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_DE, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_HSYNC, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_VSYNC, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D0, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D1, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D2, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D3, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D4, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D5, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D6, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D7, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D8, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D9, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D10, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D11, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D12, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D13, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D14, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D15, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D16, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D17, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D18, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D19, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D20, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D21, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D22, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D23, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_DC1, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(CRT_HSYNC, CRT, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(CRT_VSYNC, CRT, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(VI_D0, RSVD1, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(VI_D1, SDMMC2, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(VI_D2, SDMMC2, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(VI_D3, SDMMC2, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(VI_D4, VI, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(VI_D5, SDMMC2, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(VI_D7, SDMMC2, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(VI_D10, RSVD1, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(VI_MCLK, VI, PULL_UP, NORMAL, INPUT),
+
+ DEFAULT_PINMUX(UART2_RXD, IRDA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(UART2_TXD, IRDA, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(UART2_RTS_N, UARTB, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(UART2_CTS_N, UARTB, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(UART3_TXD, UARTC, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(UART3_RXD, UARTC, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(UART3_CTS_N, UARTC, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(UART3_RTS_N, UARTC, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(GPIO_PU0, RSVD1, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GPIO_PU1, RSVD1, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(GPIO_PU2, RSVD1, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GPIO_PU3, RSVD1, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GPIO_PU4, PWM1, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(GPIO_PU5, PWM2, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GPIO_PU6, RSVD1, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(DAP4_FS, I2S3, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(DAP4_DIN, I2S3, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(DAP4_DOUT, I2S3, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(DAP4_SCLK, I2S3, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(CLK3_OUT, EXTPERIPH3, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(CLK3_REQ, DEV3, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GMI_WP_N, GMI, NORMAL, NORMAL, INPUT),
+
+ DEFAULT_PINMUX(KB_ROW5, OWR, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(KB_ROW12, KBC, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(KB_ROW14, KBC, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(KB_ROW15, KBC, NORMAL, NORMAL, OUTPUT),
+
+#if 0 /* for testing on Verbier */
+ DEFAULT_PINMUX(GMI_WAIT, NAND, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GMI_ADV_N, NAND, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(GMI_CLK, NAND, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(GMI_CS0_N, NAND, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(GMI_CS1_N, NAND, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(GMI_CS3_N, NAND, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(GMI_CS4_N, NAND, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(GMI_CS6_N, NAND_ALT, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(GMI_CS7_N, NAND_ALT, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(GMI_AD0, NAND, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GMI_AD1, NAND, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GMI_AD2, NAND, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GMI_AD3, NAND, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GMI_AD4, NAND, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GMI_AD5, NAND, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GMI_AD6, NAND, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GMI_AD7, NAND, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GMI_AD8, NAND, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(GMI_AD9, NAND, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GMI_AD10, NAND, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(GMI_AD11, NAND, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GMI_AD12, NAND, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GMI_AD13, NAND, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GMI_AD14, NAND, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GMI_AD15, NAND, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GMI_WR_N, NAND, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(GMI_OE_N, NAND, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(GMI_DQS, NAND, NORMAL, NORMAL, INPUT),
+#else
+ DEFAULT_PINMUX(GMI_AD8, PWM0, NORMAL, NORMAL, OUTPUT), /* LCD1_BL_PWM */
+#endif
+ DEFAULT_PINMUX(GMI_A16, SPI4, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GMI_A17, SPI4, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GMI_A18, SPI4, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GMI_A19, SPI4, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(CAM_MCLK, VI_ALT2, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(GPIO_PCC1, RSVD1, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GPIO_PBB0, RSVD1, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GPIO_PBB3, VGP3, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GPIO_PBB5, VGP5, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GPIO_PBB6, VGP6, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GPIO_PBB7, I2S4, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GPIO_PCC2, I2S4, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(JTAG_RTCK, RTCK, NORMAL, NORMAL, OUTPUT),
+
+ /* KBC keys */
+ DEFAULT_PINMUX(KB_ROW0, KBC, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(KB_ROW1, KBC, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(KB_ROW2, KBC, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(KB_ROW3, KBC, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(KB_COL0, KBC, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(KB_COL1, KBC, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(KB_COL2, KBC, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(KB_COL3, KBC, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(KB_COL4, KBC, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(KB_COL5, KBC, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(GPIO_PV0, RSVD, PULL_UP, NORMAL, INPUT),
+
+ DEFAULT_PINMUX(CLK_32K_OUT, BLINK, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(SYS_CLK_REQ, SYSCLK, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(OWR, OWR, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(DAP1_FS, I2S0, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(DAP1_DIN, I2S0, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(DAP1_DOUT, I2S0, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(DAP1_SCLK, I2S0, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(CLK1_REQ, DAP, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(CLK1_OUT, EXTPERIPH1, NORMAL, NORMAL, INPUT),
+#if 0 /* For HDA realtek Codec */
+ DEFAULT_PINMUX(SPDIF_IN, DAP2, PULL_DOWN, NORMAL, INPUT),
+#else
+ DEFAULT_PINMUX(SPDIF_IN, SPDIF, NORMAL, NORMAL, INPUT),
+#endif
+ DEFAULT_PINMUX(SPDIF_OUT, SPDIF, NORMAL, NORMAL, OUTPUT),
+#if 0 /* For HDA realtek Codec */
+ DEFAULT_PINMUX(DAP2_FS, HDA, PULL_DOWN, NORMAL, INPUT),
+ DEFAULT_PINMUX(DAP2_DIN, HDA, PULL_DOWN, NORMAL, INPUT),
+ DEFAULT_PINMUX(DAP2_DOUT, HDA, PULL_DOWN, NORMAL, INPUT),
+ DEFAULT_PINMUX(DAP2_SCLK, HDA, PULL_DOWN, NORMAL, INPUT),
+#else
+ DEFAULT_PINMUX(DAP2_FS, I2S1, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(DAP2_DIN, I2S1, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(DAP2_DOUT, I2S1, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(DAP2_SCLK, I2S1, NORMAL, NORMAL, INPUT),
+#endif
+ DEFAULT_PINMUX(SPI2_CS1_N, SPI2, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(SPI1_MOSI, SPI1, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(SPI1_SCK, SPI1, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(SPI1_CS0_N, SPI1, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(SPI1_MISO, SPI1, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(PEX_L0_PRSNT_N, PCIE, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(PEX_L0_RST_N, PCIE, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(PEX_L0_CLKREQ_N, PCIE, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(PEX_WAKE_N, PCIE, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(PEX_L1_PRSNT_N, PCIE, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(PEX_L1_RST_N, PCIE, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(PEX_L1_CLKREQ_N, PCIE, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(PEX_L2_PRSNT_N, PCIE, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(PEX_L2_RST_N, PCIE, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(PEX_L2_CLKREQ_N, PCIE, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(HDMI_CEC, CEC, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(HDMI_INT, RSVD0, NORMAL, TRISTATE, INPUT),
+
+ /* Gpios */
+ /* SDMMC1 CD gpio */
+ DEFAULT_PINMUX(GMI_IORDY, RSVD1, PULL_UP, NORMAL, INPUT),
+ /* SDMMC1 WP gpio */
+ DEFAULT_PINMUX(VI_D11, RSVD1, PULL_UP, NORMAL, INPUT),
+ /* Touch panel GPIO */
+ /* Touch IRQ */
+ DEFAULT_PINMUX(GMI_AD12, NAND, PULL_UP, NORMAL, INPUT),
+
+ /* Touch RESET */
+ DEFAULT_PINMUX(GMI_AD14, NAND, NORMAL, NORMAL, OUTPUT),
+
+
+ /* Power rails GPIO */
+ DEFAULT_PINMUX(SPI2_SCK, GMI, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GPIO_PBB4, VGP4, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(KB_ROW8, KBC, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC3_DAT5, SDMMC3, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC3_DAT4, SDMMC3, PULL_UP, NORMAL, INPUT),
+
+ VI_PINMUX(VI_D6, VI, NORMAL, NORMAL, OUTPUT, DISABLE, DISABLE),
+ VI_PINMUX(VI_D8, SDMMC2, NORMAL, NORMAL, INPUT, DISABLE, DISABLE),
+ VI_PINMUX(VI_D9, SDMMC2, NORMAL, NORMAL, INPUT, DISABLE, DISABLE),
+ VI_PINMUX(VI_PCLK, RSVD1, PULL_UP, TRISTATE, INPUT, DISABLE, DISABLE),
+ VI_PINMUX(VI_HSYNC, RSVD1, NORMAL, NORMAL, INPUT, DISABLE, DISABLE),
+ VI_PINMUX(VI_VSYNC, RSVD1, NORMAL, NORMAL, INPUT, DISABLE, DISABLE),
+};
+
+static __initdata struct tegra_pingroup_config cardhu_pinmux_e118x[] = {
+ /* Power rails GPIO */
+ DEFAULT_PINMUX(SPI2_SCK, SPI2, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GMI_RST_N, RSVD3, PULL_UP, TRISTATE, INPUT),
+ DEFAULT_PINMUX(GMI_AD15, NAND, PULL_UP, TRISTATE, INPUT),
+};
+
+static __initdata struct tegra_pingroup_config cardhu_pinmux_cardhu[] = {
+ DEFAULT_PINMUX(LCD_CS0_N, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_SCK, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_CS1_N, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_M1, DISPLAYA, NORMAL, NORMAL, INPUT),
+
+ DEFAULT_PINMUX(GMI_CS2_N, RSVD1, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(GMI_AD8, PWM0, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(GMI_AD10, NAND, NORMAL, NORMAL, OUTPUT),
+
+ /* Power rails GPIO */
+ DEFAULT_PINMUX(GMI_CS2_N, NAND, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(GMI_RST_N, RSVD3, PULL_UP, TRISTATE, INPUT),
+ DEFAULT_PINMUX(GMI_AD15, NAND, PULL_UP, TRISTATE, INPUT),
+
+ DEFAULT_PINMUX(GMI_CS0_N, GMI, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(GMI_CS1_N, GMI, PULL_UP, TRISTATE, INPUT),
+ /*TP_IRQ*/
+ DEFAULT_PINMUX(GMI_CS4_N, GMI, PULL_UP, NORMAL, INPUT),
+};
+
+static __initdata struct tegra_pingroup_config cardhu_pinmux_cardhu_a03[] = {
+ DEFAULT_PINMUX(LCD_CS0_N, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_SCK, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_CS1_N, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_M1, DISPLAYA, NORMAL, NORMAL, INPUT),
+
+ DEFAULT_PINMUX(GMI_CS2_N, RSVD1, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(GMI_AD8, PWM0, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(GMI_AD10, NAND, NORMAL, NORMAL, OUTPUT),
+
+ /* Power rails GPIO */
+ DEFAULT_PINMUX(PEX_L0_PRSNT_N, PCIE, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(PEX_L0_CLKREQ_N, PCIE, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(PEX_L1_CLKREQ_N, RSVD3, PULL_UP, TRISTATE, INPUT),
+ DEFAULT_PINMUX(PEX_L1_PRSNT_N, RSVD3, PULL_UP, TRISTATE, INPUT),
+};
+
+static __initdata struct tegra_pingroup_config cardhu_pinmux_e1291_a04[] = {
+ DEFAULT_PINMUX(GMI_AD15, NAND, PULL_DOWN, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(ULPI_DATA5, UARTA, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(ULPI_DATA6, UARTA, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(SPI2_MOSI, SPI6, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(DAP3_SCLK, RSVD1, NORMAL, NORMAL, OUTPUT),
+};
+
+static __initdata struct tegra_pingroup_config cardhu_pinmux_e1198[] = {
+ DEFAULT_PINMUX(LCD_CS0_N, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_SCK, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_CS1_N, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_M1, DISPLAYA, NORMAL, NORMAL, INPUT),
+
+ DEFAULT_PINMUX(GMI_CS2_N, RSVD1, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(GMI_AD8, PWM0, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(GMI_AD10, NAND, NORMAL, NORMAL, OUTPUT),
+
+ /* SPI2 */
+ DEFAULT_PINMUX(SPI2_SCK, SPI2, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(SPI2_MOSI, SPI2, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(SPI2_MISO, SPI2, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(SPI2_CS0_N, SPI2, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(SPI2_CS2_N, SPI2, PULL_UP, NORMAL, INPUT),
+};
+
+static __initdata struct tegra_pingroup_config unused_pins_lowpower[] = {
+ DEFAULT_PINMUX(GMI_WAIT, NAND, PULL_UP, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(GMI_ADV_N, NAND, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(GMI_CLK, NAND, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(GMI_CS3_N, NAND, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(GMI_CS6_N, SATA, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(GMI_CS7_N, NAND, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(GMI_AD0, NAND, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(GMI_AD1, NAND, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(GMI_AD2, NAND, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(GMI_AD3, NAND, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(GMI_AD4, NAND, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(GMI_AD5, NAND, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(GMI_AD6, NAND, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(GMI_AD7, NAND, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(GMI_AD9, PWM1, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(GMI_AD11, NAND, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(GMI_AD13, NAND, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(GMI_WR_N, NAND, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(GMI_OE_N, NAND, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(GMI_DQS, NAND, NORMAL, TRISTATE, OUTPUT),
+};
+
+static __initdata struct tegra_pingroup_config gmi_pins_269[] = {
+ /* Continuation of table unused_pins_lowpower only for PM269 */
+ DEFAULT_PINMUX(GMI_CS0_N, NAND, PULL_UP, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(GMI_CS1_N, NAND, PULL_UP, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(GMI_CS2_N, RSVD1, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GMI_CS3_N, NAND, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(GMI_CS4_N, NAND, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(GMI_CS6_N, SATA, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(GMI_CS7_N, NAND, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(GMI_AD8, PWM0, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(GMI_AD9, PWM1, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(GMI_AD10, NAND, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(GMI_AD11, NAND, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(GMI_AD13, NAND, PULL_UP, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(GMI_AD15, NAND, PULL_UP, TRISTATE, INPUT),
+ DEFAULT_PINMUX(GMI_A16, SPI4, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GMI_A17, SPI4, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GMI_A18, SPI4, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(GMI_A19, SPI4, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GMI_RST_N, NAND, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(GMI_WP_N, NAND, NORMAL, NORMAL, INPUT),
+};
+
+static void __init cardhu_pinmux_audio_init(void)
+{
+ tegra_gpio_enable(TEGRA_GPIO_CDC_IRQ);
+ gpio_request(TEGRA_GPIO_CDC_IRQ, "wm8903");
+ gpio_direction_input(TEGRA_GPIO_CDC_IRQ);
+
+ tegra_gpio_enable(TEGRA_GPIO_HP_DET);
+}
+
+#define GPIO_INIT_PIN_MODE(_gpio, _is_input, _value) \
+ { \
+ .gpio_nr = _gpio, \
+ .is_input = _is_input, \
+ .value = _value, \
+ }
+
+
+/* E1198-A01/E1291 specific fab < A03 */
+static struct gpio_init_pin_info init_gpio_mode_e1291_a02[] = {
+ GPIO_INIT_PIN_MODE(TEGRA_GPIO_PH7, false, 0),
+ GPIO_INIT_PIN_MODE(TEGRA_GPIO_PI4, false, 0),
+};
+
+/* E1198-A02/E1291 specific fab = A03 */
+static struct gpio_init_pin_info init_gpio_mode_e1291_a03[] = {
+ GPIO_INIT_PIN_MODE(TEGRA_GPIO_PDD6, false, 0),
+ GPIO_INIT_PIN_MODE(TEGRA_GPIO_PDD4, false, 0),
+};
+
+/* E1198-A02/E1291 specific fab >= A04 */
+static struct gpio_init_pin_info init_gpio_mode_e1291_a04[] = {
+ GPIO_INIT_PIN_MODE(TEGRA_GPIO_PDD6, false, 0),
+ GPIO_INIT_PIN_MODE(TEGRA_GPIO_PDD4, false, 0),
+ GPIO_INIT_PIN_MODE(TEGRA_GPIO_PR2, false, 0),
+};
+
+static void __init cardhu_gpio_init_configure(void)
+{
+ struct board_info board_info;
+ int len;
+ int i;
+ struct gpio_init_pin_info *pins_info;
+
+ tegra_get_board_info(&board_info);
+
+ switch (board_info.board_id) {
+ case BOARD_E1198:
+ if (board_info.fab < BOARD_FAB_A02) {
+ len = ARRAY_SIZE(init_gpio_mode_e1291_a02);
+ pins_info = init_gpio_mode_e1291_a02;
+ } else {
+ len = ARRAY_SIZE(init_gpio_mode_e1291_a03);
+ pins_info = init_gpio_mode_e1291_a03;
+ }
+ break;
+ case BOARD_E1291:
+ if (board_info.fab < BOARD_FAB_A03) {
+ len = ARRAY_SIZE(init_gpio_mode_e1291_a02);
+ pins_info = init_gpio_mode_e1291_a02;
+ } else if (board_info.fab == BOARD_FAB_A03) {
+ len = ARRAY_SIZE(init_gpio_mode_e1291_a03);
+ pins_info = init_gpio_mode_e1291_a03;
+ } else {
+ len = ARRAY_SIZE(init_gpio_mode_e1291_a04);
+ pins_info = init_gpio_mode_e1291_a04;
+ }
+ break;
+ default:
+ return;
+ }
+
+ for (i = 0; i < len; ++i) {
+ tegra_gpio_init_configure(pins_info->gpio_nr,
+ pins_info->is_input, pins_info->value);
+ pins_info++;
+ }
+}
+
+int __init cardhu_pinmux_init(void)
+{
+ struct board_info board_info;
+
+ cardhu_gpio_init_configure();
+
+ tegra_pinmux_config_table(cardhu_pinmux_common, ARRAY_SIZE(cardhu_pinmux_common));
+ tegra_drive_pinmux_config_table(cardhu_drive_pinmux,
+ ARRAY_SIZE(cardhu_drive_pinmux));
+
+ tegra_get_board_info(&board_info);
+ switch (board_info.board_id) {
+ case BOARD_E1198:
+ tegra_pinmux_config_table(cardhu_pinmux_e1198,
+ ARRAY_SIZE(cardhu_pinmux_e1198));
+ tegra_pinmux_config_table(unused_pins_lowpower,
+ ARRAY_SIZE(unused_pins_lowpower));
+ if (board_info.fab >= BOARD_FAB_A02)
+ tegra_pinmux_config_table(cardhu_pinmux_cardhu_a03,
+ ARRAY_SIZE(cardhu_pinmux_cardhu_a03));
+ break;
+ case BOARD_E1291:
+ if (board_info.fab < BOARD_FAB_A03) {
+ tegra_pinmux_config_table(cardhu_pinmux_cardhu,
+ ARRAY_SIZE(cardhu_pinmux_cardhu));
+ tegra_pinmux_config_table(unused_pins_lowpower,
+ ARRAY_SIZE(unused_pins_lowpower));
+ } else {
+ tegra_pinmux_config_table(cardhu_pinmux_cardhu_a03,
+ ARRAY_SIZE(cardhu_pinmux_cardhu_a03));
+ }
+ if (board_info.fab >= BOARD_FAB_A04)
+ tegra_pinmux_config_table(cardhu_pinmux_e1291_a04,
+ ARRAY_SIZE(cardhu_pinmux_e1291_a04));
+ break;
+
+ case BOARD_PM269:
+ case BOARD_PM305:
+ case BOARD_PM311:
+ case BOARD_E1257:
+ tegra_pinmux_config_table(cardhu_pinmux_e118x,
+ ARRAY_SIZE(cardhu_pinmux_e118x));
+ tegra_pinmux_config_table(unused_pins_lowpower,
+ ARRAY_SIZE(unused_pins_lowpower));
+ tegra_pinmux_config_table(gmi_pins_269,
+ ARRAY_SIZE(gmi_pins_269));
+ break;
+ default:
+ tegra_pinmux_config_table(cardhu_pinmux_e118x,
+ ARRAY_SIZE(cardhu_pinmux_e118x));
+ break;
+ }
+
+ cardhu_pinmux_audio_init();
+
+ return 0;
+}
+
+#define PIN_GPIO_LPM(_name, _gpio, _is_input, _value) \
+ { \
+ .name = _name, \
+ .gpio_nr = _gpio, \
+ .is_gpio = true, \
+ .is_input = _is_input, \
+ .value = _value, \
+ }
+
+struct gpio_init_pin_info pin_lpm_cardhu_common[] = {
+ PIN_GPIO_LPM("GMI_CS3_N", TEGRA_GPIO_PK4, 0, 0),
+ PIN_GPIO_LPM("GMI_CS4_N", TEGRA_GPIO_PK2, 1, 0),
+ PIN_GPIO_LPM("GMI_CS7", TEGRA_GPIO_PI6, 1, 0),
+ PIN_GPIO_LPM("GMI_CS0", TEGRA_GPIO_PJ0, 1, 0),
+ PIN_GPIO_LPM("GMI_CS1", TEGRA_GPIO_PJ2, 1, 0),
+ PIN_GPIO_LPM("GMI_WP_N", TEGRA_GPIO_PC7, 1, 0),
+};
+
+/* E1198 without PM313 display board */
+struct gpio_init_pin_info pin_lpm_cardhu_common_wo_pm313[] = {
+ PIN_GPIO_LPM("GMI_AD9", TEGRA_GPIO_PH1, 0, 0),
+ PIN_GPIO_LPM("GMI_AD11", TEGRA_GPIO_PH3, 0, 0),
+};
+
+struct gpio_init_pin_info vddio_gmi_pins_pm269[] = {
+ PIN_GPIO_LPM("GMI_CS3_N", TEGRA_GPIO_PK4, 0, 0),
+ PIN_GPIO_LPM("GMI_CS4_N", TEGRA_GPIO_PK2, 1, 0),
+ PIN_GPIO_LPM("GMI_CS7", TEGRA_GPIO_PI6, 1, 0),
+ PIN_GPIO_LPM("GMI_CS0", TEGRA_GPIO_PJ0, 1, 0),
+ PIN_GPIO_LPM("GMI_CS1", TEGRA_GPIO_PJ2, 1, 0),
+ PIN_GPIO_LPM("GMI_WP_N", TEGRA_GPIO_PC7, 1, 0),
+ PIN_GPIO_LPM("GMI_A16", TEGRA_GPIO_PJ7, 0, 0),
+ PIN_GPIO_LPM("GMI_A17", TEGRA_GPIO_PB0, 0, 0),
+ PIN_GPIO_LPM("GMI_A18", TEGRA_GPIO_PB1, 1, 0),
+ PIN_GPIO_LPM("GMI_A19", TEGRA_GPIO_PK7, 0, 0),
+};
+
+/* PM269 without PM313 display board */
+struct gpio_init_pin_info vddio_gmi_pins_pm269_wo_pm313[] = {
+ PIN_GPIO_LPM("GMI_CS2", TEGRA_GPIO_PK3, 1, 0),
+ PIN_GPIO_LPM("GMI_AD9", TEGRA_GPIO_PH1, 0, 0),
+};
+
+static void set_unused_pin_gpio(struct gpio_init_pin_info *lpm_pin_info,
+ int list_count)
+{
+ int i;
+ struct gpio_init_pin_info *pin_info;
+ int ret;
+
+ for (i = 0; i < list_count; ++i) {
+ pin_info = (struct gpio_init_pin_info *)(lpm_pin_info + i);
+ if (!pin_info->is_gpio)
+ continue;
+
+ ret = gpio_request(pin_info->gpio_nr, pin_info->name);
+ if (ret < 0) {
+ pr_err("%s() Error in gpio_request() for gpio %d\n",
+ __func__, pin_info->gpio_nr);
+ continue;
+ }
+ if (pin_info->is_input)
+ ret = gpio_direction_input(pin_info->gpio_nr);
+ else
+ ret = gpio_direction_output(pin_info->gpio_nr,
+ pin_info->value);
+ if (ret < 0) {
+ pr_err("%s() Error in setting gpio %d to in/out\n",
+ __func__, pin_info->gpio_nr);
+ gpio_free(pin_info->gpio_nr);
+ continue;
+ }
+ tegra_gpio_enable(pin_info->gpio_nr);
+ }
+}
+
+/* Initialize the pins to desired state as per power/asic/system-eng
+ * recomendation */
+int __init cardhu_pins_state_init(void)
+{
+ struct board_info board_info;
+ struct board_info display_board_info;
+
+ tegra_get_board_info(&board_info);
+ tegra_get_display_board_info(&display_board_info);
+ if ((board_info.board_id == BOARD_E1291) ||
+ (board_info.board_id == BOARD_E1198)) {
+ set_unused_pin_gpio(&pin_lpm_cardhu_common[0],
+ ARRAY_SIZE(pin_lpm_cardhu_common));
+
+ if (display_board_info.board_id != BOARD_DISPLAY_PM313) {
+ set_unused_pin_gpio(&pin_lpm_cardhu_common_wo_pm313[0],
+ ARRAY_SIZE(pin_lpm_cardhu_common_wo_pm313));
+ }
+ }
+
+ if ((board_info.board_id == BOARD_PM269) ||
+ (board_info.board_id == BOARD_E1257) ||
+ (board_info.board_id == BOARD_PM305) ||
+ (board_info.board_id == BOARD_PM311)) {
+ set_unused_pin_gpio(&vddio_gmi_pins_pm269[0],
+ ARRAY_SIZE(vddio_gmi_pins_pm269));
+
+ if (display_board_info.board_id != BOARD_DISPLAY_PM313) {
+ set_unused_pin_gpio(&vddio_gmi_pins_pm269_wo_pm313[0],
+ ARRAY_SIZE(vddio_gmi_pins_pm269_wo_pm313));
+ }
+ }
+
+ return 0;
+}
diff --git a/arch/arm/mach-tegra/board-cardhu-pm298-power-rails.c b/arch/arm/mach-tegra/board-cardhu-pm298-power-rails.c
new file mode 100644
index 000000000000..9839249d197b
--- /dev/null
+++ b/arch/arm/mach-tegra/board-cardhu-pm298-power-rails.c
@@ -0,0 +1,758 @@
+/*
+ * arch/arm/mach-tegra/board-cardhu-pm298-power-rails.c
+ *
+ * Copyright (C) 2011 NVIDIA, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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/i2c.h>
+#include <linux/pda_power.h>
+#include <linux/platform_device.h>
+#include <linux/resource.h>
+#include <linux/regulator/machine.h>
+#include <linux/gpio.h>
+#include <linux/io.h>
+#include <linux/regulator/gpio-switch-regulator.h>
+#include <linux/mfd/max77663-core.h>
+#include <linux/regulator/max77663-regulator.h>
+
+#include <mach/iomap.h>
+#include <mach/irqs.h>
+#include <mach/pinmux.h>
+#include <mach/edp.h>
+
+#include "gpio-names.h"
+#include "board.h"
+#include "board-cardhu.h"
+#include "pm.h"
+#include "wakeups-t3.h"
+#include "mach/tsensor.h"
+
+#define PMC_CTRL 0x0
+#define PMC_CTRL_INTR_LOW BIT(17)
+
+static struct regulator_consumer_supply max77663_sd0_supply[] = {
+ REGULATOR_SUPPLY("vdd_cpu_pmu", NULL),
+ REGULATOR_SUPPLY("vdd_cpu", NULL),
+ REGULATOR_SUPPLY("vdd_sys", NULL),
+};
+
+static struct regulator_consumer_supply max77663_sd1_supply[] = {
+ REGULATOR_SUPPLY("vdd_core", NULL),
+ REGULATOR_SUPPLY("en_vddio_ddr_1v2", NULL),
+};
+
+static struct regulator_consumer_supply max77663_sd2_supply[] = {
+ REGULATOR_SUPPLY("avdd_hdmi_pll", NULL),
+ REGULATOR_SUPPLY("avdd_usb_pll", NULL),
+ REGULATOR_SUPPLY("avdd_osc", NULL),
+ REGULATOR_SUPPLY("vdd1v8_satelite", NULL),
+ REGULATOR_SUPPLY("vddio_uart", NULL),
+ REGULATOR_SUPPLY("pwrdet_uart", NULL),
+ REGULATOR_SUPPLY("vddio_audio", NULL),
+ REGULATOR_SUPPLY("pwrdet_audio", NULL),
+ REGULATOR_SUPPLY("vddio_bb", NULL),
+ REGULATOR_SUPPLY("pwrdet_bb", NULL),
+ REGULATOR_SUPPLY("vddio_lcd_pmu", NULL),
+ REGULATOR_SUPPLY("pwrdet_lcd", NULL),
+ REGULATOR_SUPPLY("vddio_cam", NULL),
+ REGULATOR_SUPPLY("pwrdet_cam", NULL),
+ REGULATOR_SUPPLY("vddio_vi", NULL),
+ REGULATOR_SUPPLY("pwrdet_vi", NULL),
+ REGULATOR_SUPPLY("ldo6", NULL),
+ REGULATOR_SUPPLY("ldo7", NULL),
+ REGULATOR_SUPPLY("ldo8", NULL),
+ REGULATOR_SUPPLY("vcore_audio", NULL),
+ REGULATOR_SUPPLY("avcore_audio", NULL),
+ REGULATOR_SUPPLY("vddio_sdmmc", "sdhci-tegra.2"),
+ REGULATOR_SUPPLY("pwrdet_sdmmc3", NULL),
+ REGULATOR_SUPPLY("vcore1_lpddr2", NULL),
+ REGULATOR_SUPPLY("vcom_1v8", NULL),
+ REGULATOR_SUPPLY("pmuio_1v8", NULL),
+ REGULATOR_SUPPLY("avdd_ic_usb", NULL),
+ REGULATOR_SUPPLY("vdd_gen1v8", NULL),
+};
+
+static struct regulator_consumer_supply max77663_sd3_supply[] = {
+ REGULATOR_SUPPLY("vdd_gen1v5", NULL),
+ REGULATOR_SUPPLY("vcore_lcd", NULL),
+ REGULATOR_SUPPLY("track_ldo1", NULL),
+ REGULATOR_SUPPLY("external_ldo_1v2", NULL),
+ REGULATOR_SUPPLY("vcore_cam1", NULL),
+ REGULATOR_SUPPLY("vcore_cam2", NULL),
+ REGULATOR_SUPPLY("avdd_pexb", NULL),
+ REGULATOR_SUPPLY("vdd_pexb", NULL),
+ REGULATOR_SUPPLY("avdd_pex_pll", NULL),
+ REGULATOR_SUPPLY("avdd_pexa", NULL),
+ REGULATOR_SUPPLY("vdd_pexa", NULL),
+ REGULATOR_SUPPLY("vcom_1v2", NULL),
+ REGULATOR_SUPPLY("vdio_hsic", NULL),
+};
+
+static struct regulator_consumer_supply max77663_ldo0_supply[] = {
+ REGULATOR_SUPPLY("vdd_ddr_hs", NULL),
+};
+
+static struct regulator_consumer_supply max77663_ldo1_supply[] = {
+ REGULATOR_SUPPLY("avdd_plla_p_c_s", NULL),
+ REGULATOR_SUPPLY("avdd_pllm", NULL),
+ REGULATOR_SUPPLY("avdd_pllu_d", NULL),
+ REGULATOR_SUPPLY("avdd_pllu_d2", NULL),
+ REGULATOR_SUPPLY("avdd_pllx", NULL),
+};
+
+static struct regulator_consumer_supply max77663_ldo2_supply[] = {
+ REGULATOR_SUPPLY("avdd_dsi_csi", NULL),
+ REGULATOR_SUPPLY("pwrdet_mipi", NULL),
+};
+
+static struct regulator_consumer_supply max77663_ldo3_supply[] = {
+ REGULATOR_SUPPLY("vddio_sdmmc", "sdhci-tegra.3"),
+ REGULATOR_SUPPLY("pwrdet_sdmmc4", NULL),
+};
+
+static struct regulator_consumer_supply max77663_ldo4_supply[] = {
+ REGULATOR_SUPPLY("vdd_rtc", NULL),
+};
+
+static struct regulator_consumer_supply max77663_ldo5_supply[] = {
+ REGULATOR_SUPPLY("vddio_sdmmc", "sdhci-tegra.0"),
+ REGULATOR_SUPPLY("pwrdet_sdmmc1", NULL),
+};
+
+static struct regulator_consumer_supply max77663_ldo6_supply[] = {
+ REGULATOR_SUPPLY("vddio_sys", NULL),
+};
+
+static struct regulator_consumer_supply max77663_ldo7_supply[] = {
+ REGULATOR_SUPPLY("unused_ldo7", NULL),
+};
+
+static struct regulator_consumer_supply max77663_ldo8_supply[] = {
+ REGULATOR_SUPPLY("vcore_mmc", NULL),
+};
+
+static struct max77663_regulator_fps_cfg max77663_fps_cfgs[] = {
+ {
+ .src = FPS_SRC_0,
+ .en_src = FPS_EN_SRC_EN0,
+ .time_period = FPS_TIME_PERIOD_DEF,
+ },
+ {
+ .src = FPS_SRC_1,
+ .en_src = FPS_EN_SRC_EN1,
+ .time_period = FPS_TIME_PERIOD_DEF,
+ },
+ {
+ .src = FPS_SRC_2,
+ .en_src = FPS_EN_SRC_EN0,
+ .time_period = FPS_TIME_PERIOD_DEF,
+ },
+};
+
+#define MAX77663_PDATA_INIT(_id, _min_uV, _max_uV, _supply_reg, \
+ _always_on, _boot_on, _apply_uV, \
+ _init_apply, _init_enable, _init_uV, \
+ _fps_src, _fps_pu_period, _fps_pd_period, _flags) \
+ static struct max77663_regulator_platform_data max77663_regulator_pdata_##_id = \
+ { \
+ .init_data = { \
+ .constraints = { \
+ .min_uV = _min_uV, \
+ .max_uV = _max_uV, \
+ .valid_modes_mask = (REGULATOR_MODE_NORMAL | \
+ REGULATOR_MODE_STANDBY), \
+ .valid_ops_mask = (REGULATOR_CHANGE_MODE | \
+ REGULATOR_CHANGE_STATUS | \
+ REGULATOR_CHANGE_VOLTAGE), \
+ .always_on = _always_on, \
+ .boot_on = _boot_on, \
+ .apply_uV = _apply_uV, \
+ }, \
+ .num_consumer_supplies = \
+ ARRAY_SIZE(max77663_##_id##_supply), \
+ .consumer_supplies = max77663_##_id##_supply, \
+ .supply_regulator = _supply_reg, \
+ }, \
+ .init_apply = _init_apply, \
+ .init_enable = _init_enable, \
+ .init_uV = _init_uV, \
+ .fps_src = _fps_src, \
+ .fps_pu_period = _fps_pu_period, \
+ .fps_pd_period = _fps_pd_period, \
+ .fps_cfgs = max77663_fps_cfgs, \
+ .flags = _flags, \
+ }
+
+MAX77663_PDATA_INIT(sd0, 600000, 3387500, NULL, 1, 0, 0,
+ 0, 0, -1, FPS_SRC_NONE, -1, -1, EN2_CTRL_SD0 | SD_FSRADE_DISABLE);
+
+MAX77663_PDATA_INIT(sd1, 800000, 1587500, NULL, 1, 0, 0,
+ 1, 1, -1, FPS_SRC_1, -1, -1, SD_FSRADE_DISABLE);
+
+MAX77663_PDATA_INIT(sd2, 600000, 3387500, NULL, 1, 0, 0,
+ 1, 1, -1, FPS_SRC_NONE, -1, -1, 0);
+
+MAX77663_PDATA_INIT(sd3, 600000, 3387500, NULL, 0, 0, 0,
+ 1, 1, -1, FPS_SRC_NONE, -1, -1, 0);
+
+MAX77663_PDATA_INIT(ldo0, 800000, 2350000, max77663_rails(sd2), 0, 0, 0,
+ 1, 1, -1, FPS_SRC_NONE, -1, -1, 0);
+
+MAX77663_PDATA_INIT(ldo1, 800000, 2350000, max77663_rails(sd2), 0, 0, 0,
+ 1, 1, -1, FPS_SRC_NONE, -1, -1, 0);
+
+MAX77663_PDATA_INIT(ldo2, 800000, 3950000, max77663_rails(sd2), 0, 0, 0,
+ 0, 0, -1, FPS_SRC_NONE, -1, -1, 0);
+
+MAX77663_PDATA_INIT(ldo3, 800000, 3950000, NULL, 0, 0, 0,
+ 1, 1, -1, FPS_SRC_NONE, -1, -1, 0);
+
+MAX77663_PDATA_INIT(ldo4, 800000, 1587500, NULL, 0, 0, 0,
+ 1, 1, -1, FPS_SRC_NONE, -1, -1, 0);
+
+MAX77663_PDATA_INIT(ldo5, 800000, 3950000, NULL, 0, 0, 0,
+ 0, 0, -1, FPS_SRC_NONE, -1, -1, 0);
+
+MAX77663_PDATA_INIT(ldo6, 800000, 3950000, NULL, 1, 0, 0,
+ 1, 1, -1, FPS_SRC_NONE, -1, -1, 0);
+
+MAX77663_PDATA_INIT(ldo7, 800000, 3950000, NULL, 0, 0, 0,
+ 0, 0, -1, FPS_SRC_NONE, -1, -1, 0);
+
+MAX77663_PDATA_INIT(ldo8, 800000, 3950000, NULL, 0, 0, 0,
+ 1, 1, -1, FPS_SRC_NONE, -1, -1, 0);
+
+#define MAX77663_REG(_id, _data) \
+ { \
+ .name = "max77663-regulator", \
+ .id = MAX77663_REGULATOR_ID_##_id, \
+ .platform_data = &max77663_regulator_pdata_##_data, \
+ .pdata_size = sizeof(max77663_regulator_pdata_##_data), \
+ }
+
+#define MAX77663_RTC() \
+ { \
+ .name = "max77663-rtc", \
+ .id = 0, \
+ }
+
+static struct mfd_cell max77663_subdevs[] = {
+ MAX77663_REG(SD0, sd0),
+ MAX77663_REG(SD1, sd1),
+ MAX77663_REG(SD2, sd2),
+ MAX77663_REG(SD3, sd3),
+ MAX77663_REG(LDO0, ldo0),
+ MAX77663_REG(LDO1, ldo1),
+ MAX77663_REG(LDO2, ldo2),
+ MAX77663_REG(LDO3, ldo3),
+ MAX77663_REG(LDO4, ldo4),
+ MAX77663_REG(LDO5, ldo5),
+ MAX77663_REG(LDO6, ldo6),
+ MAX77663_REG(LDO7, ldo7),
+ MAX77663_REG(LDO8, ldo8),
+ MAX77663_RTC(),
+};
+
+struct max77663_gpio_config max77663_gpio_cfgs[] = {
+ {
+ .gpio = MAX77663_GPIO0,
+ .dir = GPIO_DIR_OUT,
+ .dout = GPIO_DOUT_LOW,
+ .out_drv = GPIO_OUT_DRV_PUSH_PULL,
+ .alternate = GPIO_ALT_DISABLE,
+ },
+ {
+ .gpio = MAX77663_GPIO1,
+ .dir = GPIO_DIR_OUT,
+ .dout = GPIO_DOUT_HIGH,
+ .out_drv = GPIO_OUT_DRV_OPEN_DRAIN,
+ .alternate = GPIO_ALT_DISABLE,
+ },
+ {
+ .gpio = MAX77663_GPIO2,
+ .dir = GPIO_DIR_OUT,
+ .dout = GPIO_DOUT_HIGH,
+ .out_drv = GPIO_OUT_DRV_OPEN_DRAIN,
+ .alternate = GPIO_ALT_DISABLE,
+ },
+ {
+ .gpio = MAX77663_GPIO3,
+ .dir = GPIO_DIR_OUT,
+ .dout = GPIO_DOUT_HIGH,
+ .out_drv = GPIO_OUT_DRV_OPEN_DRAIN,
+ .alternate = GPIO_ALT_DISABLE,
+ },
+ {
+ .gpio = MAX77663_GPIO4,
+ .out_drv = GPIO_OUT_DRV_PUSH_PULL,
+ .alternate = GPIO_ALT_ENABLE,
+ },
+ {
+ .gpio = MAX77663_GPIO5,
+ .dir = GPIO_DIR_OUT,
+ .dout = GPIO_DOUT_LOW,
+ .out_drv = GPIO_OUT_DRV_PUSH_PULL,
+ .alternate = GPIO_ALT_DISABLE,
+ },
+ {
+ .gpio = MAX77663_GPIO6,
+ .dir = GPIO_DIR_OUT,
+ .dout = GPIO_DOUT_LOW,
+ .out_drv = GPIO_OUT_DRV_PUSH_PULL,
+ .alternate = GPIO_ALT_DISABLE,
+ },
+ {
+ .gpio = MAX77663_GPIO7,
+ .dir = GPIO_DIR_OUT,
+ .dout = GPIO_DOUT_LOW,
+ .out_drv = GPIO_OUT_DRV_PUSH_PULL,
+ .alternate = GPIO_ALT_DISABLE,
+ },
+};
+
+static struct max77663_platform_data max7763_pdata = {
+ .irq_base = MAX77663_IRQ_BASE,
+ .gpio_base = MAX77663_GPIO_BASE,
+
+ .num_gpio_cfgs = ARRAY_SIZE(max77663_gpio_cfgs),
+ .gpio_cfgs = max77663_gpio_cfgs,
+
+ .num_subdevs = ARRAY_SIZE(max77663_subdevs),
+ .sub_devices = max77663_subdevs,
+};
+
+static struct i2c_board_info __initdata max77663_regulators[] = {
+ {
+ /* The I2C address was determined by OTP factory setting */
+ I2C_BOARD_INFO("max77663", 0x1C),
+ .irq = INT_EXTERNAL_PMU,
+ .platform_data = &max7763_pdata,
+ },
+};
+
+int __init cardhu_pm298_regulator_init(void)
+{
+ struct board_info board_info;
+ struct board_info pmu_board_info;
+ void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE);
+ u32 pmc_ctrl;
+
+ /* configure the power management controller to trigger PMU
+ * interrupts when low */
+ pmc_ctrl = readl(pmc + PMC_CTRL);
+ writel(pmc_ctrl | PMC_CTRL_INTR_LOW, pmc + PMC_CTRL);
+
+ /* The regulator details have complete constraints */
+ tegra_get_board_info(&board_info);
+ tegra_get_pmu_board_info(&pmu_board_info);
+ if (pmu_board_info.board_id != BOARD_PMU_PM298) {
+ pr_err("%s(): Board ID is not proper\n", __func__);
+ return -ENODEV;
+ }
+
+ i2c_register_board_info(4, max77663_regulators,
+ ARRAY_SIZE(max77663_regulators));
+
+ return 0;
+}
+
+static struct regulator_consumer_supply gpio_switch_en_track_ldo2_supply[] = {
+ REGULATOR_SUPPLY("avdd_sata", NULL),
+ REGULATOR_SUPPLY("vdd_sata", NULL),
+ REGULATOR_SUPPLY("avdd_sata_pll", NULL),
+ REGULATOR_SUPPLY("avdd_plle", NULL),
+};
+static int gpio_switch_en_track_ldo2_voltages[] = { 3300};
+
+static struct regulator_consumer_supply gpio_switch_en_5v0_supply[] = {
+ REGULATOR_SUPPLY("vdd_5v0_sys", NULL),
+ REGULATOR_SUPPLY("vdd_5v0_sby", NULL),
+ REGULATOR_SUPPLY("vdd_hall", NULL),
+ REGULATOR_SUPPLY("vterm_ddr", NULL),
+ REGULATOR_SUPPLY("v2ref_ddr", NULL),
+};
+static int gpio_switch_en_5v0_voltages[] = { 5000};
+
+static struct regulator_consumer_supply gpio_switch_en_ddr_supply[] = {
+ REGULATOR_SUPPLY("mem_vddio_ddr", NULL),
+ REGULATOR_SUPPLY("t30_vddio_ddr", NULL),
+};
+static int gpio_switch_en_ddr_voltages[] = { 1500};
+
+static struct regulator_consumer_supply gpio_switch_en_3v3_sys_supply[] = {
+ REGULATOR_SUPPLY("avdd_vdac", NULL),
+ REGULATOR_SUPPLY("vdd_lvds", NULL),
+ REGULATOR_SUPPLY("vdd_pnl", NULL),
+ REGULATOR_SUPPLY("vcom_3v3", NULL),
+ REGULATOR_SUPPLY("vdd_3v3", NULL),
+ REGULATOR_SUPPLY("vddio_pex_ctl", NULL),
+ REGULATOR_SUPPLY("pwrdet_pex_ctl", NULL),
+ REGULATOR_SUPPLY("hvdd_pex_pmu", NULL),
+ REGULATOR_SUPPLY("avdd_hdmi", NULL),
+ REGULATOR_SUPPLY("vpp_fuse", NULL),
+ REGULATOR_SUPPLY("avdd_usb", NULL),
+ REGULATOR_SUPPLY("vdd_ddr_rx", NULL),
+ REGULATOR_SUPPLY("vcore_nand", NULL),
+ REGULATOR_SUPPLY("hvdd_sata", NULL),
+ REGULATOR_SUPPLY("vddio_gmi_pmu", NULL),
+ REGULATOR_SUPPLY("pwrdet_nand", NULL),
+ REGULATOR_SUPPLY("avdd_cam1", NULL),
+ REGULATOR_SUPPLY("vdd_af", NULL),
+ REGULATOR_SUPPLY("avdd_cam2", NULL),
+ REGULATOR_SUPPLY("vdd_acc", NULL),
+ REGULATOR_SUPPLY("vdd_phtl", NULL),
+ REGULATOR_SUPPLY("vddio_tp", NULL),
+ REGULATOR_SUPPLY("vdd_led", NULL),
+ REGULATOR_SUPPLY("vddio_cec", NULL),
+ REGULATOR_SUPPLY("vdd_cmps", NULL),
+ REGULATOR_SUPPLY("vdd_temp", NULL),
+ REGULATOR_SUPPLY("vpp_kfuse", NULL),
+ REGULATOR_SUPPLY("vddio_ts", NULL),
+ REGULATOR_SUPPLY("vdd_ir_led", NULL),
+ REGULATOR_SUPPLY("vddio_1wire", NULL),
+ REGULATOR_SUPPLY("avddio_audio", NULL),
+ REGULATOR_SUPPLY("vdd_ec", NULL),
+ REGULATOR_SUPPLY("vcom_pa", NULL),
+ REGULATOR_SUPPLY("vdd_3v3_devices", NULL),
+ REGULATOR_SUPPLY("vdd_3v3_dock", NULL),
+ REGULATOR_SUPPLY("vdd_3v3_edid", NULL),
+ REGULATOR_SUPPLY("vdd_3v3_hdmi_cec", NULL),
+ REGULATOR_SUPPLY("vdd_3v3_gmi", NULL),
+ REGULATOR_SUPPLY("vdd_3v3_spk_amp", NULL),
+ REGULATOR_SUPPLY("vdd_3v3_sensor", NULL),
+ REGULATOR_SUPPLY("vdd_3v3_cam", NULL),
+ REGULATOR_SUPPLY("vdd_3v3_als", NULL),
+ REGULATOR_SUPPLY("debug_cons", NULL),
+ REGULATOR_SUPPLY("vdd", "4-004c"),
+};
+static int gpio_switch_en_3v3_sys_voltages[] = { 3300};
+
+/* DIS_5V_SWITCH from AP SPI2_SCK X02 */
+static struct regulator_consumer_supply gpio_switch_dis_5v_switch_supply[] = {
+ REGULATOR_SUPPLY("master_5v_switch", NULL),
+};
+static int gpio_switch_dis_5v_switch_voltages[] = { 5000};
+
+/* EN_VDD_BL */
+static struct regulator_consumer_supply gpio_switch_en_vdd_bl_supply[] = {
+ REGULATOR_SUPPLY("vdd_backlight", NULL),
+ REGULATOR_SUPPLY("vdd_backlight1", NULL),
+};
+static int gpio_switch_en_vdd_bl_voltages[] = { 5000};
+
+/* EN_3V3_MODEM from AP GPIO VI_VSYNCH D06*/
+static struct regulator_consumer_supply gpio_switch_en_3v3_modem_supply[] = {
+ REGULATOR_SUPPLY("vdd_3v3_mini_card", NULL),
+ REGULATOR_SUPPLY("vdd_mini_card", NULL),
+};
+static int gpio_switch_en_3v3_modem_voltages[] = { 3300};
+
+/* EN_USB1_VBUS_OC*/
+static struct regulator_consumer_supply gpio_switch_en_usb1_vbus_oc_supply[] = {
+ REGULATOR_SUPPLY("vdd_vbus_micro_usb", NULL),
+};
+static int gpio_switch_en_usb1_vbus_oc_voltages[] = { 5000};
+
+/*EN_USB3_VBUS_OC*/
+static struct regulator_consumer_supply gpio_switch_en_usb3_vbus_oc_supply[] = {
+ REGULATOR_SUPPLY("vdd_vbus_typea_usb", NULL),
+};
+static int gpio_switch_en_usb3_vbus_oc_voltages[] = { 5000};
+
+/* EN_VDDIO_VID_OC from AP GPIO VI_PCLK T00*/
+static struct regulator_consumer_supply gpio_switch_en_vddio_vid_oc_supply[] = {
+ REGULATOR_SUPPLY("vdd_hdmi_con", NULL),
+};
+static int gpio_switch_en_vddio_vid_oc_voltages[] = { 5000};
+
+/* EN_VDD_PNL1 from AP GPIO VI_D6 L04*/
+static struct regulator_consumer_supply gpio_switch_en_vdd_pnl1_supply[] = {
+ REGULATOR_SUPPLY("vdd_lcd_panel", NULL),
+};
+static int gpio_switch_en_vdd_pnl1_voltages[] = { 3300};
+
+/* CAM1_LDO_EN from AP GPIO KB_ROW6 R06*/
+static struct regulator_consumer_supply gpio_switch_cam1_ldo_en_supply[] = {
+ REGULATOR_SUPPLY("vdd_2v8_cam1", NULL),
+ REGULATOR_SUPPLY("vdd_2v8_cam1_af", NULL),
+};
+static int gpio_switch_cam1_ldo_en_voltages[] = { 2800};
+
+/* CAM2_LDO_EN from AP GPIO KB_ROW7 R07*/
+static struct regulator_consumer_supply gpio_switch_cam2_ldo_en_supply[] = {
+ REGULATOR_SUPPLY("vdd_2v8_cam2", NULL),
+ REGULATOR_SUPPLY("vdd_2v8_cam2_af", NULL),
+};
+static int gpio_switch_cam2_ldo_en_voltages[] = { 2800};
+
+/* CAM3_LDO_EN from AP GPIO KB_ROW8 S00*/
+static struct regulator_consumer_supply gpio_switch_cam3_ldo_en_supply[] = {
+ REGULATOR_SUPPLY("vdd_cam3", NULL),
+};
+static int gpio_switch_cam3_ldo_en_voltages[] = { 3300};
+
+/* EN_VDD_COM from AP GPIO SDMMC3_DAT5 D00*/
+static struct regulator_consumer_supply gpio_switch_en_vdd_com_supply[] = {
+ REGULATOR_SUPPLY("vdd_com_bd", NULL),
+};
+static int gpio_switch_en_vdd_com_voltages[] = { 3300};
+
+/* EN_VDD_SDMMC1 from AP GPIO VI_HSYNC D07*/
+static struct regulator_consumer_supply gpio_switch_en_vdd_sdmmc1_supply[] = {
+ REGULATOR_SUPPLY("vddio_sd_slot", "sdhci-tegra.0"),
+};
+static int gpio_switch_en_vdd_sdmmc1_voltages[] = { 3300};
+
+/* EN_3V3_EMMC from AP GPIO SDMMC3_DAT4 D01*/
+static struct regulator_consumer_supply gpio_switch_en_3v3_emmc_supply[] = {
+ REGULATOR_SUPPLY("vdd_emmc_core", NULL),
+};
+static int gpio_switch_en_3v3_emmc_voltages[] = { 3300};
+
+/* EN_3V3_PEX_HVDD from AP GPIO VI_D09 L07*/
+static struct regulator_consumer_supply gpio_switch_en_3v3_pex_hvdd_supply[] = {
+ REGULATOR_SUPPLY("hvdd_pex", NULL),
+};
+static int gpio_switch_en_3v3_pex_hvdd_voltages[] = { 3300};
+
+/* EN_3v3_FUSE from AP GPIO VI_D08 L06*/
+static struct regulator_consumer_supply gpio_switch_en_3v3_fuse_supply[] = {
+ REGULATOR_SUPPLY("vdd_fuse", NULL),
+};
+static int gpio_switch_en_3v3_fuse_voltages[] = { 3300};
+
+/* EN_1V8_CAM from AP GPIO GPIO_PBB4 PBB04*/
+static struct regulator_consumer_supply gpio_switch_en_1v8_cam_supply[] = {
+ REGULATOR_SUPPLY("vdd_1v8_cam1", NULL),
+ REGULATOR_SUPPLY("vdd_1v8_cam2", NULL),
+ REGULATOR_SUPPLY("vdd_1v8_cam3", NULL),
+};
+static int gpio_switch_en_1v8_cam_voltages[] = { 1800};
+
+static struct regulator_consumer_supply gpio_switch_en_vbrtr_supply[] = {
+ REGULATOR_SUPPLY("vdd_vbrtr", NULL),
+};
+static int gpio_switch_en_vbrtr_voltages[] = { 3300};
+
+static int enable_load_switch_rail(
+ struct gpio_switch_regulator_subdev_data *psubdev_data)
+{
+ int ret;
+
+ if (psubdev_data->pin_group <= 0)
+ return -EINVAL;
+
+ /* Tristate and make pin as input*/
+ ret = tegra_pinmux_set_tristate(psubdev_data->pin_group,
+ TEGRA_TRI_TRISTATE);
+ if (ret < 0)
+ return ret;
+ return gpio_direction_input(psubdev_data->gpio_nr);
+}
+
+static int disable_load_switch_rail(
+ struct gpio_switch_regulator_subdev_data *psubdev_data)
+{
+ int ret;
+
+ if (psubdev_data->pin_group <= 0)
+ return -EINVAL;
+
+ /* Un-tristate and driver low */
+ ret = tegra_pinmux_set_tristate(psubdev_data->pin_group,
+ TEGRA_TRI_NORMAL);
+ if (ret < 0)
+ return ret;
+ return gpio_direction_output(psubdev_data->gpio_nr, 0);
+}
+
+
+/* Macro for defining gpio switch regulator sub device data */
+#define GREG_INIT(_id, _var, _name, _input_supply, _always_on, _boot_on, \
+ _gpio_nr, _active_low, _init_state, _pg, _enable, _disable) \
+ static struct gpio_switch_regulator_subdev_data gpio_pdata_##_var = \
+ { \
+ .regulator_name = "gpio-switch-"#_name, \
+ .input_supply = _input_supply, \
+ .id = _id, \
+ .gpio_nr = _gpio_nr, \
+ .pin_group = _pg, \
+ .active_low = _active_low, \
+ .init_state = _init_state, \
+ .voltages = gpio_switch_##_name##_voltages, \
+ .n_voltages = ARRAY_SIZE(gpio_switch_##_name##_voltages), \
+ .num_consumer_supplies = \
+ ARRAY_SIZE(gpio_switch_##_name##_supply), \
+ .consumer_supplies = gpio_switch_##_name##_supply, \
+ .constraints = { \
+ .valid_modes_mask = (REGULATOR_MODE_NORMAL | \
+ REGULATOR_MODE_STANDBY), \
+ .valid_ops_mask = (REGULATOR_CHANGE_MODE | \
+ REGULATOR_CHANGE_STATUS | \
+ REGULATOR_CHANGE_VOLTAGE), \
+ .always_on = _always_on, \
+ .boot_on = _boot_on, \
+ }, \
+ .enable_rail = _enable, \
+ .disable_rail = _disable, \
+ }
+
+/* common to most of boards*/
+GREG_INIT(0, en_track_ldo2, en_track_ldo2, NULL, 0, 0, MAX77663_GPIO_BASE + MAX77663_GPIO0, false, 0, 0, 0, 0);
+GREG_INIT(1, en_5v0, en_5v0, NULL, 1, 0, MAX77663_GPIO_BASE + MAX77663_GPIO2, false, 1, 0, 0, 0);
+GREG_INIT(2, en_ddr, en_ddr, NULL, 1, 0, MAX77663_GPIO_BASE + MAX77663_GPIO3, false, 1, 0, 0, 0);
+GREG_INIT(3, en_3v3_sys, en_3v3_sys, NULL, 1, 0, MAX77663_GPIO_BASE + MAX77663_GPIO1, false, 1, 0, 0, 0);
+GREG_INIT(4, en_vdd_bl, en_vdd_bl, NULL, 0, 0, TEGRA_GPIO_PK3, false, 1, 0, 0, 0);
+GREG_INIT(5, en_3v3_modem, en_3v3_modem, NULL, 1, 0, TEGRA_GPIO_PD6, false, 1, 0, 0, 0);
+GREG_INIT(6, en_vdd_pnl1, en_vdd_pnl1, "vdd_3v3_devices", 0, 0, TEGRA_GPIO_PL4, false, 1, 0, 0, 0);
+GREG_INIT(7, cam3_ldo_en, cam3_ldo_en, "vdd_3v3_devices", 0, 0, TEGRA_GPIO_PS0, false, 0, 0, 0, 0);
+GREG_INIT(8, en_vdd_com, en_vdd_com, "vdd_3v3_devices", 1, 0, TEGRA_GPIO_PD0, false, 1, 0, 0, 0);
+GREG_INIT(9, en_3v3_fuse, en_3v3_fuse, "vdd_3v3_devices", 0, 0, TEGRA_GPIO_PL6, false, 0, 0, 0, 0);
+GREG_INIT(10, en_3v3_emmc, en_3v3_emmc, "vdd_3v3_devices", 1, 0, TEGRA_GPIO_PD1, false, 1, 0, 0, 0);
+GREG_INIT(11, en_vdd_sdmmc1, en_vdd_sdmmc1, "vdd_3v3_devices", 0, 0, TEGRA_GPIO_PD7, false, 1, 0, 0, 0);
+GREG_INIT(12, en_3v3_pex_hvdd, en_3v3_pex_hvdd, "hvdd_pex_pmu", 0, 0, TEGRA_GPIO_PL7, false, 0, 0, 0, 0);
+GREG_INIT(13, en_1v8_cam, en_1v8_cam, "vdd_gen1v8", 0, 0, TEGRA_GPIO_PBB4, false, 0, 0, 0, 0);
+
+/*Specific to pm269*/
+GREG_INIT(4, en_vdd_bl_pm269, en_vdd_bl, NULL,
+ 0, 0, TEGRA_GPIO_PH3, false, 1, 0, 0, 0);
+GREG_INIT(6, en_vdd_pnl1_pm269, en_vdd_pnl1, "vdd_3v3_devices",
+ 0, 0, TEGRA_GPIO_PW1, false, 1, 0, 0, 0);
+GREG_INIT(9, en_3v3_fuse_pm269, en_3v3_fuse, "vdd_3v3_devices",
+ 0, 0, TEGRA_GPIO_PC1, false, 0, 0, 0, 0);
+GREG_INIT(12, en_3v3_pex_hvdd_pm269, en_3v3_pex_hvdd, "hvdd_pex_pmu",
+ 0, 0, TEGRA_GPIO_PC6, false, 0, 0, 0, 0);
+GREG_INIT(17, en_vddio_vid_oc_pm269, en_vddio_vid_oc, "master_5v_switch",
+ 0, 0, TEGRA_GPIO_PP2, false, 0, TEGRA_PINGROUP_DAP3_DOUT,
+ enable_load_switch_rail, disable_load_switch_rail);
+
+/* Specific to E1187/E1186/E1256 */
+GREG_INIT(14, dis_5v_switch_e118x, dis_5v_switch, "vdd_5v0_sys",
+ 0, 0, TEGRA_GPIO_PX2, true, 0, 0, 0, 0);
+GREG_INIT(15, en_usb1_vbus_oc_e118x, en_usb1_vbus_oc, "master_5v_switch",
+ 0, 0, TEGRA_GPIO_PI4, false, 0, TEGRA_PINGROUP_GMI_RST_N,
+ enable_load_switch_rail, disable_load_switch_rail);
+GREG_INIT(16, en_usb3_vbus_oc_e118x, en_usb3_vbus_oc, "master_5v_switch",
+ 0, 0, TEGRA_GPIO_PH7, false, 0, TEGRA_PINGROUP_GMI_AD15,
+ enable_load_switch_rail, disable_load_switch_rail);
+GREG_INIT(17, en_vddio_vid_oc_e118x, en_vddio_vid_oc, "master_5v_switch",
+ 0, 0, TEGRA_GPIO_PT0, false, 0, TEGRA_PINGROUP_VI_PCLK,
+ enable_load_switch_rail, disable_load_switch_rail);
+
+/* E1198/E1291 specific*/
+GREG_INIT(18, cam1_ldo_en, cam1_ldo_en, "vdd_3v3_cam", 0, 0, TEGRA_GPIO_PR6, false, 0, 0, 0, 0);
+GREG_INIT(19, cam2_ldo_en, cam2_ldo_en, "vdd_3v3_cam", 0, 0, TEGRA_GPIO_PR7, false, 0, 0, 0, 0);
+
+GREG_INIT(22, en_vbrtr, en_vbrtr, "vdd_3v3_devices", 0, 0, PMU_TCA6416_GPIO_PORT12, false, 0, 0, 0, 0);
+
+#define ADD_GPIO_REG(_name) &gpio_pdata_##_name
+
+#define COMMON_GPIO_REG \
+ ADD_GPIO_REG(en_track_ldo2), \
+ ADD_GPIO_REG(en_5v0), \
+ ADD_GPIO_REG(en_ddr), \
+ ADD_GPIO_REG(en_3v3_sys), \
+ ADD_GPIO_REG(en_3v3_modem), \
+ ADD_GPIO_REG(en_vdd_pnl1), \
+ ADD_GPIO_REG(cam1_ldo_en), \
+ ADD_GPIO_REG(cam2_ldo_en), \
+ ADD_GPIO_REG(cam3_ldo_en), \
+ ADD_GPIO_REG(en_vdd_com), \
+ ADD_GPIO_REG(en_3v3_fuse), \
+ ADD_GPIO_REG(en_3v3_emmc), \
+ ADD_GPIO_REG(en_vdd_sdmmc1), \
+ ADD_GPIO_REG(en_3v3_pex_hvdd), \
+ ADD_GPIO_REG(en_1v8_cam),
+
+#define PM269_GPIO_REG \
+ ADD_GPIO_REG(en_track_ldo2), \
+ ADD_GPIO_REG(en_5v0), \
+ ADD_GPIO_REG(en_ddr), \
+ ADD_GPIO_REG(en_vdd_bl_pm269), \
+ ADD_GPIO_REG(en_3v3_sys), \
+ ADD_GPIO_REG(en_3v3_modem), \
+ ADD_GPIO_REG(en_vdd_pnl1_pm269), \
+ ADD_GPIO_REG(cam1_ldo_en), \
+ ADD_GPIO_REG(cam2_ldo_en), \
+ ADD_GPIO_REG(cam3_ldo_en), \
+ ADD_GPIO_REG(en_vdd_com), \
+ ADD_GPIO_REG(en_3v3_fuse_pm269), \
+ ADD_GPIO_REG(en_3v3_emmc), \
+ ADD_GPIO_REG(en_3v3_pex_hvdd_pm269), \
+ ADD_GPIO_REG(en_1v8_cam), \
+ ADD_GPIO_REG(dis_5v_switch_e118x), \
+ ADD_GPIO_REG(en_usb1_vbus_oc_e118x), \
+ ADD_GPIO_REG(en_usb3_vbus_oc_e118x), \
+ ADD_GPIO_REG(en_vddio_vid_oc_pm269),
+
+#define E118x_GPIO_REG \
+ ADD_GPIO_REG(en_vdd_bl), \
+ ADD_GPIO_REG(dis_5v_switch_e118x), \
+ ADD_GPIO_REG(en_usb1_vbus_oc_e118x), \
+ ADD_GPIO_REG(en_usb3_vbus_oc_e118x), \
+ ADD_GPIO_REG(en_vddio_vid_oc_e118x), \
+ ADD_GPIO_REG(en_vbrtr),
+
+/* Gpio switch regulator platform data for E1186/E1187/E1256*/
+static struct gpio_switch_regulator_subdev_data *gswitch_subdevs_e118x[] = {
+ COMMON_GPIO_REG
+ E118x_GPIO_REG
+};
+
+/* Gpio switch regulator platform data for PM269*/
+static struct gpio_switch_regulator_subdev_data *gswitch_subdevs_pm269[] = {
+ PM269_GPIO_REG
+};
+
+static struct gpio_switch_regulator_platform_data gswitch_pdata;
+static struct platform_device gswitch_regulator_pdata = {
+ .name = "gpio-switch-regulator",
+ .id = -1,
+ .dev = {
+ .platform_data = &gswitch_pdata,
+ },
+};
+
+int __init cardhu_pm298_gpio_switch_regulator_init(void)
+{
+ int i;
+ struct board_info board_info;
+ tegra_get_board_info(&board_info);
+
+ switch (board_info.board_id) {
+ case BOARD_PM269:
+ case BOARD_PM305:
+ case BOARD_PM311:
+ case BOARD_E1257:
+ gswitch_pdata.num_subdevs = ARRAY_SIZE(gswitch_subdevs_pm269);
+ gswitch_pdata.subdevs = gswitch_subdevs_pm269;
+ break;
+
+ default:
+ gswitch_pdata.num_subdevs = ARRAY_SIZE(gswitch_subdevs_e118x);
+ gswitch_pdata.subdevs = gswitch_subdevs_e118x;
+ break;
+ }
+
+ for (i = 0; i < gswitch_pdata.num_subdevs; ++i) {
+ struct gpio_switch_regulator_subdev_data *gswitch_data =
+ gswitch_pdata.subdevs[i];
+ if (gswitch_data->gpio_nr <= TEGRA_NR_GPIOS)
+ tegra_gpio_enable(gswitch_data->gpio_nr);
+ }
+
+ return platform_device_register(&gswitch_regulator_pdata);
+}
diff --git a/arch/arm/mach-tegra/board-cardhu-pm299-power-rails.c b/arch/arm/mach-tegra/board-cardhu-pm299-power-rails.c
new file mode 100644
index 000000000000..be317568ca82
--- /dev/null
+++ b/arch/arm/mach-tegra/board-cardhu-pm299-power-rails.c
@@ -0,0 +1,728 @@
+/*
+ * arch/arm/mach-tegra/board-cardhu-pm299-power-rails.c
+ *
+ * Copyright (C) 2011 NVIDIA, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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/i2c.h>
+#include <linux/pda_power.h>
+#include <linux/platform_device.h>
+#include <linux/resource.h>
+#include <linux/regulator/machine.h>
+#include <linux/mfd/ricoh583.h>
+#include <linux/gpio.h>
+#include <linux/io.h>
+#include <linux/regulator/gpio-switch-regulator.h>
+#include <linux/regulator/ricoh583-regulator.h>
+#include <linux/regulator/tps6236x-regulator.h>
+
+#include <mach/iomap.h>
+#include <mach/irqs.h>
+#include <mach/pinmux.h>
+#include <mach/edp.h>
+
+#include "gpio-names.h"
+#include "board.h"
+#include "board-cardhu.h"
+#include "wakeups-t3.h"
+#include "mach/tsensor.h"
+
+#define PMC_CTRL 0x0
+#define PMC_CTRL_INTR_LOW (1 << 17)
+
+static struct regulator_consumer_supply ricoh583_dc1_supply_skubit0_0[] = {
+ REGULATOR_SUPPLY("vdd_core", NULL),
+ REGULATOR_SUPPLY("en_vddio_ddr_1v2", NULL),
+};
+
+static struct regulator_consumer_supply ricoh583_dc1_supply_skubit0_1[] = {
+ REGULATOR_SUPPLY("en_vddio_ddr_1v2", NULL),
+};
+
+static struct regulator_consumer_supply ricoh583_dc3_supply_0[] = {
+ REGULATOR_SUPPLY("vdd_gen1v5", NULL),
+ REGULATOR_SUPPLY("vcore_lcd", NULL),
+ REGULATOR_SUPPLY("track_ldo1", NULL),
+ REGULATOR_SUPPLY("external_ldo_1v2", NULL),
+ REGULATOR_SUPPLY("vcore_cam1", NULL),
+ REGULATOR_SUPPLY("vcore_cam2", NULL),
+};
+
+static struct regulator_consumer_supply ricoh583_dc0_supply_0[] = {
+ REGULATOR_SUPPLY("vdd_cpu_pmu", NULL),
+ REGULATOR_SUPPLY("vdd_cpu", NULL),
+ REGULATOR_SUPPLY("vdd_sys", NULL),
+};
+
+static struct regulator_consumer_supply ricoh583_dc2_supply_0[] = {
+ REGULATOR_SUPPLY("vdd_gen1v8", NULL),
+ REGULATOR_SUPPLY("avdd_hdmi_pll", NULL),
+ REGULATOR_SUPPLY("avdd_usb_pll", NULL),
+ REGULATOR_SUPPLY("avdd_osc", NULL),
+ REGULATOR_SUPPLY("vddio_sys", NULL),
+ REGULATOR_SUPPLY("vddio_sdmmc", "sdhci-tegra.3"),
+ REGULATOR_SUPPLY("pwrdet_sdmmc4", NULL),
+ REGULATOR_SUPPLY("vdd1v8_satelite", NULL),
+ REGULATOR_SUPPLY("vddio_uart", NULL),
+ REGULATOR_SUPPLY("pwrdet_uart", NULL),
+ REGULATOR_SUPPLY("vddio_audio", NULL),
+ REGULATOR_SUPPLY("pwrdet_audio", NULL),
+ REGULATOR_SUPPLY("vddio_bb", NULL),
+ REGULATOR_SUPPLY("pwrdet_bb", NULL),
+ REGULATOR_SUPPLY("vddio_lcd_pmu", NULL),
+ REGULATOR_SUPPLY("pwrdet_lcd", NULL),
+ REGULATOR_SUPPLY("vddio_cam", NULL),
+ REGULATOR_SUPPLY("pwrdet_cam", NULL),
+ REGULATOR_SUPPLY("vddio_vi", NULL),
+ REGULATOR_SUPPLY("pwrdet_vi", NULL),
+ REGULATOR_SUPPLY("ldo6", NULL),
+ REGULATOR_SUPPLY("ldo7", NULL),
+ REGULATOR_SUPPLY("ldo8", NULL),
+ REGULATOR_SUPPLY("vcore_audio", NULL),
+ REGULATOR_SUPPLY("avcore_audio", NULL),
+ REGULATOR_SUPPLY("vddio_sdmmc", "sdhci-tegra.2"),
+ REGULATOR_SUPPLY("pwrdet_sdmmc3", NULL),
+ REGULATOR_SUPPLY("vcore1_lpddr2", NULL),
+ REGULATOR_SUPPLY("vcom_1v8", NULL),
+ REGULATOR_SUPPLY("pmuio_1v8", NULL),
+ REGULATOR_SUPPLY("avdd_ic_usb", NULL),
+};
+
+static struct regulator_consumer_supply ricoh583_ldo0_supply_0[] = {
+ REGULATOR_SUPPLY("unused_ldo0", NULL),
+};
+
+static struct regulator_consumer_supply ricoh583_ldo1_supply_0[] = {
+ REGULATOR_SUPPLY("avdd_pexb", NULL),
+ REGULATOR_SUPPLY("vdd_pexb", NULL),
+ REGULATOR_SUPPLY("avdd_pex_pll", NULL),
+ REGULATOR_SUPPLY("avdd_pexa", NULL),
+ REGULATOR_SUPPLY("vdd_pexa", NULL),
+};
+
+static struct regulator_consumer_supply ricoh583_ldo2_supply_0[] = {
+ REGULATOR_SUPPLY("avdd_sata", NULL),
+ REGULATOR_SUPPLY("vdd_sata", NULL),
+ REGULATOR_SUPPLY("avdd_sata_pll", NULL),
+ REGULATOR_SUPPLY("avdd_plle", NULL),
+};
+
+static struct regulator_consumer_supply ricoh583_ldo3_supply_0[] = {
+ REGULATOR_SUPPLY("vddio_sdmmc", "sdhci-tegra.0"),
+ REGULATOR_SUPPLY("pwrdet_sdmmc1", NULL),
+};
+
+static struct regulator_consumer_supply ricoh583_ldo4_supply_0[] = {
+ REGULATOR_SUPPLY("vdd_rtc", NULL),
+};
+
+static struct regulator_consumer_supply ricoh583_ldo5_supply_0[] = {
+ REGULATOR_SUPPLY("avdd_vdac", NULL),
+};
+
+static struct regulator_consumer_supply ricoh583_ldo6_supply_0[] = {
+ REGULATOR_SUPPLY("avdd_dsi_csi", NULL),
+ REGULATOR_SUPPLY("pwrdet_mipi", NULL),
+};
+static struct regulator_consumer_supply ricoh583_ldo7_supply_0[] = {
+ REGULATOR_SUPPLY("avdd_plla_p_c_s", NULL),
+ REGULATOR_SUPPLY("avdd_pllm", NULL),
+ REGULATOR_SUPPLY("avdd_pllu_d", NULL),
+ REGULATOR_SUPPLY("avdd_pllu_d2", NULL),
+ REGULATOR_SUPPLY("avdd_pllx", NULL),
+};
+
+static struct regulator_consumer_supply ricoh583_ldo8_supply_0[] = {
+ REGULATOR_SUPPLY("vdd_ddr_hs", NULL),
+};
+
+#define TPS_PDATA_INIT(_name, _sname, _minmv, _maxmv, _supply_reg, _always_on, \
+ _boot_on, _apply_uv, _init_uV, _init_enable, _init_apply, _flags, \
+ _ext_contol, _ds_slots) \
+ static struct ricoh583_regulator_platform_data pdata_##_name##_##_sname = \
+ { \
+ .regulator = { \
+ .constraints = { \
+ .min_uV = (_minmv)*1000, \
+ .max_uV = (_maxmv)*1000, \
+ .valid_modes_mask = (REGULATOR_MODE_NORMAL | \
+ REGULATOR_MODE_STANDBY), \
+ .valid_ops_mask = (REGULATOR_CHANGE_MODE | \
+ REGULATOR_CHANGE_STATUS | \
+ REGULATOR_CHANGE_VOLTAGE), \
+ .always_on = _always_on, \
+ .boot_on = _boot_on, \
+ .apply_uV = _apply_uv, \
+ }, \
+ .num_consumer_supplies = \
+ ARRAY_SIZE(ricoh583_##_name##_supply_##_sname), \
+ .consumer_supplies = ricoh583_##_name##_supply_##_sname, \
+ .supply_regulator = _supply_reg, \
+ }, \
+ .init_uV = _init_uV * 1000, \
+ .init_enable = _init_enable, \
+ .init_apply = _init_apply, \
+ .deepsleep_slots = _ds_slots, \
+ .flags = _flags, \
+ .ext_pwr_req = _ext_contol, \
+ }
+
+TPS_PDATA_INIT(dc0, 0, 700, 1500, 0, 1, 1, 0, -1, 0, 0, 0,
+ RICOH583_EXT_PWRREQ2_CONTROL, 0);
+TPS_PDATA_INIT(dc1, skubit0_0, 700, 1500, 0, 1, 1, 0, -1, 0, 0, 0, 0, 0);
+TPS_PDATA_INIT(dc2, 0, 900, 2400, 0, 1, 1, 0, -1, 0, 0, 0, 0, 0);
+TPS_PDATA_INIT(dc3, 0, 900, 2400, 0, 1, 1, 0, -1, 0, 0, 0, 0, 0);
+
+TPS_PDATA_INIT(ldo0, 0, 1000, 3300, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0);
+TPS_PDATA_INIT(ldo1, 0, 1000, 3300, ricoh583_rails(DC1), 0, 0, 0, -1, 0, 0, 0, 0, 0);
+TPS_PDATA_INIT(ldo2, 0, 1050, 1050, ricoh583_rails(DC1), 0, 0, 1, -1, 0, 0, 0, 0, 0);
+
+TPS_PDATA_INIT(ldo3, 0, 1000, 3300, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0);
+TPS_PDATA_INIT(ldo4, 0, 750, 1500, 0, 1, 0, 0, -1, 0, 0, 0, 0, 0);
+TPS_PDATA_INIT(ldo5, 0, 1000, 3300, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0);
+
+TPS_PDATA_INIT(ldo6, 0, 1200, 1200, ricoh583_rails(DC2), 0, 0, 1, -1, 0, 0, 0, 0, 0);
+TPS_PDATA_INIT(ldo7, 0, 1200, 1200, ricoh583_rails(DC2), 1, 1, 1, -1, 0, 0, 0, 0, 0);
+TPS_PDATA_INIT(ldo8, 0, 900, 3400, ricoh583_rails(DC2), 1, 0, 0, -1, 0, 0, 0, 0, 0);
+
+#define TPS_REG(_id, _name, _sname) \
+ { \
+ .id = RICOH583_ID_##_id, \
+ .name = "ricoh583-regulator", \
+ .platform_data = &pdata_##_name##_##_sname, \
+ }
+
+#define TPS6591X_DEV_COMMON_E118X \
+ TPS_REG(DC0, dc0, 0), \
+ TPS_REG(DC1, dc1, skubit0_0), \
+ TPS_REG(DC2, dc2, 0), \
+ TPS_REG(DC3, dc3, 0), \
+ TPS_REG(LDO0, ldo8, 0), \
+ TPS_REG(LDO1, ldo7, 0), \
+ TPS_REG(LDO2, ldo6, 0), \
+ TPS_REG(LDO3, ldo5, 0), \
+ TPS_REG(LDO4, ldo4, 0), \
+ TPS_REG(LDO5, ldo3, 0), \
+ TPS_REG(LDO6, ldo0, 0), \
+ TPS_REG(LDO7, ldo1, 0), \
+ TPS_REG(LDO8, ldo2, 0)
+
+static struct ricoh583_subdev_info tps_devs_e118x_dcdc[] = {
+ TPS6591X_DEV_COMMON_E118X,
+};
+
+#define RICOH_GPIO_INIT(_init_apply, _pulldn, _output_mode, _output_val) \
+ { \
+ .pulldn_en = _pulldn, \
+ .output_mode_en = _output_mode, \
+ .output_val = _output_val, \
+ .init_apply = _init_apply, \
+ }
+struct ricoh583_gpio_init_data ricoh_gpio_data[] = {
+ RICOH_GPIO_INIT(false, false, false, 0),
+ RICOH_GPIO_INIT(false, false, false, 0),
+ RICOH_GPIO_INIT(false, false, false, 0),
+ RICOH_GPIO_INIT(true, false, true, 1),
+ RICOH_GPIO_INIT(true, false, true, 1),
+ RICOH_GPIO_INIT(false, false, false, 0),
+ RICOH_GPIO_INIT(false, false, false, 0),
+ RICOH_GPIO_INIT(false, false, false, 0),
+};
+
+static struct ricoh583_platform_data ricoh_platform = {
+ .irq_base = RICOH583_IRQ_BASE,
+ .gpio_base = RICOH583_GPIO_BASE,
+ .gpio_init_data = ricoh_gpio_data,
+ .num_gpioinit_data = ARRAY_SIZE(ricoh_gpio_data),
+ .enable_shutdown_pin = true,
+};
+
+static struct i2c_board_info __initdata ricoh583_regulators[] = {
+ {
+ I2C_BOARD_INFO("ricoh583", 0x34),
+ .irq = INT_EXTERNAL_PMU,
+ .platform_data = &ricoh_platform,
+ },
+};
+
+/* TPS62361B DC-DC converter */
+static struct regulator_consumer_supply tps6236x_dcdc_supply[] = {
+ REGULATOR_SUPPLY("vdd_core", NULL),
+};
+
+static struct tps6236x_regulator_platform_data tps6236x_pdata = {
+ .reg_init_data = { \
+ .constraints = { \
+ .min_uV = 500000, \
+ .max_uV = 1770000, \
+ .valid_modes_mask = (REGULATOR_MODE_NORMAL | \
+ REGULATOR_MODE_STANDBY), \
+ .valid_ops_mask = (REGULATOR_CHANGE_MODE | \
+ REGULATOR_CHANGE_STATUS | \
+ REGULATOR_CHANGE_VOLTAGE), \
+ .always_on = 1, \
+ .boot_on = 1, \
+ .apply_uV = 0, \
+ }, \
+ .num_consumer_supplies = ARRAY_SIZE(tps6236x_dcdc_supply), \
+ .consumer_supplies = tps6236x_dcdc_supply, \
+ }, \
+ .internal_pd_enable = 0, \
+ .vsel = 3, \
+ .init_uV = -1, \
+ .init_apply = 0, \
+};
+
+static struct i2c_board_info __initdata tps6236x_boardinfo[] = {
+ {
+ I2C_BOARD_INFO("tps62361B", 0x60),
+ .platform_data = &tps6236x_pdata,
+ },
+};
+
+int __init cardhu_pm299_regulator_init(void)
+{
+ struct board_info board_info;
+ struct board_info pmu_board_info;
+ void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE);
+ u32 pmc_ctrl;
+
+ /* configure the power management controller to trigger PMU
+ * interrupts when low */
+ pmc_ctrl = readl(pmc + PMC_CTRL);
+ writel(pmc_ctrl | PMC_CTRL_INTR_LOW, pmc + PMC_CTRL);
+
+ /* The regulator details have complete constraints */
+ tegra_get_board_info(&board_info);
+ tegra_get_pmu_board_info(&pmu_board_info);
+ if (pmu_board_info.board_id != BOARD_PMU_PM299) {
+ pr_err("%s(): Board ID is not proper\n", __func__);
+ return -ENODEV;
+ }
+
+ /* If TPS6236x DCDC is there then consumer for dc1 should
+ * not have vdd_core */
+ if ((board_info.sku & SKU_DCDC_TPS62361_SUPPORT) ||
+ (pmu_board_info.sku & SKU_DCDC_TPS62361_SUPPORT)) {
+ pdata_dc1_skubit0_0.regulator.consumer_supplies =
+ ricoh583_dc1_supply_skubit0_1;
+ pdata_dc1_skubit0_0.regulator.num_consumer_supplies =
+ ARRAY_SIZE(ricoh583_dc1_supply_skubit0_1);
+ }
+
+ ricoh_platform.num_subdevs = ARRAY_SIZE(tps_devs_e118x_dcdc);
+ ricoh_platform.subdevs = tps_devs_e118x_dcdc;
+
+ i2c_register_board_info(4, ricoh583_regulators, 1);
+
+ /* Register the TPS6236x for all boards whose sku bit 0 is set. */
+ if ((board_info.sku & SKU_DCDC_TPS62361_SUPPORT) ||
+ (pmu_board_info.sku & SKU_DCDC_TPS62361_SUPPORT)) {
+ pr_info("Registering the device TPS62361B\n");
+ i2c_register_board_info(4, tps6236x_boardinfo, 1);
+ }
+ return 0;
+}
+
+/* EN_5V_CP from PMU GP0 */
+static struct regulator_consumer_supply gpio_switch_en_5v_cp_supply[] = {
+ REGULATOR_SUPPLY("vdd_5v0_sby", NULL),
+ REGULATOR_SUPPLY("vdd_hall", NULL),
+ REGULATOR_SUPPLY("vterm_ddr", NULL),
+ REGULATOR_SUPPLY("v2ref_ddr", NULL),
+};
+static int gpio_switch_en_5v_cp_voltages[] = { 5000};
+
+/* EN_5V0 From PMU GP2 */
+static struct regulator_consumer_supply gpio_switch_en_5v0_supply[] = {
+ REGULATOR_SUPPLY("vdd_5v0_sys", NULL),
+};
+static int gpio_switch_en_5v0_voltages[] = { 5000};
+
+/* EN_DDR From PMU GP6 */
+static struct regulator_consumer_supply gpio_switch_en_ddr_supply[] = {
+ REGULATOR_SUPPLY("mem_vddio_ddr", NULL),
+ REGULATOR_SUPPLY("t30_vddio_ddr", NULL),
+};
+static int gpio_switch_en_ddr_voltages[] = { 1500};
+
+/* EN_3V3_SYS From PMU GP7 */
+static struct regulator_consumer_supply gpio_switch_en_3v3_sys_supply[] = {
+ REGULATOR_SUPPLY("vdd_lvds", NULL),
+ REGULATOR_SUPPLY("vdd_pnl", NULL),
+ REGULATOR_SUPPLY("vcom_3v3", NULL),
+ REGULATOR_SUPPLY("vdd_3v3", NULL),
+ REGULATOR_SUPPLY("vcore_mmc", NULL),
+ REGULATOR_SUPPLY("vddio_pex_ctl", NULL),
+ REGULATOR_SUPPLY("pwrdet_pex_ctl", NULL),
+ REGULATOR_SUPPLY("hvdd_pex_pmu", NULL),
+ REGULATOR_SUPPLY("avdd_hdmi", NULL),
+ REGULATOR_SUPPLY("vpp_fuse", NULL),
+ REGULATOR_SUPPLY("avdd_usb", NULL),
+ REGULATOR_SUPPLY("vdd_ddr_rx", NULL),
+ REGULATOR_SUPPLY("vcore_nand", NULL),
+ REGULATOR_SUPPLY("hvdd_sata", NULL),
+ REGULATOR_SUPPLY("vddio_gmi_pmu", NULL),
+ REGULATOR_SUPPLY("pwrdet_nand", NULL),
+ REGULATOR_SUPPLY("avdd_cam1", NULL),
+ REGULATOR_SUPPLY("vdd_af", NULL),
+ REGULATOR_SUPPLY("avdd_cam2", NULL),
+ REGULATOR_SUPPLY("vdd_acc", NULL),
+ REGULATOR_SUPPLY("vdd_phtl", NULL),
+ REGULATOR_SUPPLY("vddio_tp", NULL),
+ REGULATOR_SUPPLY("vdd_led", NULL),
+ REGULATOR_SUPPLY("vddio_cec", NULL),
+ REGULATOR_SUPPLY("vdd_cmps", NULL),
+ REGULATOR_SUPPLY("vdd_temp", NULL),
+ REGULATOR_SUPPLY("vpp_kfuse", NULL),
+ REGULATOR_SUPPLY("vddio_ts", NULL),
+ REGULATOR_SUPPLY("vdd_ir_led", NULL),
+ REGULATOR_SUPPLY("vddio_1wire", NULL),
+ REGULATOR_SUPPLY("avddio_audio", NULL),
+ REGULATOR_SUPPLY("vdd_ec", NULL),
+ REGULATOR_SUPPLY("vcom_pa", NULL),
+ REGULATOR_SUPPLY("vdd_3v3_devices", NULL),
+ REGULATOR_SUPPLY("vdd_3v3_dock", NULL),
+ REGULATOR_SUPPLY("vdd_3v3_edid", NULL),
+ REGULATOR_SUPPLY("vdd_3v3_hdmi_cec", NULL),
+ REGULATOR_SUPPLY("vdd_3v3_gmi", NULL),
+ REGULATOR_SUPPLY("vdd_3v3_spk_amp", NULL),
+ REGULATOR_SUPPLY("vdd_3v3_sensor", NULL),
+ REGULATOR_SUPPLY("vdd_3v3_cam", NULL),
+ REGULATOR_SUPPLY("vdd_3v3_als", NULL),
+ REGULATOR_SUPPLY("debug_cons", NULL),
+};
+static int gpio_switch_en_3v3_sys_voltages[] = { 3300};
+
+/* DIS_5V_SWITCH from AP SPI2_SCK X02 */
+static struct regulator_consumer_supply gpio_switch_dis_5v_switch_supply[] = {
+ REGULATOR_SUPPLY("master_5v_switch", NULL),
+};
+static int gpio_switch_dis_5v_switch_voltages[] = { 5000};
+
+/* EN_VDD_BL */
+static struct regulator_consumer_supply gpio_switch_en_vdd_bl_supply[] = {
+ REGULATOR_SUPPLY("vdd_backlight", NULL),
+ REGULATOR_SUPPLY("vdd_backlight1", NULL),
+};
+static int gpio_switch_en_vdd_bl_voltages[] = { 5000};
+
+/* EN_3V3_MODEM from AP GPIO VI_VSYNCH D06*/
+static struct regulator_consumer_supply gpio_switch_en_3v3_modem_supply[] = {
+ REGULATOR_SUPPLY("vdd_3v3_mini_card", NULL),
+ REGULATOR_SUPPLY("vdd_mini_card", NULL),
+};
+static int gpio_switch_en_3v3_modem_voltages[] = { 3300};
+
+/* EN_USB1_VBUS_OC*/
+static struct regulator_consumer_supply gpio_switch_en_usb1_vbus_oc_supply[] = {
+ REGULATOR_SUPPLY("vdd_vbus_micro_usb", NULL),
+};
+static int gpio_switch_en_usb1_vbus_oc_voltages[] = { 5000};
+
+/*EN_USB3_VBUS_OC*/
+static struct regulator_consumer_supply gpio_switch_en_usb3_vbus_oc_supply[] = {
+ REGULATOR_SUPPLY("vdd_vbus_typea_usb", NULL),
+};
+static int gpio_switch_en_usb3_vbus_oc_voltages[] = { 5000};
+
+/* EN_VDDIO_VID_OC from AP GPIO VI_PCLK T00*/
+static struct regulator_consumer_supply gpio_switch_en_vddio_vid_oc_supply[] = {
+ REGULATOR_SUPPLY("vdd_hdmi_con", NULL),
+};
+static int gpio_switch_en_vddio_vid_oc_voltages[] = { 5000};
+
+/* EN_VDD_PNL1 from AP GPIO VI_D6 L04*/
+static struct regulator_consumer_supply gpio_switch_en_vdd_pnl1_supply[] = {
+ REGULATOR_SUPPLY("vdd_lcd_panel", NULL),
+};
+static int gpio_switch_en_vdd_pnl1_voltages[] = { 3300};
+
+/* CAM1_LDO_EN from AP GPIO KB_ROW6 R06*/
+static struct regulator_consumer_supply gpio_switch_cam1_ldo_en_supply[] = {
+ REGULATOR_SUPPLY("vdd_2v8_cam1", NULL),
+ REGULATOR_SUPPLY("vdd_2v8_cam1_af", NULL),
+};
+static int gpio_switch_cam1_ldo_en_voltages[] = { 2800};
+
+/* CAM2_LDO_EN from AP GPIO KB_ROW7 R07*/
+static struct regulator_consumer_supply gpio_switch_cam2_ldo_en_supply[] = {
+ REGULATOR_SUPPLY("vdd_2v8_cam2", NULL),
+ REGULATOR_SUPPLY("vdd_2v8_cam2_af", NULL),
+};
+static int gpio_switch_cam2_ldo_en_voltages[] = { 2800};
+
+/* CAM3_LDO_EN from AP GPIO KB_ROW8 S00*/
+static struct regulator_consumer_supply gpio_switch_cam3_ldo_en_supply[] = {
+ REGULATOR_SUPPLY("vdd_cam3", NULL),
+};
+static int gpio_switch_cam3_ldo_en_voltages[] = { 3300};
+
+/* EN_VDD_COM from AP GPIO SDMMC3_DAT5 D00*/
+static struct regulator_consumer_supply gpio_switch_en_vdd_com_supply[] = {
+ REGULATOR_SUPPLY("vdd_com_bd", NULL),
+};
+static int gpio_switch_en_vdd_com_voltages[] = { 3300};
+
+/* EN_VDD_SDMMC1 from AP GPIO VI_HSYNC D07*/
+static struct regulator_consumer_supply gpio_switch_en_vdd_sdmmc1_supply[] = {
+ REGULATOR_SUPPLY("vddio_sd_slot", "sdhci-tegra.0"),
+};
+static int gpio_switch_en_vdd_sdmmc1_voltages[] = { 3300};
+
+/* EN_3V3_EMMC from AP GPIO SDMMC3_DAT4 D01*/
+static struct regulator_consumer_supply gpio_switch_en_3v3_emmc_supply[] = {
+ REGULATOR_SUPPLY("vdd_emmc_core", NULL),
+};
+static int gpio_switch_en_3v3_emmc_voltages[] = { 3300};
+
+/* EN_3V3_PEX_HVDD from AP GPIO VI_D09 L07*/
+static struct regulator_consumer_supply gpio_switch_en_3v3_pex_hvdd_supply[] = {
+ REGULATOR_SUPPLY("hvdd_pex", NULL),
+};
+static int gpio_switch_en_3v3_pex_hvdd_voltages[] = { 3300};
+
+/* EN_3v3_FUSE from AP GPIO VI_D08 L06*/
+static struct regulator_consumer_supply gpio_switch_en_3v3_fuse_supply[] = {
+ REGULATOR_SUPPLY("vdd_fuse", NULL),
+};
+static int gpio_switch_en_3v3_fuse_voltages[] = { 3300};
+
+/* EN_1V8_CAM from AP GPIO GPIO_PBB4 PBB04*/
+static struct regulator_consumer_supply gpio_switch_en_1v8_cam_supply[] = {
+ REGULATOR_SUPPLY("vdd_1v8_cam1", NULL),
+ REGULATOR_SUPPLY("vdd_1v8_cam2", NULL),
+ REGULATOR_SUPPLY("vdd_1v8_cam3", NULL),
+};
+static int gpio_switch_en_1v8_cam_voltages[] = { 1800};
+
+static struct regulator_consumer_supply gpio_switch_en_vbrtr_supply[] = {
+ REGULATOR_SUPPLY("vdd_vbrtr", NULL),
+};
+static int gpio_switch_en_vbrtr_voltages[] = { 3300};
+
+static int enable_load_switch_rail(
+ struct gpio_switch_regulator_subdev_data *psubdev_data)
+{
+ int ret;
+
+ if (psubdev_data->pin_group <= 0)
+ return -EINVAL;
+
+ /* Tristate and make pin as input*/
+ ret = tegra_pinmux_set_tristate(psubdev_data->pin_group,
+ TEGRA_TRI_TRISTATE);
+ if (ret < 0)
+ return ret;
+ return gpio_direction_input(psubdev_data->gpio_nr);
+}
+
+static int disable_load_switch_rail(
+ struct gpio_switch_regulator_subdev_data *psubdev_data)
+{
+ int ret;
+
+ if (psubdev_data->pin_group <= 0)
+ return -EINVAL;
+
+ /* Un-tristate and driver low */
+ ret = tegra_pinmux_set_tristate(psubdev_data->pin_group,
+ TEGRA_TRI_NORMAL);
+ if (ret < 0)
+ return ret;
+ return gpio_direction_output(psubdev_data->gpio_nr, 0);
+}
+
+
+/* Macro for defining gpio switch regulator sub device data */
+#define GREG_INIT(_id, _var, _name, _input_supply, _always_on, _boot_on, \
+ _gpio_nr, _active_low, _init_state, _pg, _enable, _disable) \
+ static struct gpio_switch_regulator_subdev_data gpio_pdata_##_var = \
+ { \
+ .regulator_name = "gpio-switch-"#_name, \
+ .input_supply = _input_supply, \
+ .id = _id, \
+ .gpio_nr = _gpio_nr, \
+ .pin_group = _pg, \
+ .active_low = _active_low, \
+ .init_state = _init_state, \
+ .voltages = gpio_switch_##_name##_voltages, \
+ .n_voltages = ARRAY_SIZE(gpio_switch_##_name##_voltages), \
+ .num_consumer_supplies = \
+ ARRAY_SIZE(gpio_switch_##_name##_supply), \
+ .consumer_supplies = gpio_switch_##_name##_supply, \
+ .constraints = { \
+ .valid_modes_mask = (REGULATOR_MODE_NORMAL | \
+ REGULATOR_MODE_STANDBY), \
+ .valid_ops_mask = (REGULATOR_CHANGE_MODE | \
+ REGULATOR_CHANGE_STATUS | \
+ REGULATOR_CHANGE_VOLTAGE), \
+ .always_on = _always_on, \
+ .boot_on = _boot_on, \
+ }, \
+ .enable_rail = _enable, \
+ .disable_rail = _disable, \
+ }
+
+/* common to most of boards*/
+GREG_INIT(0, en_5v_cp, en_5v_cp, NULL, 1, 0, TPS6591X_GPIO_0, false, 1, 0, 0, 0);
+GREG_INIT(1, en_5v0, en_5v0, NULL, 0, 0, TPS6591X_GPIO_4, false, 0, 0, 0, 0);
+GREG_INIT(2, en_ddr, en_ddr, NULL, 0, 0, TPS6591X_GPIO_3, false, 1, 0, 0, 0);
+GREG_INIT(3, en_3v3_sys, en_3v3_sys, NULL, 0, 0, TPS6591X_GPIO_1, false, 0, 0, 0, 0);
+GREG_INIT(4, en_vdd_bl, en_vdd_bl, NULL, 0, 0, TEGRA_GPIO_PK3, false, 1, 0, 0, 0);
+GREG_INIT(5, en_3v3_modem, en_3v3_modem, NULL, 1, 0, TEGRA_GPIO_PD6, false, 1, 0, 0, 0);
+GREG_INIT(6, en_vdd_pnl1, en_vdd_pnl1, "vdd_3v3_devices", 0, 0, TEGRA_GPIO_PL4, false, 1, 0, 0, 0);
+GREG_INIT(7, cam3_ldo_en, cam3_ldo_en, "vdd_3v3_devices", 0, 0, TEGRA_GPIO_PS0, false, 0, 0, 0, 0);
+GREG_INIT(8, en_vdd_com, en_vdd_com, "vdd_3v3_devices", 1, 0, TEGRA_GPIO_PD0, false, 1, 0, 0, 0);
+GREG_INIT(9, en_3v3_fuse, en_3v3_fuse, "vdd_3v3_devices", 0, 0, TEGRA_GPIO_PL6, false, 0, 0, 0, 0);
+GREG_INIT(10, en_3v3_emmc, en_3v3_emmc, "vdd_3v3_devices", 1, 0, TEGRA_GPIO_PD1, false, 1, 0, 0, 0);
+GREG_INIT(11, en_vdd_sdmmc1, en_vdd_sdmmc1, "vdd_3v3_devices", 0, 0, TEGRA_GPIO_PD7, false, 1, 0, 0, 0);
+GREG_INIT(12, en_3v3_pex_hvdd, en_3v3_pex_hvdd, "hvdd_pex_pmu", 0, 0, TEGRA_GPIO_PL7, false, 0, 0, 0, 0);
+GREG_INIT(13, en_1v8_cam, en_1v8_cam, "vdd_gen1v8", 0, 0, TEGRA_GPIO_PBB4, false, 0, 0, 0, 0);
+
+/*Specific to pm269*/
+GREG_INIT(4, en_vdd_bl_pm269, en_vdd_bl, NULL,
+ 0, 0, TEGRA_GPIO_PH3, false, 1, 0, 0, 0);
+GREG_INIT(6, en_vdd_pnl1_pm269, en_vdd_pnl1, "vdd_3v3_devices",
+ 0, 0, TEGRA_GPIO_PW1, false, 1, 0, 0, 0);
+GREG_INIT(9, en_3v3_fuse_pm269, en_3v3_fuse, "vdd_3v3_devices",
+ 0, 0, TEGRA_GPIO_PC1, false, 0, 0, 0, 0);
+GREG_INIT(11, en_vdd_sdmmc1_pm269, en_vdd_sdmmc1, "vdd_3v3_devices",
+ 0, 0, TEGRA_GPIO_PP1, false, 1, 0, 0, 0);
+GREG_INIT(12, en_3v3_pex_hvdd_pm269, en_3v3_pex_hvdd, "hvdd_pex_pmu",
+ 0, 0, TEGRA_GPIO_PC6, false, 0, 0, 0, 0);
+GREG_INIT(17, en_vddio_vid_oc_pm269, en_vddio_vid_oc, "master_5v_switch",
+ 0, 0, TEGRA_GPIO_PP2, false, 0, TEGRA_PINGROUP_DAP3_DOUT,
+ enable_load_switch_rail, disable_load_switch_rail);
+
+/* Specific to E1187/E1186/E1256 */
+GREG_INIT(14, dis_5v_switch_e118x, dis_5v_switch, "vdd_5v0_sys",
+ 0, 0, TEGRA_GPIO_PX2, true, 0, 0, 0, 0);
+GREG_INIT(15, en_usb1_vbus_oc_e118x, en_usb1_vbus_oc, "master_5v_switch",
+ 0, 0, TEGRA_GPIO_PI4, false, 0, TEGRA_PINGROUP_GMI_RST_N,
+ enable_load_switch_rail, disable_load_switch_rail);
+GREG_INIT(16, en_usb3_vbus_oc_e118x, en_usb3_vbus_oc, "master_5v_switch",
+ 0, 0, TEGRA_GPIO_PH7, false, 0, TEGRA_PINGROUP_GMI_AD15,
+ enable_load_switch_rail, disable_load_switch_rail);
+GREG_INIT(17, en_vddio_vid_oc_e118x, en_vddio_vid_oc, "master_5v_switch",
+ 0, 0, TEGRA_GPIO_PT0, false, 0, TEGRA_PINGROUP_VI_PCLK,
+ enable_load_switch_rail, disable_load_switch_rail);
+
+/* E1198/E1291 specific*/
+GREG_INIT(18, cam1_ldo_en, cam1_ldo_en, "vdd_3v3_cam", 0, 0, TEGRA_GPIO_PR6, false, 0, 0, 0, 0);
+GREG_INIT(19, cam2_ldo_en, cam2_ldo_en, "vdd_3v3_cam", 0, 0, TEGRA_GPIO_PR7, false, 0, 0, 0, 0);
+
+GREG_INIT(22, en_vbrtr, en_vbrtr, "vdd_3v3_devices", 0, 0, PMU_TCA6416_GPIO_PORT12, false, 0, 0, 0, 0);
+
+#define ADD_GPIO_REG(_name) &gpio_pdata_##_name
+
+#define COMMON_GPIO_REG \
+ ADD_GPIO_REG(en_5v_cp), \
+ ADD_GPIO_REG(en_5v0), \
+ ADD_GPIO_REG(en_ddr), \
+ ADD_GPIO_REG(en_3v3_sys), \
+ ADD_GPIO_REG(en_3v3_modem), \
+ ADD_GPIO_REG(en_vdd_pnl1), \
+ ADD_GPIO_REG(cam1_ldo_en), \
+ ADD_GPIO_REG(cam2_ldo_en), \
+ ADD_GPIO_REG(cam3_ldo_en), \
+ ADD_GPIO_REG(en_vdd_com), \
+ ADD_GPIO_REG(en_3v3_fuse), \
+ ADD_GPIO_REG(en_3v3_emmc), \
+ ADD_GPIO_REG(en_vdd_sdmmc1), \
+ ADD_GPIO_REG(en_3v3_pex_hvdd), \
+ ADD_GPIO_REG(en_1v8_cam),
+
+#define PM269_GPIO_REG \
+ ADD_GPIO_REG(en_5v_cp), \
+ ADD_GPIO_REG(en_5v0), \
+ ADD_GPIO_REG(en_ddr), \
+ ADD_GPIO_REG(en_vdd_bl_pm269), \
+ ADD_GPIO_REG(en_3v3_sys), \
+ ADD_GPIO_REG(en_3v3_modem), \
+ ADD_GPIO_REG(en_vdd_pnl1_pm269), \
+ ADD_GPIO_REG(cam1_ldo_en), \
+ ADD_GPIO_REG(cam2_ldo_en), \
+ ADD_GPIO_REG(cam3_ldo_en), \
+ ADD_GPIO_REG(en_vdd_com), \
+ ADD_GPIO_REG(en_3v3_fuse_pm269), \
+ ADD_GPIO_REG(en_3v3_emmc), \
+ ADD_GPIO_REG(en_vdd_sdmmc1_pm269), \
+ ADD_GPIO_REG(en_3v3_pex_hvdd_pm269), \
+ ADD_GPIO_REG(en_1v8_cam), \
+ ADD_GPIO_REG(dis_5v_switch_e118x), \
+ ADD_GPIO_REG(en_usb1_vbus_oc_e118x), \
+ ADD_GPIO_REG(en_usb3_vbus_oc_e118x), \
+ ADD_GPIO_REG(en_vddio_vid_oc_pm269),
+
+#define E118x_GPIO_REG \
+ ADD_GPIO_REG(en_vdd_bl), \
+ ADD_GPIO_REG(dis_5v_switch_e118x), \
+ ADD_GPIO_REG(en_usb1_vbus_oc_e118x), \
+ ADD_GPIO_REG(en_usb3_vbus_oc_e118x), \
+ ADD_GPIO_REG(en_vddio_vid_oc_e118x), \
+ ADD_GPIO_REG(en_vbrtr),
+
+/* Gpio switch regulator platform data for E1186/E1187/E1256*/
+static struct gpio_switch_regulator_subdev_data *gswitch_subdevs_e118x[] = {
+ COMMON_GPIO_REG
+ E118x_GPIO_REG
+};
+
+/* Gpio switch regulator platform data for PM269*/
+static struct gpio_switch_regulator_subdev_data *gswitch_subdevs_pm269[] = {
+ PM269_GPIO_REG
+};
+
+static struct gpio_switch_regulator_platform_data gswitch_pdata;
+static struct platform_device gswitch_regulator_pdata = {
+ .name = "gpio-switch-regulator",
+ .id = -1,
+ .dev = {
+ .platform_data = &gswitch_pdata,
+ },
+};
+
+int __init cardhu_pm299_gpio_switch_regulator_init(void)
+{
+ int i;
+ struct board_info board_info;
+ tegra_get_board_info(&board_info);
+
+ switch (board_info.board_id) {
+ case BOARD_PM269:
+ case BOARD_PM305:
+ case BOARD_PM311:
+ case BOARD_E1257:
+ gswitch_pdata.num_subdevs = ARRAY_SIZE(gswitch_subdevs_pm269);
+ gswitch_pdata.subdevs = gswitch_subdevs_pm269;
+ break;
+
+ default:
+ gswitch_pdata.num_subdevs = ARRAY_SIZE(gswitch_subdevs_e118x);
+ gswitch_pdata.subdevs = gswitch_subdevs_e118x;
+ break;
+ }
+
+ for (i = 0; i < gswitch_pdata.num_subdevs; ++i) {
+ struct gpio_switch_regulator_subdev_data *gswitch_data =
+ gswitch_pdata.subdevs[i];
+ if (gswitch_data->gpio_nr <= TEGRA_NR_GPIOS)
+ tegra_gpio_enable(gswitch_data->gpio_nr);
+ }
+
+ return platform_device_register(&gswitch_regulator_pdata);
+}
diff --git a/arch/arm/mach-tegra/board-cardhu-power.c b/arch/arm/mach-tegra/board-cardhu-power.c
new file mode 100644
index 000000000000..b9aaf897f961
--- /dev/null
+++ b/arch/arm/mach-tegra/board-cardhu-power.c
@@ -0,0 +1,1205 @@
+/*
+ * arch/arm/mach-tegra/board-cardhu-power.c
+ *
+ * Copyright (C) 2011 NVIDIA, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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/i2c.h>
+#include <linux/pda_power.h>
+#include <linux/platform_device.h>
+#include <linux/resource.h>
+#include <linux/regulator/machine.h>
+#include <linux/mfd/tps6591x.h>
+#include <linux/mfd/max77663-core.h>
+#include <linux/gpio.h>
+#include <linux/io.h>
+#include <linux/regulator/gpio-switch-regulator.h>
+#include <linux/regulator/tps6591x-regulator.h>
+#include <linux/regulator/tps6236x-regulator.h>
+#include <linux/power/gpio-charger.h>
+
+#include <asm/mach-types.h>
+
+#include <mach/iomap.h>
+#include <mach/irqs.h>
+#include <mach/pinmux.h>
+#include <mach/edp.h>
+#include <mach/tsensor.h>
+
+#include "gpio-names.h"
+#include "board.h"
+#include "board-cardhu.h"
+#include "pm.h"
+#include "wakeups-t3.h"
+
+#define PMC_CTRL 0x0
+#define PMC_CTRL_INTR_LOW (1 << 17)
+
+static struct regulator_consumer_supply tps6591x_vdd1_supply_skubit0_0[] = {
+ REGULATOR_SUPPLY("vdd_core", NULL),
+ REGULATOR_SUPPLY("en_vddio_ddr_1v2", NULL),
+};
+
+static struct regulator_consumer_supply tps6591x_vdd1_supply_skubit0_1[] = {
+ REGULATOR_SUPPLY("en_vddio_ddr_1v2", NULL),
+};
+
+static struct regulator_consumer_supply tps6591x_vdd2_supply_0[] = {
+ REGULATOR_SUPPLY("vdd_gen1v5", NULL),
+ REGULATOR_SUPPLY("vcore_lcd", NULL),
+ REGULATOR_SUPPLY("track_ldo1", NULL),
+ REGULATOR_SUPPLY("external_ldo_1v2", NULL),
+ REGULATOR_SUPPLY("vcore_cam1", NULL),
+ REGULATOR_SUPPLY("vcore_cam2", NULL),
+};
+
+static struct regulator_consumer_supply tps6591x_vddctrl_supply_0[] = {
+ REGULATOR_SUPPLY("vdd_cpu_pmu", NULL),
+ REGULATOR_SUPPLY("vdd_cpu", NULL),
+ REGULATOR_SUPPLY("vdd_sys", NULL),
+};
+
+static struct regulator_consumer_supply tps6591x_vio_supply_0[] = {
+ REGULATOR_SUPPLY("vdd_gen1v8", NULL),
+ REGULATOR_SUPPLY("avdd_hdmi_pll", NULL),
+ REGULATOR_SUPPLY("avdd_usb_pll", NULL),
+ REGULATOR_SUPPLY("avdd_osc", NULL),
+ REGULATOR_SUPPLY("vddio_sys", NULL),
+ REGULATOR_SUPPLY("vddio_sdmmc", "sdhci-tegra.3"),
+ REGULATOR_SUPPLY("pwrdet_sdmmc4", NULL),
+ REGULATOR_SUPPLY("vdd1v8_satelite", NULL),
+ REGULATOR_SUPPLY("vddio_uart", NULL),
+ REGULATOR_SUPPLY("pwrdet_uart", NULL),
+ REGULATOR_SUPPLY("vddio_audio", NULL),
+ REGULATOR_SUPPLY("pwrdet_audio", NULL),
+ REGULATOR_SUPPLY("vddio_bb", NULL),
+ REGULATOR_SUPPLY("pwrdet_bb", NULL),
+ REGULATOR_SUPPLY("vddio_lcd_pmu", NULL),
+ REGULATOR_SUPPLY("pwrdet_lcd", NULL),
+ REGULATOR_SUPPLY("vddio_cam", NULL),
+ REGULATOR_SUPPLY("pwrdet_cam", NULL),
+ REGULATOR_SUPPLY("vddio_vi", NULL),
+ REGULATOR_SUPPLY("pwrdet_vi", NULL),
+ REGULATOR_SUPPLY("ldo6", NULL),
+ REGULATOR_SUPPLY("ldo7", NULL),
+ REGULATOR_SUPPLY("ldo8", NULL),
+ REGULATOR_SUPPLY("vcore_audio", NULL),
+ REGULATOR_SUPPLY("avcore_audio", NULL),
+ REGULATOR_SUPPLY("vddio_sdmmc", "sdhci-tegra.2"),
+ REGULATOR_SUPPLY("pwrdet_sdmmc3", NULL),
+ REGULATOR_SUPPLY("vcore1_lpddr2", NULL),
+ REGULATOR_SUPPLY("vcom_1v8", NULL),
+ REGULATOR_SUPPLY("pmuio_1v8", NULL),
+ REGULATOR_SUPPLY("avdd_ic_usb", NULL),
+};
+
+static struct regulator_consumer_supply tps6591x_ldo1_supply_0[] = {
+ REGULATOR_SUPPLY("avdd_pexb", NULL),
+ REGULATOR_SUPPLY("vdd_pexb", NULL),
+ REGULATOR_SUPPLY("avdd_pex_pll", NULL),
+ REGULATOR_SUPPLY("avdd_pexa", NULL),
+ REGULATOR_SUPPLY("vdd_pexa", NULL),
+};
+
+static struct regulator_consumer_supply tps6591x_ldo2_supply_0[] = {
+ REGULATOR_SUPPLY("avdd_sata", NULL),
+ REGULATOR_SUPPLY("vdd_sata", NULL),
+ REGULATOR_SUPPLY("avdd_sata_pll", NULL),
+ REGULATOR_SUPPLY("avdd_plle", NULL),
+};
+
+static struct regulator_consumer_supply tps6591x_ldo3_supply_e118x[] = {
+ REGULATOR_SUPPLY("vddio_sdmmc", "sdhci-tegra.0"),
+ REGULATOR_SUPPLY("pwrdet_sdmmc1", NULL),
+};
+
+static struct regulator_consumer_supply tps6591x_ldo3_supply_e1198[] = {
+ REGULATOR_SUPPLY("unused_rail_ldo3", NULL),
+};
+
+static struct regulator_consumer_supply tps6591x_ldo4_supply_0[] = {
+ REGULATOR_SUPPLY("vdd_rtc", NULL),
+};
+
+static struct regulator_consumer_supply tps6591x_ldo5_supply_e118x[] = {
+ REGULATOR_SUPPLY("avdd_vdac", NULL),
+};
+
+static struct regulator_consumer_supply tps6591x_ldo5_supply_e1198[] = {
+ REGULATOR_SUPPLY("avdd_vdac", NULL),
+ REGULATOR_SUPPLY("vddio_sdmmc", "sdhci-tegra.0"),
+ REGULATOR_SUPPLY("pwrdet_sdmmc1", NULL),
+};
+
+static struct regulator_consumer_supply tps6591x_ldo6_supply_0[] = {
+ REGULATOR_SUPPLY("avdd_dsi_csi", NULL),
+ REGULATOR_SUPPLY("pwrdet_mipi", NULL),
+};
+static struct regulator_consumer_supply tps6591x_ldo7_supply_0[] = {
+ REGULATOR_SUPPLY("avdd_plla_p_c_s", NULL),
+ REGULATOR_SUPPLY("avdd_pllm", NULL),
+ REGULATOR_SUPPLY("avdd_pllu_d", NULL),
+ REGULATOR_SUPPLY("avdd_pllu_d2", NULL),
+ REGULATOR_SUPPLY("avdd_pllx", NULL),
+};
+
+static struct regulator_consumer_supply tps6591x_ldo8_supply_0[] = {
+ REGULATOR_SUPPLY("vdd_ddr_hs", NULL),
+};
+
+#define TPS_PDATA_INIT(_name, _sname, _minmv, _maxmv, _supply_reg, _always_on, \
+ _boot_on, _apply_uv, _init_uV, _init_enable, _init_apply, _ectrl, _flags) \
+ static struct tps6591x_regulator_platform_data pdata_##_name##_##_sname = \
+ { \
+ .regulator = { \
+ .constraints = { \
+ .min_uV = (_minmv)*1000, \
+ .max_uV = (_maxmv)*1000, \
+ .valid_modes_mask = (REGULATOR_MODE_NORMAL | \
+ REGULATOR_MODE_STANDBY), \
+ .valid_ops_mask = (REGULATOR_CHANGE_MODE | \
+ REGULATOR_CHANGE_STATUS | \
+ REGULATOR_CHANGE_VOLTAGE), \
+ .always_on = _always_on, \
+ .boot_on = _boot_on, \
+ .apply_uV = _apply_uv, \
+ }, \
+ .num_consumer_supplies = \
+ ARRAY_SIZE(tps6591x_##_name##_supply_##_sname), \
+ .consumer_supplies = tps6591x_##_name##_supply_##_sname, \
+ .supply_regulator = _supply_reg, \
+ }, \
+ .init_uV = _init_uV * 1000, \
+ .init_enable = _init_enable, \
+ .init_apply = _init_apply, \
+ .ectrl = _ectrl, \
+ .flags = _flags, \
+ }
+
+TPS_PDATA_INIT(vdd1, skubit0_0, 600, 1500, 0, 1, 1, 0, -1, 0, 0, EXT_CTRL_SLEEP_OFF, 0);
+TPS_PDATA_INIT(vdd1, skubit0_1, 600, 1500, 0, 1, 1, 0, -1, 0, 0, EXT_CTRL_SLEEP_OFF, 0);
+TPS_PDATA_INIT(vdd2, 0, 600, 1500, 0, 1, 1, 0, -1, 0, 0, 0, 0);
+TPS_PDATA_INIT(vddctrl, 0, 600, 1400, 0, 1, 1, 0, -1, 0, 0, EXT_CTRL_EN1, 0);
+TPS_PDATA_INIT(vio, 0, 1500, 3300, 0, 1, 1, 0, -1, 0, 0, 0, 0);
+
+TPS_PDATA_INIT(ldo1, 0, 1000, 3300, tps6591x_rails(VDD_2), 0, 0, 0, -1, 0, 1, 0, 0);
+TPS_PDATA_INIT(ldo2, 0, 1050, 1050, tps6591x_rails(VDD_2), 0, 0, 1, -1, 0, 1, 0, 0);
+
+TPS_PDATA_INIT(ldo3, e118x, 1000, 3300, 0, 0, 0, 0, -1, 0, 0, 0, 0);
+TPS_PDATA_INIT(ldo3, e1198, 1000, 3300, 0, 0, 0, 0, -1, 0, 0, 0, 0);
+TPS_PDATA_INIT(ldo4, 0, 1000, 3300, 0, 1, 0, 0, -1, 0, 0, 0, 0);
+TPS_PDATA_INIT(ldo5, e118x, 1000, 3300, 0, 0, 0, 0, -1, 0, 0, 0, 0);
+TPS_PDATA_INIT(ldo5, e1198, 1000, 3300, 0, 0, 0, 0, -1, 0, 0, 0, 0);
+
+TPS_PDATA_INIT(ldo6, 0, 1200, 1200, tps6591x_rails(VIO), 0, 0, 1, -1, 0, 0, 0, 0);
+TPS_PDATA_INIT(ldo7, 0, 1200, 1200, tps6591x_rails(VIO), 1, 1, 1, -1, 0, 0, EXT_CTRL_SLEEP_OFF, LDO_LOW_POWER_ON_SUSPEND);
+TPS_PDATA_INIT(ldo8, 0, 1000, 3300, tps6591x_rails(VIO), 1, 0, 0, -1, 0, 0, EXT_CTRL_SLEEP_OFF, LDO_LOW_POWER_ON_SUSPEND);
+
+#if defined(CONFIG_RTC_DRV_TPS6591x)
+static struct tps6591x_rtc_platform_data rtc_data = {
+ .irq = TEGRA_NR_IRQS + TPS6591X_INT_RTC_ALARM,
+ .time = {
+ .tm_year = 2000,
+ .tm_mon = 0,
+ .tm_mday = 1,
+ .tm_hour = 0,
+ .tm_min = 0,
+ .tm_sec = 0,
+ },
+};
+
+#define TPS_RTC_REG() \
+ { \
+ .id = 0, \
+ .name = "rtc_tps6591x", \
+ .platform_data = &rtc_data, \
+ }
+#endif
+
+#define TPS_REG(_id, _name, _sname) \
+ { \
+ .id = TPS6591X_ID_##_id, \
+ .name = "tps6591x-regulator", \
+ .platform_data = &pdata_##_name##_##_sname, \
+ }
+
+#define TPS6591X_DEV_COMMON_E118X \
+ TPS_REG(VDD_2, vdd2, 0), \
+ TPS_REG(VDDCTRL, vddctrl, 0), \
+ TPS_REG(LDO_1, ldo1, 0), \
+ TPS_REG(LDO_2, ldo2, 0), \
+ TPS_REG(LDO_3, ldo3, e118x), \
+ TPS_REG(LDO_4, ldo4, 0), \
+ TPS_REG(LDO_5, ldo5, e118x), \
+ TPS_REG(LDO_6, ldo6, 0), \
+ TPS_REG(LDO_7, ldo7, 0), \
+ TPS_REG(LDO_8, ldo8, 0)
+
+static struct tps6591x_subdev_info tps_devs_e118x_skubit0_0[] = {
+ TPS_REG(VIO, vio, 0),
+ TPS_REG(VDD_1, vdd1, skubit0_0),
+ TPS6591X_DEV_COMMON_E118X,
+#if defined(CONFIG_RTC_DRV_TPS6591x)
+ TPS_RTC_REG(),
+#endif
+};
+
+static struct tps6591x_subdev_info tps_devs_e118x_skubit0_1[] = {
+ TPS_REG(VIO, vio, 0),
+ TPS_REG(VDD_1, vdd1, skubit0_1),
+ TPS6591X_DEV_COMMON_E118X,
+#if defined(CONFIG_RTC_DRV_TPS6591x)
+ TPS_RTC_REG(),
+#endif
+};
+
+#define TPS6591X_DEV_COMMON_CARDHU \
+ TPS_REG(VDD_2, vdd2, 0), \
+ TPS_REG(VDDCTRL, vddctrl, 0), \
+ TPS_REG(LDO_1, ldo1, 0), \
+ TPS_REG(LDO_2, ldo2, 0), \
+ TPS_REG(LDO_3, ldo3, e1198), \
+ TPS_REG(LDO_4, ldo4, 0), \
+ TPS_REG(LDO_5, ldo5, e1198), \
+ TPS_REG(LDO_6, ldo6, 0), \
+ TPS_REG(LDO_7, ldo7, 0), \
+ TPS_REG(LDO_8, ldo8, 0)
+
+static struct tps6591x_subdev_info tps_devs_e1198_skubit0_0[] = {
+ TPS_REG(VIO, vio, 0),
+ TPS_REG(VDD_1, vdd1, skubit0_0),
+ TPS6591X_DEV_COMMON_CARDHU,
+#if defined(CONFIG_RTC_DRV_TPS6591x)
+ TPS_RTC_REG(),
+#endif
+};
+
+static struct tps6591x_subdev_info tps_devs_e1198_skubit0_1[] = {
+ TPS_REG(VIO, vio, 0),
+ TPS_REG(VDD_1, vdd1, skubit0_1),
+ TPS6591X_DEV_COMMON_CARDHU,
+#if defined(CONFIG_RTC_DRV_TPS6591x)
+ TPS_RTC_REG(),
+#endif
+};
+
+#define TPS_GPIO_INIT_PDATA(gpio_nr, _init_apply, _sleep_en, _pulldn_en, _output_en, _output_val) \
+ [gpio_nr] = { \
+ .sleep_en = _sleep_en, \
+ .pulldn_en = _pulldn_en, \
+ .output_mode_en = _output_en, \
+ .output_val = _output_val, \
+ .init_apply = _init_apply, \
+ }
+static struct tps6591x_gpio_init_data tps_gpio_pdata_e1291_a04[] = {
+ TPS_GPIO_INIT_PDATA(0, 0, 0, 0, 0, 0),
+ TPS_GPIO_INIT_PDATA(1, 0, 0, 0, 0, 0),
+ TPS_GPIO_INIT_PDATA(2, 1, 1, 0, 1, 1),
+ TPS_GPIO_INIT_PDATA(3, 0, 0, 0, 0, 0),
+ TPS_GPIO_INIT_PDATA(4, 0, 0, 0, 0, 0),
+ TPS_GPIO_INIT_PDATA(5, 0, 0, 0, 0, 0),
+ TPS_GPIO_INIT_PDATA(6, 0, 0, 0, 0, 0),
+ TPS_GPIO_INIT_PDATA(7, 0, 0, 0, 0, 0),
+ TPS_GPIO_INIT_PDATA(8, 0, 0, 0, 0, 0),
+};
+
+static struct tps6591x_sleep_keepon_data tps_slp_keepon = {
+ .clkout32k_keepon = 1,
+};
+
+static struct tps6591x_platform_data tps_platform = {
+ .irq_base = TPS6591X_IRQ_BASE,
+ .gpio_base = TPS6591X_GPIO_BASE,
+ .dev_slp_en = true,
+ .slp_keepon = &tps_slp_keepon,
+};
+
+static struct i2c_board_info __initdata cardhu_regulators[] = {
+ {
+ I2C_BOARD_INFO("tps6591x", 0x2D),
+ .irq = INT_EXTERNAL_PMU,
+ .platform_data = &tps_platform,
+ },
+};
+
+/* TPS62361B DC-DC converter */
+static struct regulator_consumer_supply tps6236x_dcdc_supply[] = {
+ REGULATOR_SUPPLY("vdd_core", NULL),
+};
+
+static struct tps6236x_regulator_platform_data tps6236x_pdata = {
+ .reg_init_data = { \
+ .constraints = { \
+ .min_uV = 500000, \
+ .max_uV = 1770000, \
+ .valid_modes_mask = (REGULATOR_MODE_NORMAL | \
+ REGULATOR_MODE_STANDBY), \
+ .valid_ops_mask = (REGULATOR_CHANGE_MODE | \
+ REGULATOR_CHANGE_STATUS | \
+ REGULATOR_CHANGE_VOLTAGE), \
+ .always_on = 1, \
+ .boot_on = 1, \
+ .apply_uV = 0, \
+ }, \
+ .num_consumer_supplies = ARRAY_SIZE(tps6236x_dcdc_supply), \
+ .consumer_supplies = tps6236x_dcdc_supply, \
+ }, \
+ .internal_pd_enable = 0, \
+ .enable_discharge = true, \
+ .vsel = 3, \
+ .init_uV = -1, \
+ .init_apply = 0, \
+};
+
+static struct i2c_board_info __initdata tps6236x_boardinfo[] = {
+ {
+ I2C_BOARD_INFO("tps62361B", 0x60),
+ .platform_data = &tps6236x_pdata,
+ },
+};
+
+int __init cardhu_regulator_init(void)
+{
+ struct board_info board_info;
+ struct board_info pmu_board_info;
+ void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE);
+ u32 pmc_ctrl;
+
+ /* configure the power management controller to trigger PMU
+ * interrupts when low */
+
+ pmc_ctrl = readl(pmc + PMC_CTRL);
+ writel(pmc_ctrl | PMC_CTRL_INTR_LOW, pmc + PMC_CTRL);
+
+ tegra_get_board_info(&board_info);
+ tegra_get_pmu_board_info(&pmu_board_info);
+
+ if (pmu_board_info.board_id == BOARD_PMU_PM298)
+ return cardhu_pm298_regulator_init();
+
+ if (pmu_board_info.board_id == BOARD_PMU_PM299)
+ return cardhu_pm299_regulator_init();
+
+ /* The regulator details have complete constraints */
+ regulator_has_full_constraints();
+
+ /* PMU-E1208, the ldo2 should be set to 1200mV */
+ if (pmu_board_info.board_id == BOARD_E1208) {
+ pdata_ldo2_0.regulator.constraints.min_uV = 1200000;
+ pdata_ldo2_0.regulator.constraints.max_uV = 1200000;
+ }
+
+ if ((board_info.board_id == BOARD_E1198) ||
+ (board_info.board_id == BOARD_E1291)) {
+ if (board_info.sku & SKU_DCDC_TPS62361_SUPPORT) {
+ tps_platform.num_subdevs =
+ ARRAY_SIZE(tps_devs_e1198_skubit0_1);
+ tps_platform.subdevs = tps_devs_e1198_skubit0_1;
+ } else {
+ tps_platform.num_subdevs =
+ ARRAY_SIZE(tps_devs_e1198_skubit0_0);
+ tps_platform.subdevs = tps_devs_e1198_skubit0_0;
+ }
+ } else {
+ if (board_info.board_id == BOARD_PM269)
+ pdata_ldo3_e118x.slew_rate_uV_per_us = 250;
+
+ if (pmu_board_info.sku & SKU_DCDC_TPS62361_SUPPORT) {
+ tps_platform.num_subdevs = ARRAY_SIZE(tps_devs_e118x_skubit0_1);
+ tps_platform.subdevs = tps_devs_e118x_skubit0_1;
+ } else {
+ tps_platform.num_subdevs = ARRAY_SIZE(tps_devs_e118x_skubit0_0);
+ tps_platform.subdevs = tps_devs_e118x_skubit0_0;
+ }
+ }
+
+ /* E1291-A04/A05: Enable DEV_SLP and enable sleep on GPIO2 */
+ if ((board_info.board_id == BOARD_E1291) &&
+ ((board_info.fab == BOARD_FAB_A04) ||
+ (board_info.fab == BOARD_FAB_A05))) {
+ tps_platform.dev_slp_en = true;
+ tps_platform.gpio_init_data = tps_gpio_pdata_e1291_a04;
+ tps_platform.num_gpioinit_data =
+ ARRAY_SIZE(tps_gpio_pdata_e1291_a04);
+ }
+
+ i2c_register_board_info(4, cardhu_regulators, 1);
+
+ /* Resgister the TPS6236x for all boards whose sku bit 0 is set. */
+ if ((board_info.sku & SKU_DCDC_TPS62361_SUPPORT) ||
+ (pmu_board_info.sku & SKU_DCDC_TPS62361_SUPPORT)) {
+ pr_info("Registering the device TPS62361B\n");
+ i2c_register_board_info(4, tps6236x_boardinfo, 1);
+ }
+ return 0;
+}
+
+/* EN_5V_CP from PMU GP0 */
+static struct regulator_consumer_supply gpio_switch_en_5v_cp_supply[] = {
+ REGULATOR_SUPPLY("vdd_5v0_sby", NULL),
+ REGULATOR_SUPPLY("vdd_hall", NULL),
+ REGULATOR_SUPPLY("vterm_ddr", NULL),
+ REGULATOR_SUPPLY("v2ref_ddr", NULL),
+};
+static int gpio_switch_en_5v_cp_voltages[] = { 5000};
+
+/* EN_5V0 From PMU GP2 */
+static struct regulator_consumer_supply gpio_switch_en_5v0_supply[] = {
+ REGULATOR_SUPPLY("vdd_5v0_sys", NULL),
+};
+static int gpio_switch_en_5v0_voltages[] = { 5000};
+
+/* EN_DDR From PMU GP6 */
+static struct regulator_consumer_supply gpio_switch_en_ddr_supply[] = {
+ REGULATOR_SUPPLY("mem_vddio_ddr", NULL),
+ REGULATOR_SUPPLY("t30_vddio_ddr", NULL),
+};
+static int gpio_switch_en_ddr_voltages[] = { 1500};
+
+/* EN_3V3_SYS From PMU GP7 */
+static struct regulator_consumer_supply gpio_switch_en_3v3_sys_supply[] = {
+ REGULATOR_SUPPLY("vdd_lvds", NULL),
+ REGULATOR_SUPPLY("vdd_pnl", NULL),
+ REGULATOR_SUPPLY("vcom_3v3", NULL),
+ REGULATOR_SUPPLY("vdd_3v3", NULL),
+ REGULATOR_SUPPLY("vcore_mmc", NULL),
+ REGULATOR_SUPPLY("vddio_pex_ctl", NULL),
+ REGULATOR_SUPPLY("pwrdet_pex_ctl", NULL),
+ REGULATOR_SUPPLY("hvdd_pex_pmu", NULL),
+ REGULATOR_SUPPLY("avdd_hdmi", NULL),
+ REGULATOR_SUPPLY("vpp_fuse", NULL),
+ REGULATOR_SUPPLY("avdd_usb", NULL),
+ REGULATOR_SUPPLY("vdd_ddr_rx", NULL),
+ REGULATOR_SUPPLY("vcore_nand", NULL),
+ REGULATOR_SUPPLY("hvdd_sata", NULL),
+ REGULATOR_SUPPLY("vddio_gmi_pmu", NULL),
+ REGULATOR_SUPPLY("pwrdet_nand", NULL),
+ REGULATOR_SUPPLY("avdd_cam1", NULL),
+ REGULATOR_SUPPLY("vdd_af", NULL),
+ REGULATOR_SUPPLY("avdd_cam2", NULL),
+ REGULATOR_SUPPLY("vdd_acc", NULL),
+ REGULATOR_SUPPLY("vdd_phtl", NULL),
+ REGULATOR_SUPPLY("vddio_tp", NULL),
+ REGULATOR_SUPPLY("vdd_led", NULL),
+ REGULATOR_SUPPLY("vddio_cec", NULL),
+ REGULATOR_SUPPLY("vdd_cmps", NULL),
+ REGULATOR_SUPPLY("vdd_temp", NULL),
+ REGULATOR_SUPPLY("vpp_kfuse", NULL),
+ REGULATOR_SUPPLY("vddio_ts", NULL),
+ REGULATOR_SUPPLY("vdd_ir_led", NULL),
+ REGULATOR_SUPPLY("vddio_1wire", NULL),
+ REGULATOR_SUPPLY("avddio_audio", NULL),
+ REGULATOR_SUPPLY("vdd_ec", NULL),
+ REGULATOR_SUPPLY("vcom_pa", NULL),
+ REGULATOR_SUPPLY("vdd_3v3_devices", NULL),
+ REGULATOR_SUPPLY("vdd_3v3_dock", NULL),
+ REGULATOR_SUPPLY("vdd_3v3_edid", NULL),
+ REGULATOR_SUPPLY("vdd_3v3_hdmi_cec", NULL),
+ REGULATOR_SUPPLY("vdd_3v3_gmi", NULL),
+ REGULATOR_SUPPLY("vdd_spk_amp", "tegra-snd-wm8903"),
+ REGULATOR_SUPPLY("vdd_3v3_sensor", NULL),
+ REGULATOR_SUPPLY("vdd_3v3_cam", NULL),
+ REGULATOR_SUPPLY("vdd_3v3_als", NULL),
+ REGULATOR_SUPPLY("debug_cons", NULL),
+ REGULATOR_SUPPLY("vdd", "4-004c"),
+};
+static int gpio_switch_en_3v3_sys_voltages[] = { 3300};
+
+/* DIS_5V_SWITCH from AP SPI2_SCK X02 */
+static struct regulator_consumer_supply gpio_switch_dis_5v_switch_supply[] = {
+ REGULATOR_SUPPLY("master_5v_switch", NULL),
+};
+static int gpio_switch_dis_5v_switch_voltages[] = { 5000};
+
+/* EN_VDD_BL */
+static struct regulator_consumer_supply gpio_switch_en_vdd_bl_supply[] = {
+ REGULATOR_SUPPLY("vdd_backlight", NULL),
+ REGULATOR_SUPPLY("vdd_backlight1", NULL),
+};
+static int gpio_switch_en_vdd_bl_voltages[] = { 5000};
+
+/* EN_VDD_BL2 (E1291-A03) from AP PEX_L0_PRSNT_N DD.00 */
+static struct regulator_consumer_supply gpio_switch_en_vdd_bl2_supply[] = {
+ REGULATOR_SUPPLY("vdd_backlight2", NULL),
+};
+static int gpio_switch_en_vdd_bl2_voltages[] = { 5000};
+
+/* EN_3V3_MODEM from AP GPIO VI_VSYNCH D06*/
+static struct regulator_consumer_supply gpio_switch_en_3v3_modem_supply[] = {
+ REGULATOR_SUPPLY("vdd_3v3_mini_card", NULL),
+ REGULATOR_SUPPLY("vdd_mini_card", NULL),
+};
+static int gpio_switch_en_3v3_modem_voltages[] = { 3300};
+
+/* EN_USB1_VBUS_OC*/
+static struct regulator_consumer_supply gpio_switch_en_usb1_vbus_oc_supply[] = {
+ REGULATOR_SUPPLY("vdd_vbus_micro_usb", NULL),
+};
+static int gpio_switch_en_usb1_vbus_oc_voltages[] = { 5000};
+
+/*EN_USB3_VBUS_OC*/
+static struct regulator_consumer_supply gpio_switch_en_usb3_vbus_oc_supply[] = {
+ REGULATOR_SUPPLY("vdd_vbus_typea_usb", NULL),
+};
+static int gpio_switch_en_usb3_vbus_oc_voltages[] = { 5000};
+
+/* EN_VDDIO_VID_OC from AP GPIO VI_PCLK T00*/
+static struct regulator_consumer_supply gpio_switch_en_vddio_vid_oc_supply[] = {
+ REGULATOR_SUPPLY("vdd_hdmi_con", NULL),
+};
+static int gpio_switch_en_vddio_vid_oc_voltages[] = { 5000};
+
+/* EN_VDD_PNL1 from AP GPIO VI_D6 L04*/
+static struct regulator_consumer_supply gpio_switch_en_vdd_pnl1_supply[] = {
+ REGULATOR_SUPPLY("vdd_lcd_panel", NULL),
+};
+static int gpio_switch_en_vdd_pnl1_voltages[] = { 3300};
+
+/* CAM1_LDO_EN from AP GPIO KB_ROW6 R06*/
+static struct regulator_consumer_supply gpio_switch_cam1_ldo_en_supply[] = {
+ REGULATOR_SUPPLY("vdd_2v8_cam1", NULL),
+ REGULATOR_SUPPLY("vdd", "6-0072"),
+};
+static int gpio_switch_cam1_ldo_en_voltages[] = { 2800};
+
+/* CAM2_LDO_EN from AP GPIO KB_ROW7 R07*/
+static struct regulator_consumer_supply gpio_switch_cam2_ldo_en_supply[] = {
+ REGULATOR_SUPPLY("vdd_2v8_cam2", NULL),
+ REGULATOR_SUPPLY("vdd", "7-0072"),
+};
+static int gpio_switch_cam2_ldo_en_voltages[] = { 2800};
+
+/* CAM3_LDO_EN from AP GPIO KB_ROW8 S00*/
+static struct regulator_consumer_supply gpio_switch_cam3_ldo_en_supply[] = {
+ REGULATOR_SUPPLY("vdd_cam3", NULL),
+};
+static int gpio_switch_cam3_ldo_en_voltages[] = { 3300};
+
+/* EN_VDD_COM from AP GPIO SDMMC3_DAT5 D00*/
+static struct regulator_consumer_supply gpio_switch_en_vdd_com_supply[] = {
+ REGULATOR_SUPPLY("vdd_com_bd", NULL),
+};
+static int gpio_switch_en_vdd_com_voltages[] = { 3300};
+
+/* EN_VDD_SDMMC1 from AP GPIO VI_HSYNC D07*/
+static struct regulator_consumer_supply gpio_switch_en_vdd_sdmmc1_supply[] = {
+ REGULATOR_SUPPLY("vddio_sd_slot", "sdhci-tegra.0"),
+};
+static int gpio_switch_en_vdd_sdmmc1_voltages[] = { 3300};
+
+/* EN_3V3_EMMC from AP GPIO SDMMC3_DAT4 D01*/
+static struct regulator_consumer_supply gpio_switch_en_3v3_emmc_supply[] = {
+ REGULATOR_SUPPLY("vdd_emmc_core", NULL),
+};
+static int gpio_switch_en_3v3_emmc_voltages[] = { 3300};
+
+/* EN_3V3_PEX_HVDD from AP GPIO VI_D09 L07*/
+static struct regulator_consumer_supply gpio_switch_en_3v3_pex_hvdd_supply[] = {
+ REGULATOR_SUPPLY("hvdd_pex", NULL),
+};
+static int gpio_switch_en_3v3_pex_hvdd_voltages[] = { 3300};
+
+/* EN_3v3_FUSE from AP GPIO VI_D08 L06*/
+static struct regulator_consumer_supply gpio_switch_en_3v3_fuse_supply[] = {
+ REGULATOR_SUPPLY("vdd_fuse", NULL),
+};
+static int gpio_switch_en_3v3_fuse_voltages[] = { 3300};
+
+/* EN_1V8_CAM from AP GPIO GPIO_PBB4 PBB04*/
+static struct regulator_consumer_supply gpio_switch_en_1v8_cam_supply[] = {
+ REGULATOR_SUPPLY("vdd_1v8_cam1", NULL),
+ REGULATOR_SUPPLY("vdd_1v8_cam2", NULL),
+ REGULATOR_SUPPLY("vdd_1v8_cam3", NULL),
+ REGULATOR_SUPPLY("vdd_i2c", "6-0072"),
+ REGULATOR_SUPPLY("vdd_i2c", "7-0072"),
+ REGULATOR_SUPPLY("vdd_i2c", "2-0033"),
+};
+static int gpio_switch_en_1v8_cam_voltages[] = { 1800};
+
+static struct regulator_consumer_supply gpio_switch_en_vbrtr_supply[] = {
+ REGULATOR_SUPPLY("vdd_vbrtr", NULL),
+};
+static int gpio_switch_en_vbrtr_voltages[] = { 3300};
+
+static int enable_load_switch_rail(
+ struct gpio_switch_regulator_subdev_data *psubdev_data)
+{
+ int ret;
+
+ if (psubdev_data->pin_group <= 0)
+ return -EINVAL;
+
+ /* Tristate and make pin as input*/
+ ret = tegra_pinmux_set_tristate(psubdev_data->pin_group,
+ TEGRA_TRI_TRISTATE);
+ if (ret < 0)
+ return ret;
+ return gpio_direction_input(psubdev_data->gpio_nr);
+}
+
+static int disable_load_switch_rail(
+ struct gpio_switch_regulator_subdev_data *psubdev_data)
+{
+ int ret;
+
+ if (psubdev_data->pin_group <= 0)
+ return -EINVAL;
+
+ /* Un-tristate and driver low */
+ ret = tegra_pinmux_set_tristate(psubdev_data->pin_group,
+ TEGRA_TRI_NORMAL);
+ if (ret < 0)
+ return ret;
+ return gpio_direction_output(psubdev_data->gpio_nr, 0);
+}
+
+
+/* Macro for defining gpio switch regulator sub device data */
+#define GREG_INIT(_id, _var, _name, _input_supply, _always_on, _boot_on, \
+ _gpio_nr, _active_low, _init_state, _pg, _enable, _disable) \
+ static struct gpio_switch_regulator_subdev_data gpio_pdata_##_var = \
+ { \
+ .regulator_name = "gpio-switch-"#_name, \
+ .input_supply = _input_supply, \
+ .id = _id, \
+ .gpio_nr = _gpio_nr, \
+ .pin_group = _pg, \
+ .active_low = _active_low, \
+ .init_state = _init_state, \
+ .voltages = gpio_switch_##_name##_voltages, \
+ .n_voltages = ARRAY_SIZE(gpio_switch_##_name##_voltages), \
+ .num_consumer_supplies = \
+ ARRAY_SIZE(gpio_switch_##_name##_supply), \
+ .consumer_supplies = gpio_switch_##_name##_supply, \
+ .constraints = { \
+ .valid_modes_mask = (REGULATOR_MODE_NORMAL | \
+ REGULATOR_MODE_STANDBY), \
+ .valid_ops_mask = (REGULATOR_CHANGE_MODE | \
+ REGULATOR_CHANGE_STATUS | \
+ REGULATOR_CHANGE_VOLTAGE), \
+ .always_on = _always_on, \
+ .boot_on = _boot_on, \
+ }, \
+ .enable_rail = _enable, \
+ .disable_rail = _disable, \
+ }
+
+/* common to most of boards*/
+GREG_INIT(0, en_5v_cp, en_5v_cp, NULL, 1, 0, TPS6591X_GPIO_0, false, 1, 0, 0, 0);
+GREG_INIT(1, en_5v0, en_5v0, NULL, 0, 0, TPS6591X_GPIO_2, false, 0, 0, 0, 0);
+GREG_INIT(2, en_ddr, en_ddr, NULL, 0, 0, TPS6591X_GPIO_6, false, 0, 0, 0, 0);
+GREG_INIT(3, en_3v3_sys, en_3v3_sys, NULL, 0, 0, TPS6591X_GPIO_7, false, 0, 0, 0, 0);
+GREG_INIT(4, en_vdd_bl, en_vdd_bl, NULL, 0, 0, TEGRA_GPIO_PK3, false, 1, 0, 0, 0);
+GREG_INIT(5, en_3v3_modem, en_3v3_modem, NULL, 1, 0, TEGRA_GPIO_PD6, false, 1, 0, 0, 0);
+GREG_INIT(6, en_vdd_pnl1, en_vdd_pnl1, "vdd_3v3_devices", 0, 0, TEGRA_GPIO_PL4, false, 1, 0, 0, 0);
+GREG_INIT(7, cam3_ldo_en, cam3_ldo_en, "vdd_3v3_devices", 0, 0, TEGRA_GPIO_PS0, false, 0, 0, 0, 0);
+GREG_INIT(8, en_vdd_com, en_vdd_com, "vdd_3v3_devices", 1, 0, TEGRA_GPIO_PD0, false, 1, 0, 0, 0);
+GREG_INIT(9, en_3v3_fuse, en_3v3_fuse, "vdd_3v3_devices", 0, 0, TEGRA_GPIO_PL6, false, 0, 0, 0, 0);
+GREG_INIT(10, en_3v3_emmc, en_3v3_emmc, "vdd_3v3_devices", 1, 0, TEGRA_GPIO_PD1, false, 1, 0, 0, 0);
+GREG_INIT(11, en_vdd_sdmmc1, en_vdd_sdmmc1, "vdd_3v3_devices", 0, 0, TEGRA_GPIO_PD7, false, 1, 0, 0, 0);
+GREG_INIT(12, en_3v3_pex_hvdd, en_3v3_pex_hvdd, "hvdd_pex_pmu", 0, 0, TEGRA_GPIO_PL7, false, 0, 0, 0, 0);
+GREG_INIT(13, en_1v8_cam, en_1v8_cam, "vdd_gen1v8", 0, 0, TEGRA_GPIO_PBB4, false, 0, 0, 0, 0);
+
+/* E1291-A04/A05 specific */
+GREG_INIT(1, en_5v0_a04, en_5v0, NULL, 0, 0, TPS6591X_GPIO_8, false, 0, 0, 0, 0);
+GREG_INIT(2, en_ddr_a04, en_ddr, NULL, 0, 0, TPS6591X_GPIO_7, false, 0, 0, 0, 0);
+GREG_INIT(3, en_3v3_sys_a04, en_3v3_sys, NULL, 0, 0, TPS6591X_GPIO_6, false, 0, 0, 0, 0);
+
+
+/*Specific to pm269*/
+GREG_INIT(4, en_vdd_bl_pm269, en_vdd_bl, NULL,
+ 0, 0, TEGRA_GPIO_PH3, false, 1, 0, 0, 0);
+GREG_INIT(6, en_vdd_pnl1_pm269, en_vdd_pnl1, "vdd_3v3_devices",
+ 0, 0, TEGRA_GPIO_PW1, false, 1, 0, 0, 0);
+GREG_INIT(9, en_3v3_fuse_pm269, en_3v3_fuse, "vdd_3v3_devices",
+ 0, 0, TEGRA_GPIO_PC1, false, 0, 0, 0, 0);
+GREG_INIT(12, en_3v3_pex_hvdd_pm269, en_3v3_pex_hvdd, "hvdd_pex_pmu",
+ 0, 0, TEGRA_GPIO_PC6, false, 0, 0, 0, 0);
+GREG_INIT(17, en_vddio_vid_oc_pm269, en_vddio_vid_oc, "master_5v_switch",
+ 0, 0, TEGRA_GPIO_PP2, false, 0, TEGRA_PINGROUP_DAP3_DOUT,
+ enable_load_switch_rail, disable_load_switch_rail);
+
+/* Specific to E1187/E1186/E1256 */
+GREG_INIT(14, dis_5v_switch_e118x, dis_5v_switch, "vdd_5v0_sys",
+ 0, 0, TEGRA_GPIO_PX2, true, 0, 0, 0, 0);
+GREG_INIT(15, en_usb1_vbus_oc_e118x, en_usb1_vbus_oc, "master_5v_switch",
+ 0, 0, TEGRA_GPIO_PI4, false, 0, TEGRA_PINGROUP_GMI_RST_N,
+ enable_load_switch_rail, disable_load_switch_rail);
+GREG_INIT(16, en_usb3_vbus_oc_e118x, en_usb3_vbus_oc, "master_5v_switch",
+ 0, 0, TEGRA_GPIO_PH7, false, 0, TEGRA_PINGROUP_GMI_AD15,
+ enable_load_switch_rail, disable_load_switch_rail);
+GREG_INIT(17, en_vddio_vid_oc_e118x, en_vddio_vid_oc, "master_5v_switch",
+ 0, 0, TEGRA_GPIO_PT0, false, 0, TEGRA_PINGROUP_VI_PCLK,
+ enable_load_switch_rail, disable_load_switch_rail);
+
+/* E1198/E1291 specific fab < A03 */
+GREG_INIT(15, en_usb1_vbus_oc, en_usb1_vbus_oc, "vdd_5v0_sys",
+ 0, 0, TEGRA_GPIO_PI4, false, 0, TEGRA_PINGROUP_GMI_RST_N,
+ enable_load_switch_rail, disable_load_switch_rail);
+GREG_INIT(16, en_usb3_vbus_oc, en_usb3_vbus_oc, "vdd_5v0_sys",
+ 0, 0, TEGRA_GPIO_PH7, false, 0, TEGRA_PINGROUP_GMI_AD15,
+ enable_load_switch_rail, disable_load_switch_rail);
+
+/* E1198/E1291 specific fab >= A03 */
+GREG_INIT(15, en_usb1_vbus_oc_a03, en_usb1_vbus_oc, "vdd_5v0_sys",
+ 0, 0, TEGRA_GPIO_PDD6, false, 0, TEGRA_PINGROUP_PEX_L1_CLKREQ_N,
+ enable_load_switch_rail, disable_load_switch_rail);
+GREG_INIT(16, en_usb3_vbus_oc_a03, en_usb3_vbus_oc, "vdd_5v0_sys",
+ 0, 0, TEGRA_GPIO_PDD4, false, 0, TEGRA_PINGROUP_PEX_L1_PRSNT_N,
+ enable_load_switch_rail, disable_load_switch_rail);
+
+/* E1198/E1291 specific */
+GREG_INIT(17, en_vddio_vid_oc, en_vddio_vid_oc, "vdd_5v0_sys",
+ 0, 0, TEGRA_GPIO_PT0, false, 0, TEGRA_PINGROUP_VI_PCLK,
+ enable_load_switch_rail, disable_load_switch_rail);
+
+/* E1198/E1291 specific*/
+GREG_INIT(18, cam1_ldo_en, cam1_ldo_en, "vdd_3v3_cam", 0, 0, TEGRA_GPIO_PR6, false, 0, 0, 0, 0);
+GREG_INIT(19, cam2_ldo_en, cam2_ldo_en, "vdd_3v3_cam", 0, 0, TEGRA_GPIO_PR7, false, 0, 0, 0, 0);
+
+/* E1291 A03 specific */
+GREG_INIT(20, en_vdd_bl1_a03, en_vdd_bl, NULL, 0, 0, TEGRA_GPIO_PDD2, false, 1, 0, 0, 0);
+GREG_INIT(21, en_vdd_bl2_a03, en_vdd_bl2, NULL, 0, 0, TEGRA_GPIO_PDD0, false, 1, 0, 0, 0);
+
+GREG_INIT(22, en_vbrtr, en_vbrtr, "vdd_3v3_devices", 0, 0, PMU_TCA6416_GPIO_PORT12, false, 0, 0, 0, 0);
+
+/* PM313 display board specific */
+GREG_INIT(4, en_vdd_bl_pm313, en_vdd_bl, NULL,
+ 0, 0, TEGRA_GPIO_PK3, false, 1, 0, 0, 0);
+GREG_INIT(6, en_vdd_pnl1_pm313, en_vdd_pnl1, "vdd_3v3_devices",
+ 0, 0, TEGRA_GPIO_PH3, false, 1, 0, 0, 0);
+
+#define ADD_GPIO_REG(_name) &gpio_pdata_##_name
+
+#define COMMON_GPIO_REG \
+ ADD_GPIO_REG(en_5v_cp), \
+ ADD_GPIO_REG(en_5v0), \
+ ADD_GPIO_REG(en_ddr), \
+ ADD_GPIO_REG(en_3v3_sys), \
+ ADD_GPIO_REG(en_3v3_modem), \
+ ADD_GPIO_REG(en_vdd_pnl1), \
+ ADD_GPIO_REG(cam3_ldo_en), \
+ ADD_GPIO_REG(en_vdd_com), \
+ ADD_GPIO_REG(en_3v3_fuse), \
+ ADD_GPIO_REG(en_3v3_emmc), \
+ ADD_GPIO_REG(en_vdd_sdmmc1), \
+ ADD_GPIO_REG(en_3v3_pex_hvdd), \
+ ADD_GPIO_REG(en_1v8_cam),
+
+#define COMMON_GPIO_REG_E1291_A04 \
+ ADD_GPIO_REG(en_5v_cp), \
+ ADD_GPIO_REG(en_5v0_a04), \
+ ADD_GPIO_REG(en_ddr_a04), \
+ ADD_GPIO_REG(en_3v3_sys_a04), \
+ ADD_GPIO_REG(en_3v3_modem), \
+ ADD_GPIO_REG(en_vdd_pnl1), \
+ ADD_GPIO_REG(cam3_ldo_en), \
+ ADD_GPIO_REG(en_vdd_com), \
+ ADD_GPIO_REG(en_3v3_fuse), \
+ ADD_GPIO_REG(en_3v3_emmc), \
+ ADD_GPIO_REG(en_vdd_sdmmc1), \
+ ADD_GPIO_REG(en_3v3_pex_hvdd), \
+ ADD_GPIO_REG(en_1v8_cam),
+
+#define PM269_GPIO_REG \
+ ADD_GPIO_REG(en_5v_cp), \
+ ADD_GPIO_REG(en_5v0), \
+ ADD_GPIO_REG(en_ddr), \
+ ADD_GPIO_REG(en_3v3_sys), \
+ ADD_GPIO_REG(en_3v3_modem), \
+ ADD_GPIO_REG(cam1_ldo_en), \
+ ADD_GPIO_REG(cam2_ldo_en), \
+ ADD_GPIO_REG(cam3_ldo_en), \
+ ADD_GPIO_REG(en_vdd_com), \
+ ADD_GPIO_REG(en_3v3_fuse_pm269), \
+ ADD_GPIO_REG(en_3v3_emmc), \
+ ADD_GPIO_REG(en_3v3_pex_hvdd_pm269), \
+ ADD_GPIO_REG(en_1v8_cam), \
+ ADD_GPIO_REG(dis_5v_switch_e118x), \
+ ADD_GPIO_REG(en_usb1_vbus_oc_e118x), \
+ ADD_GPIO_REG(en_usb3_vbus_oc_e118x), \
+ ADD_GPIO_REG(en_vddio_vid_oc_pm269),
+
+#define E1247_DISPLAY_GPIO_REG \
+ ADD_GPIO_REG(en_vdd_bl_pm269), \
+ ADD_GPIO_REG(en_vdd_pnl1_pm269),
+
+#define PM313_DISPLAY_GPIO_REG \
+ ADD_GPIO_REG(en_vdd_bl_pm313), \
+ ADD_GPIO_REG(en_vdd_pnl1_pm313),
+
+#define E118x_GPIO_REG \
+ ADD_GPIO_REG(en_5v_cp), \
+ ADD_GPIO_REG(en_5v0), \
+ ADD_GPIO_REG(en_ddr), \
+ ADD_GPIO_REG(en_3v3_sys), \
+ ADD_GPIO_REG(en_3v3_modem), \
+ ADD_GPIO_REG(cam3_ldo_en), \
+ ADD_GPIO_REG(en_vdd_com), \
+ ADD_GPIO_REG(en_3v3_fuse), \
+ ADD_GPIO_REG(en_3v3_emmc), \
+ ADD_GPIO_REG(en_vdd_sdmmc1), \
+ ADD_GPIO_REG(en_3v3_pex_hvdd), \
+ ADD_GPIO_REG(en_1v8_cam), \
+ ADD_GPIO_REG(dis_5v_switch_e118x), \
+ ADD_GPIO_REG(en_usb1_vbus_oc_e118x), \
+ ADD_GPIO_REG(en_usb3_vbus_oc_e118x), \
+ ADD_GPIO_REG(en_vddio_vid_oc_e118x), \
+ ADD_GPIO_REG(en_vbrtr),
+
+#define E1198_GPIO_REG \
+ ADD_GPIO_REG(en_vddio_vid_oc), \
+ ADD_GPIO_REG(cam1_ldo_en), \
+ ADD_GPIO_REG(cam2_ldo_en),
+
+#define E1291_1198_A00_GPIO_REG \
+ ADD_GPIO_REG(en_usb1_vbus_oc), \
+ ADD_GPIO_REG(en_usb3_vbus_oc), \
+ ADD_GPIO_REG(en_vdd_bl),
+
+#define E1291_A03_GPIO_REG \
+ ADD_GPIO_REG(en_usb1_vbus_oc_a03), \
+ ADD_GPIO_REG(en_usb3_vbus_oc_a03), \
+ ADD_GPIO_REG(en_vdd_bl1_a03), \
+ ADD_GPIO_REG(en_vdd_bl2_a03),
+
+/* Gpio switch regulator platform data for E1186/E1187/E1256*/
+static struct gpio_switch_regulator_subdev_data *gswitch_subdevs_e118x[] = {
+ E118x_GPIO_REG
+ E1247_DISPLAY_GPIO_REG
+};
+
+/* Gpio switch regulator platform data for E1186/E1187/E1256*/
+static struct gpio_switch_regulator_subdev_data *gswitch_subdevs_e118x_pm313[] = {
+ E118x_GPIO_REG
+ PM313_DISPLAY_GPIO_REG
+};
+
+/* Gpio switch regulator platform data for E1198 and E1291*/
+static struct gpio_switch_regulator_subdev_data *gswitch_subdevs_e1198_base[] = {
+ COMMON_GPIO_REG
+ E1291_1198_A00_GPIO_REG
+ E1198_GPIO_REG
+};
+
+static struct gpio_switch_regulator_subdev_data *gswitch_subdevs_e1198_a02[] = {
+ ADD_GPIO_REG(en_5v_cp),
+ ADD_GPIO_REG(en_5v0),
+ ADD_GPIO_REG(en_ddr_a04),
+ ADD_GPIO_REG(en_3v3_sys_a04),
+ ADD_GPIO_REG(en_3v3_modem),
+ ADD_GPIO_REG(en_vdd_pnl1),
+ ADD_GPIO_REG(cam3_ldo_en),
+ ADD_GPIO_REG(en_vdd_com),
+ ADD_GPIO_REG(en_3v3_fuse),
+ ADD_GPIO_REG(en_3v3_emmc),
+ ADD_GPIO_REG(en_vdd_sdmmc1),
+ ADD_GPIO_REG(en_3v3_pex_hvdd),
+ ADD_GPIO_REG(en_1v8_cam),
+ ADD_GPIO_REG(en_usb1_vbus_oc_a03),
+ ADD_GPIO_REG(en_usb3_vbus_oc_a03),
+ ADD_GPIO_REG(en_vdd_bl1_a03),
+ ADD_GPIO_REG(en_vdd_bl2_a03),
+ ADD_GPIO_REG(en_vddio_vid_oc),
+ ADD_GPIO_REG(cam1_ldo_en),
+ ADD_GPIO_REG(cam2_ldo_en),
+};
+
+/* Gpio switch regulator platform data for PM269*/
+static struct gpio_switch_regulator_subdev_data *gswitch_subdevs_pm269[] = {
+ PM269_GPIO_REG
+ E1247_DISPLAY_GPIO_REG
+};
+
+/* Gpio switch regulator platform data for PM269*/
+static struct gpio_switch_regulator_subdev_data *gswitch_subdevs_pm269_pm313[] = {
+ PM269_GPIO_REG
+ PM313_DISPLAY_GPIO_REG
+};
+
+/* Gpio switch regulator platform data for E1291 A03*/
+static struct gpio_switch_regulator_subdev_data *gswitch_subdevs_e1291_a03[] = {
+ COMMON_GPIO_REG
+ E1291_A03_GPIO_REG
+ E1198_GPIO_REG
+};
+
+/* Gpio switch regulator platform data for E1291 A04/A05*/
+static struct gpio_switch_regulator_subdev_data *gswitch_subdevs_e1291_a04[] = {
+ COMMON_GPIO_REG_E1291_A04
+ E1291_A03_GPIO_REG
+ E1198_GPIO_REG
+};
+
+
+static struct gpio_switch_regulator_platform_data gswitch_pdata;
+static struct platform_device gswitch_regulator_pdata = {
+ .name = "gpio-switch-regulator",
+ .id = -1,
+ .dev = {
+ .platform_data = &gswitch_pdata,
+ },
+};
+
+int __init cardhu_gpio_switch_regulator_init(void)
+{
+ int i;
+ struct board_info board_info;
+ struct board_info pmu_board_info;
+ struct board_info display_board_info;
+
+ tegra_get_board_info(&board_info);
+ tegra_get_pmu_board_info(&pmu_board_info);
+ tegra_get_display_board_info(&display_board_info);
+
+ if (pmu_board_info.board_id == BOARD_PMU_PM298)
+ return cardhu_pm298_gpio_switch_regulator_init();
+
+ if (pmu_board_info.board_id == BOARD_PMU_PM299)
+ return cardhu_pm299_gpio_switch_regulator_init();
+
+ switch (board_info.board_id) {
+ case BOARD_E1198:
+ if (board_info.fab <= BOARD_FAB_A01) {
+ gswitch_pdata.num_subdevs = ARRAY_SIZE(gswitch_subdevs_e1198_base);
+ gswitch_pdata.subdevs = gswitch_subdevs_e1198_base;
+ } else {
+ gswitch_pdata.num_subdevs = ARRAY_SIZE(gswitch_subdevs_e1198_a02);
+ gswitch_pdata.subdevs = gswitch_subdevs_e1198_a02;
+ }
+ break;
+
+ case BOARD_E1291:
+ if (board_info.fab == BOARD_FAB_A03) {
+ gswitch_pdata.num_subdevs =
+ ARRAY_SIZE(gswitch_subdevs_e1291_a03);
+ gswitch_pdata.subdevs = gswitch_subdevs_e1291_a03;
+ } else if ((board_info.fab == BOARD_FAB_A04) ||
+ (board_info.fab == BOARD_FAB_A05)) {
+ gswitch_pdata.num_subdevs =
+ ARRAY_SIZE(gswitch_subdevs_e1291_a04);
+ gswitch_pdata.subdevs = gswitch_subdevs_e1291_a04;
+ } else {
+ gswitch_pdata.num_subdevs =
+ ARRAY_SIZE(gswitch_subdevs_e1198_base);
+ gswitch_pdata.subdevs = gswitch_subdevs_e1198_base;
+ }
+ break;
+
+ case BOARD_PM269:
+ case BOARD_PM305:
+ case BOARD_PM311:
+ case BOARD_E1257:
+ gswitch_pdata.num_subdevs = ARRAY_SIZE(gswitch_subdevs_pm269);
+ gswitch_pdata.subdevs = gswitch_subdevs_pm269;
+ if (display_board_info.board_id == BOARD_DISPLAY_PM313) {
+ gswitch_pdata.num_subdevs = ARRAY_SIZE(gswitch_subdevs_pm269_pm313);
+ gswitch_pdata.subdevs = gswitch_subdevs_pm269_pm313;
+ } else {
+ gswitch_pdata.num_subdevs = ARRAY_SIZE(gswitch_subdevs_pm269);
+ gswitch_pdata.subdevs = gswitch_subdevs_pm269;
+ }
+ break;
+ default:
+ if (display_board_info.board_id == BOARD_DISPLAY_PM313) {
+ gswitch_pdata.num_subdevs = ARRAY_SIZE(gswitch_subdevs_e118x_pm313);
+ gswitch_pdata.subdevs = gswitch_subdevs_e118x_pm313;
+ } else {
+ gswitch_pdata.num_subdevs = ARRAY_SIZE(gswitch_subdevs_e118x);
+ gswitch_pdata.subdevs = gswitch_subdevs_e118x;
+ }
+ break;
+ }
+
+ for (i = 0; i < gswitch_pdata.num_subdevs; ++i) {
+ struct gpio_switch_regulator_subdev_data *gswitch_data = gswitch_pdata.subdevs[i];
+ if (gswitch_data->gpio_nr < TEGRA_NR_GPIOS)
+ tegra_gpio_enable(gswitch_data->gpio_nr);
+ }
+
+ return platform_device_register(&gswitch_regulator_pdata);
+}
+
+static void cardhu_board_suspend(int lp_state, enum suspend_stage stg)
+{
+ if ((lp_state == TEGRA_SUSPEND_LP1) && (stg == TEGRA_SUSPEND_BEFORE_CPU))
+ tegra_console_uart_suspend();
+}
+
+static void cardhu_board_resume(int lp_state, enum resume_stage stg)
+{
+ if ((lp_state == TEGRA_SUSPEND_LP1) && (stg == TEGRA_RESUME_AFTER_CPU))
+ tegra_console_uart_resume();
+}
+
+static struct tegra_suspend_platform_data cardhu_suspend_data = {
+ .cpu_timer = 2000,
+ .cpu_off_timer = 200,
+ .suspend_mode = TEGRA_SUSPEND_LP0,
+ .core_timer = 0x7e7e,
+ .core_off_timer = 0,
+ .corereq_high = true,
+ .sysclkreq_high = true,
+ .cpu_lp2_min_residency = 2000,
+ .board_suspend = cardhu_board_suspend,
+ .board_resume = cardhu_board_resume,
+};
+
+int __init cardhu_suspend_init(void)
+{
+ struct board_info board_info;
+ struct board_info pmu_board_info;
+
+ tegra_get_board_info(&board_info);
+ tegra_get_pmu_board_info(&pmu_board_info);
+
+ /* For PMU Fab A03, A04 and A05 make core_pwr_req to high */
+ if ((pmu_board_info.fab == BOARD_FAB_A03) ||
+ (pmu_board_info.fab == BOARD_FAB_A04) ||
+ (pmu_board_info.fab == BOARD_FAB_A05))
+ cardhu_suspend_data.corereq_high = true;
+
+ /* CORE_PWR_REQ to be high for all processor/pmu board whose sku bit 0
+ * is set. This is require to enable the dc-dc converter tps62361x */
+ if ((board_info.sku & SKU_DCDC_TPS62361_SUPPORT) || (pmu_board_info.sku & SKU_DCDC_TPS62361_SUPPORT))
+ cardhu_suspend_data.corereq_high = true;
+
+ switch (board_info.board_id) {
+ case BOARD_E1291:
+ /* CORE_PWR_REQ to be high for E1291-A03 */
+ if (board_info.fab == BOARD_FAB_A03)
+ cardhu_suspend_data.corereq_high = true;
+ break;
+ case BOARD_E1198:
+ case BOARD_PM269:
+ case BOARD_PM305:
+ case BOARD_PM311:
+ break;
+ case BOARD_E1187:
+ case BOARD_E1186:
+ case BOARD_E1256:
+ case BOARD_E1257:
+ cardhu_suspend_data.cpu_timer = 5000;
+ cardhu_suspend_data.cpu_off_timer = 5000;
+ break;
+ default:
+ break;
+ }
+
+ tegra_init_suspend(&cardhu_suspend_data);
+ return 0;
+}
+
+static void cardhu_power_off(void)
+{
+ int ret;
+ pr_err("cardhu: Powering off the device\n");
+ ret = tps6591x_power_off();
+ if (ret)
+ pr_err("cardhu: failed to power off\n");
+
+ while (1);
+}
+
+static void cardhu_pm298_power_off(void)
+{
+ int ret;
+ pr_err("cardhu-pm298: Powering off the device\n");
+ ret = max77663_power_off();
+ if (ret)
+ pr_err("cardhu-pm298: failed to power off\n");
+
+ while (1);
+}
+
+int __init cardhu_power_off_init(void)
+{
+ struct board_info pmu_board_info;
+
+ tegra_get_pmu_board_info(&pmu_board_info);
+
+ if (pmu_board_info.board_id == BOARD_PMU_PM298)
+ pm_power_off = cardhu_pm298_power_off;
+ else
+ pm_power_off = cardhu_power_off;
+
+ return 0;
+}
+
+static struct tegra_tsensor_pmu_data tpdata = {
+ .poweroff_reg_addr = 0x3F,
+ .poweroff_reg_data = 0x80,
+ .reset_tegra = 1,
+ .controller_type = 0,
+ .i2c_controller_id = 4,
+ .pinmux = 0,
+ .pmu_16bit_ops = 0,
+ .pmu_i2c_addr = 0x2D,
+};
+
+void __init cardhu_tsensor_init(void)
+{
+ tegra3_tsensor_init(&tpdata);
+}
+
+#ifdef CONFIG_TEGRA_EDP_LIMITS
+
+int __init cardhu_edp_init(void)
+{
+ unsigned int regulator_mA;
+
+ regulator_mA = get_maximum_cpu_current_supported();
+ if (!regulator_mA) {
+ regulator_mA = 5000; /* regular T30/s */
+ }
+ pr_info("%s: CPU regulator %d mA\n", __func__, regulator_mA);
+
+ tegra_init_cpu_edp_limits(regulator_mA);
+ return 0;
+}
+#endif
+
+static char *cardhu_battery[] = {
+ "bq27510-0",
+};
+
+static struct gpio_charger_platform_data cardhu_charger_pdata = {
+ .name = "ac",
+ .type = POWER_SUPPLY_TYPE_MAINS,
+ .gpio = AC_PRESENT_GPIO,
+ .gpio_active_low = 0,
+ .supplied_to = cardhu_battery,
+ .num_supplicants = ARRAY_SIZE(cardhu_battery),
+};
+
+static struct platform_device cardhu_charger_device = {
+ .name = "gpio-charger",
+ .dev = {
+ .platform_data = &cardhu_charger_pdata,
+ },
+};
+
+static int __init cardhu_charger_late_init(void)
+{
+ if (!machine_is_cardhu())
+ return 0;
+
+ platform_device_register(&cardhu_charger_device);
+ return 0;
+}
+
+late_initcall(cardhu_charger_late_init);
+
diff --git a/arch/arm/mach-tegra/board-cardhu-powermon.c b/arch/arm/mach-tegra/board-cardhu-powermon.c
new file mode 100644
index 000000000000..a637b68286a5
--- /dev/null
+++ b/arch/arm/mach-tegra/board-cardhu-powermon.c
@@ -0,0 +1,256 @@
+/*
+ * arch/arm/mach-tegra/board-cardhu-powermon.c
+ *
+ * Copyright (c) 2011, NVIDIA, All Rights Reserved.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 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/i2c.h>
+#include <linux/ina219.h>
+
+#include "board.h"
+#include "board-cardhu.h"
+
+enum {
+ VDD_AC_BAT,
+ VDD_DRAM_IN,
+ VDD_BACKLIGHT_IN,
+ VDD_CPU_IN,
+ VDD_CORE_IN,
+ VDD_DISPLAY_IN,
+ VDD_3V3_TEGRA,
+ VDD_OTHER_PMU_IN,
+ VDD_1V8_TEGRA,
+ VDD_1V8_OTHER,
+ UNUSED_RAIL,
+};
+
+static struct ina219_platform_data power_mon_info[] = {
+ [VDD_AC_BAT] = {
+ .calibration_data = 0xa000,
+ .power_lsb = 2,
+ .rail_name = "VDD_AC_BAT",
+ .divisor = 20,
+ },
+
+ [VDD_DRAM_IN] = {
+ .calibration_data = 0xa000,
+ .power_lsb = 2,
+ .rail_name = "VDD_DRAM_IN",
+ .divisor = 20,
+ },
+
+ [VDD_BACKLIGHT_IN] = {
+ .calibration_data = 0x6aaa,
+ .power_lsb = 1,
+ .rail_name = "VDD_BACKLIGHT_IN",
+ .divisor = 20,
+ },
+
+ [VDD_CPU_IN] = {
+ .calibration_data = 0xa000,
+ .power_lsb = 1,
+ .rail_name = "VDD_CPU_IN",
+ .divisor = 20,
+ },
+
+ [VDD_CORE_IN] = {
+ .calibration_data = 0x6aaa,
+ .power_lsb = 1,
+ .rail_name = "VDD_CORE_IN",
+ .divisor = 20,
+ },
+
+ [VDD_DISPLAY_IN] = {
+ .calibration_data = 0x4000,
+ .power_lsb = 1,
+ .rail_name = "VDD_DISPLAY_IN",
+ .divisor = 20,
+ },
+
+ [VDD_3V3_TEGRA] = {
+ .calibration_data = 0x6aaa,
+ .power_lsb = 1,
+ .rail_name = "VDD_3V3_TEGRA",
+ .divisor = 20,
+ },
+
+ [VDD_OTHER_PMU_IN] = {
+ .calibration_data = 0xa000,
+ .power_lsb = 1,
+ .rail_name = "VDD_OTHER_PMU_IN",
+ .divisor = 20,
+ },
+
+ [VDD_1V8_TEGRA] = {
+ .calibration_data = 0x4000,
+ .power_lsb = 1,
+ .rail_name = "VDD_1V8_TEGRA",
+ .divisor = 20,
+ },
+
+ [VDD_1V8_OTHER] = {
+ .calibration_data = 0xa000,
+ .power_lsb = 1,
+ .rail_name = "VDD_1V8_OTHER",
+ .divisor = 20,
+ },
+
+ /* All unused INA219 devices use below data*/
+ [UNUSED_RAIL] = {
+ .calibration_data = 0x4000,
+ .power_lsb = 1,
+ .rail_name = "unused_rail",
+ .divisor = 20,
+ },
+};
+
+enum {
+ INA_I2C_ADDR_40,
+ INA_I2C_ADDR_41,
+ INA_I2C_ADDR_42,
+ INA_I2C_ADDR_43,
+ INA_I2C_ADDR_44,
+ INA_I2C_ADDR_45,
+ INA_I2C_ADDR_46,
+ INA_I2C_ADDR_47,
+ INA_I2C_ADDR_48,
+ INA_I2C_ADDR_49,
+ INA_I2C_ADDR_4A,
+ INA_I2C_ADDR_4B,
+ INA_I2C_ADDR_4C,
+ INA_I2C_ADDR_4D,
+ INA_I2C_ADDR_4E,
+ INA_I2C_ADDR_4F,
+};
+
+static struct i2c_board_info cardhu_i2c0_ina219_board_info[] = {
+ [INA_I2C_ADDR_40] = {
+ I2C_BOARD_INFO("ina219", 0x40),
+ .platform_data = &power_mon_info[VDD_AC_BAT],
+ .irq = -1,
+ },
+
+ [INA_I2C_ADDR_41] = {
+ I2C_BOARD_INFO("ina219", 0x41),
+ .platform_data = &power_mon_info[VDD_DRAM_IN],
+ .irq = -1,
+ },
+
+ [INA_I2C_ADDR_42] = {
+ I2C_BOARD_INFO("ina219", 0x42),
+ .platform_data = &power_mon_info[VDD_BACKLIGHT_IN],
+ .irq = -1,
+ },
+
+ [INA_I2C_ADDR_43] = {
+ I2C_BOARD_INFO("ina219", 0x43),
+ .platform_data = &power_mon_info[VDD_CPU_IN],
+ .irq = -1,
+ },
+
+ [INA_I2C_ADDR_44] = {
+ I2C_BOARD_INFO("ina219", 0x44),
+ .platform_data = &power_mon_info[VDD_CORE_IN],
+ .irq = -1,
+ },
+
+ [INA_I2C_ADDR_45] = {
+ I2C_BOARD_INFO("ina219", 0x45),
+ .platform_data = &power_mon_info[VDD_DISPLAY_IN],
+ .irq = -1,
+ },
+
+ [INA_I2C_ADDR_46] = {
+ I2C_BOARD_INFO("ina219", 0x46),
+ .platform_data = &power_mon_info[VDD_3V3_TEGRA],
+ .irq = -1,
+ },
+
+ [INA_I2C_ADDR_47] = {
+ I2C_BOARD_INFO("ina219", 0x47),
+ .platform_data = &power_mon_info[VDD_OTHER_PMU_IN],
+ .irq = -1,
+ },
+
+ [INA_I2C_ADDR_48] = {
+ I2C_BOARD_INFO("ina219", 0x48),
+ .platform_data = &power_mon_info[VDD_1V8_TEGRA],
+ .irq = -1,
+ },
+
+ [INA_I2C_ADDR_49] = {
+ I2C_BOARD_INFO("ina219", 0x49),
+ .platform_data = &power_mon_info[VDD_1V8_OTHER],
+ .irq = -1,
+ },
+
+ [INA_I2C_ADDR_4A] = {
+ I2C_BOARD_INFO("ina219", 0x4A),
+ .platform_data = &power_mon_info[UNUSED_RAIL],
+ .irq = -1,
+ },
+
+ [INA_I2C_ADDR_4B] = {
+ I2C_BOARD_INFO("ina219", 0x4B),
+ .platform_data = &power_mon_info[UNUSED_RAIL],
+ .irq = -1,
+ },
+
+ [INA_I2C_ADDR_4C] = {
+ I2C_BOARD_INFO("ina219", 0x4C),
+ .platform_data = &power_mon_info[UNUSED_RAIL],
+ .irq = -1,
+ },
+
+ [INA_I2C_ADDR_4D] = {
+ I2C_BOARD_INFO("ina219", 0x4D),
+ .platform_data = &power_mon_info[UNUSED_RAIL],
+ .irq = -1,
+ },
+
+ [INA_I2C_ADDR_4E] = {
+ I2C_BOARD_INFO("ina219", 0x4E),
+ .platform_data = &power_mon_info[UNUSED_RAIL],
+ .irq = -1,
+ },
+
+ [INA_I2C_ADDR_4F] = {
+ I2C_BOARD_INFO("ina219", 0x4F),
+ .platform_data = &power_mon_info[UNUSED_RAIL],
+ .irq = -1,
+ },
+};
+
+int __init cardhu_pmon_init(void)
+{
+ struct board_info bi;
+
+ tegra_get_board_info(&bi);
+
+ /* for fab A04 VDD_CORE_IN changed from ina with addr 0x44 to 0x4A */
+ if (bi.fab == BOARD_FAB_A04) {
+ cardhu_i2c0_ina219_board_info[INA_I2C_ADDR_44].platform_data =
+ &power_mon_info[UNUSED_RAIL];
+ cardhu_i2c0_ina219_board_info[INA_I2C_ADDR_4A].platform_data =
+ &power_mon_info[VDD_CORE_IN];
+ }
+
+ i2c_register_board_info(0, cardhu_i2c0_ina219_board_info,
+ ARRAY_SIZE(cardhu_i2c0_ina219_board_info));
+ return 0;
+}
+
diff --git a/arch/arm/mach-tegra/board-cardhu-sdhci.c b/arch/arm/mach-tegra/board-cardhu-sdhci.c
new file mode 100644
index 000000000000..d631a1525c04
--- /dev/null
+++ b/arch/arm/mach-tegra/board-cardhu-sdhci.c
@@ -0,0 +1,299 @@
+/*
+ * arch/arm/mach-tegra/board-harmony-sdhci.c
+ *
+ * Copyright (C) 2010 Google, Inc.
+ * Copyright (C) 2011 NVIDIA Corporation.
+ *
+ * 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/resource.h>
+#include <linux/platform_device.h>
+#include <linux/wlan_plat.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/mmc/host.h>
+
+#include <asm/mach-types.h>
+#include <mach/irqs.h>
+#include <mach/iomap.h>
+#include <mach/sdhci.h>
+
+#include "gpio-names.h"
+#include "board.h"
+#include "board-cardhu.h"
+
+#define CARDHU_WLAN_PWR TEGRA_GPIO_PD4
+#define CARDHU_WLAN_RST TEGRA_GPIO_PD3
+#define CARDHU_WLAN_WOW TEGRA_GPIO_PO4
+#define CARDHU_SD_CD TEGRA_GPIO_PI5
+#define CARDHU_SD_WP TEGRA_GPIO_PT3
+#define PM269_SD_WP -1
+
+static void (*wifi_status_cb)(int card_present, void *dev_id);
+static void *wifi_status_cb_devid;
+static int cardhu_wifi_status_register(void (*callback)(int , void *), void *);
+
+static int cardhu_wifi_reset(int on);
+static int cardhu_wifi_power(int on);
+static int cardhu_wifi_set_carddetect(int val);
+
+static struct wifi_platform_data cardhu_wifi_control = {
+ .set_power = cardhu_wifi_power,
+ .set_reset = cardhu_wifi_reset,
+ .set_carddetect = cardhu_wifi_set_carddetect,
+};
+
+static struct resource wifi_resource[] = {
+ [0] = {
+ .name = "bcm4329_wlan_irq",
+ .start = TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PO4),
+ .end = TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PO4),
+ .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL | IORESOURCE_IRQ_SHAREABLE,
+ },
+};
+
+static struct platform_device cardhu_wifi_device = {
+ .name = "bcm4329_wlan",
+ .id = 1,
+ .num_resources = 1,
+ .resource = wifi_resource,
+ .dev = {
+ .platform_data = &cardhu_wifi_control,
+ },
+};
+
+static struct resource sdhci_resource0[] = {
+ [0] = {
+ .start = INT_SDMMC1,
+ .end = INT_SDMMC1,
+ .flags = IORESOURCE_IRQ,
+ },
+ [1] = {
+ .start = TEGRA_SDMMC1_BASE,
+ .end = TEGRA_SDMMC1_BASE + TEGRA_SDMMC1_SIZE-1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct resource sdhci_resource2[] = {
+ [0] = {
+ .start = INT_SDMMC3,
+ .end = INT_SDMMC3,
+ .flags = IORESOURCE_IRQ,
+ },
+ [1] = {
+ .start = TEGRA_SDMMC3_BASE,
+ .end = TEGRA_SDMMC3_BASE + TEGRA_SDMMC3_SIZE-1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct resource sdhci_resource3[] = {
+ [0] = {
+ .start = INT_SDMMC4,
+ .end = INT_SDMMC4,
+ .flags = IORESOURCE_IRQ,
+ },
+ [1] = {
+ .start = TEGRA_SDMMC4_BASE,
+ .end = TEGRA_SDMMC4_BASE + TEGRA_SDMMC4_SIZE-1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct embedded_sdio_data embedded_sdio_data2 = {
+ .cccr = {
+ .sdio_vsn = 2,
+ .multi_block = 1,
+ .low_speed = 0,
+ .wide_bus = 0,
+ .high_power = 1,
+ .high_speed = 1,
+ },
+ .cis = {
+ .vendor = 0x02d0,
+ .device = 0x4329,
+ },
+};
+
+static struct tegra_sdhci_platform_data tegra_sdhci_platform_data2 = {
+ .mmc_data = {
+ .register_status_notify = cardhu_wifi_status_register,
+ .embedded_sdio = &embedded_sdio_data2,
+ .built_in = 1,
+ },
+ .cd_gpio = -1,
+ .wp_gpio = -1,
+ .power_gpio = -1,
+/* .tap_delay = 6,
+ .is_voltage_switch_supported = false,
+ .vdd_rail_name = NULL,
+ .slot_rail_name = NULL,
+ .vdd_max_uv = -1,
+ .vdd_min_uv = -1,
+ .max_clk = 0,
+ .is_8bit_supported = false, */
+};
+
+static struct tegra_sdhci_platform_data tegra_sdhci_platform_data0 = {
+ .cd_gpio = CARDHU_SD_CD,
+ .wp_gpio = CARDHU_SD_WP,
+ .power_gpio = -1,
+/* .tap_delay = 6,
+ .is_voltage_switch_supported = true,
+ .vdd_rail_name = "vddio_sdmmc1",
+ .slot_rail_name = "vddio_sd_slot",
+ .vdd_max_uv = 3320000,
+ .vdd_min_uv = 3280000,
+ .max_clk = 208000000,
+ .is_8bit_supported = false, */
+};
+
+static struct tegra_sdhci_platform_data tegra_sdhci_platform_data3 = {
+ .cd_gpio = -1,
+ .wp_gpio = -1,
+ .power_gpio = -1,
+ .is_8bit = 1,
+ .mmc_data = {
+ .built_in = 1,
+ }
+/* .tap_delay = 6,
+ .is_voltage_switch_supported = false,
+ .vdd_rail_name = NULL,
+ .slot_rail_name = NULL,
+ .vdd_max_uv = -1,
+ .vdd_min_uv = -1,
+ .max_clk = 48000000,
+ .is_8bit_supported = true, */
+};
+
+static struct platform_device tegra_sdhci_device0 = {
+ .name = "sdhci-tegra",
+ .id = 0,
+ .resource = sdhci_resource0,
+ .num_resources = ARRAY_SIZE(sdhci_resource0),
+ .dev = {
+ .platform_data = &tegra_sdhci_platform_data0,
+ },
+};
+
+static struct platform_device tegra_sdhci_device2 = {
+ .name = "sdhci-tegra",
+ .id = 2,
+ .resource = sdhci_resource2,
+ .num_resources = ARRAY_SIZE(sdhci_resource2),
+ .dev = {
+ .platform_data = &tegra_sdhci_platform_data2,
+ },
+};
+
+static struct platform_device tegra_sdhci_device3 = {
+ .name = "sdhci-tegra",
+ .id = 3,
+ .resource = sdhci_resource3,
+ .num_resources = ARRAY_SIZE(sdhci_resource3),
+ .dev = {
+ .platform_data = &tegra_sdhci_platform_data3,
+ },
+};
+
+static int cardhu_wifi_status_register(
+ void (*callback)(int card_present, void *dev_id),
+ void *dev_id)
+{
+ if (wifi_status_cb)
+ return -EAGAIN;
+ wifi_status_cb = callback;
+ wifi_status_cb_devid = dev_id;
+ return 0;
+}
+
+static int cardhu_wifi_set_carddetect(int val)
+{
+ pr_debug("%s: %d\n", __func__, val);
+ if (wifi_status_cb)
+ wifi_status_cb(val, wifi_status_cb_devid);
+ else
+ pr_warning("%s: Nobody to notify\n", __func__);
+ return 0;
+}
+
+static int cardhu_wifi_power(int on)
+{
+ pr_debug("%s: %d\n", __func__, on);
+ gpio_set_value(CARDHU_WLAN_PWR, on);
+ mdelay(100);
+ gpio_set_value(CARDHU_WLAN_RST, on);
+ mdelay(200);
+
+ return 0;
+}
+
+static int cardhu_wifi_reset(int on)
+{
+ pr_debug("%s: do nothing\n", __func__);
+ return 0;
+}
+
+static int __init cardhu_wifi_init(void)
+{
+ int rc;
+
+ rc = gpio_request(CARDHU_WLAN_PWR, "wlan_power");
+ if (rc)
+ pr_err("WLAN_PWR gpio request failed:%d\n", rc);
+ rc = gpio_request(CARDHU_WLAN_RST, "wlan_rst");
+ if (rc)
+ pr_err("WLAN_RST gpio request failed:%d\n", rc);
+ rc = gpio_request(CARDHU_WLAN_WOW, "bcmsdh_sdmmc");
+ if (rc)
+ pr_err("WLAN_WOW gpio request failed:%d\n", rc);
+
+ tegra_gpio_enable(CARDHU_WLAN_PWR);
+ tegra_gpio_enable(CARDHU_WLAN_RST);
+ tegra_gpio_enable(CARDHU_WLAN_WOW);
+
+ rc = gpio_direction_output(CARDHU_WLAN_PWR, 0);
+ if (rc)
+ pr_err("WLAN_PWR gpio direction configuration failed:%d\n", rc);
+ gpio_direction_output(CARDHU_WLAN_RST, 0);
+ if (rc)
+ pr_err("WLAN_RST gpio direction configuration failed:%d\n", rc);
+ rc = gpio_direction_input(CARDHU_WLAN_WOW);
+ if (rc)
+ pr_err("WLAN_WOW gpio direction configuration failed:%d\n", rc);
+
+ platform_device_register(&cardhu_wifi_device);
+ return 0;
+}
+
+int __init cardhu_sdhci_init(void)
+{
+ struct board_info board_info;
+ tegra_get_board_info(&board_info);
+ if ((board_info.board_id == BOARD_PM269) ||
+ (board_info.board_id == BOARD_E1257) ||
+ (board_info.board_id == BOARD_PM305) ||
+ (board_info.board_id == BOARD_PM311)) {
+ tegra_sdhci_platform_data0.wp_gpio = PM269_SD_WP;
+ tegra_sdhci_platform_data2.max_clk_limit = 12000000;
+ }
+
+ platform_device_register(&tegra_sdhci_device3);
+ platform_device_register(&tegra_sdhci_device2);
+ platform_device_register(&tegra_sdhci_device0);
+
+ cardhu_wifi_init();
+ return 0;
+}
diff --git a/arch/arm/mach-tegra/board-cardhu-sensors.c b/arch/arm/mach-tegra/board-cardhu-sensors.c
new file mode 100644
index 000000000000..8dbdb42ba2e4
--- /dev/null
+++ b/arch/arm/mach-tegra/board-cardhu-sensors.c
@@ -0,0 +1,922 @@
+/*
+ * arch/arm/mach-tegra/board-cardhu-sensors.c
+ *
+ * Copyright (c) 2010-2011, NVIDIA CORPORATION, All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Neither the name of NVIDIA CORPORATION nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/regulator/consumer.h>
+#include <linux/i2c/pca954x.h>
+#include <linux/i2c/pca953x.h>
+#include <linux/nct1008.h>
+#include <mach/fb.h>
+#include <mach/gpio.h>
+#include <media/ov5650.h>
+#include <media/ov14810.h>
+#include <media/ov2710.h>
+#include <media/tps61050.h>
+#include <generated/mach-types.h>
+#include "gpio-names.h"
+#include "board.h"
+#include <linux/mpu.h>
+#include <media/sh532u.h>
+#include <linux/bq27x00.h>
+#include <mach/gpio.h>
+#include <mach/edp.h>
+#include <mach/thermal.h>
+
+#include "gpio-names.h"
+#include "board-cardhu.h"
+#include "cpu-tegra.h"
+
+static struct regulator *cardhu_1v8_cam1 = NULL;
+static struct regulator *cardhu_1v8_cam2 = NULL;
+static struct regulator *cardhu_1v8_cam3 = NULL;
+static struct regulator *cardhu_vdd_2v8_cam1 = NULL;
+static struct regulator *cardhu_vdd_2v8_cam2 = NULL;
+static struct regulator *cardhu_vdd_cam3 = NULL;
+
+static struct board_info board_info;
+
+static struct pca954x_platform_mode cardhu_pca954x_modes[] = {
+ { .adap_id = PCA954x_I2C_BUS0, .deselect_on_exit = true, },
+ { .adap_id = PCA954x_I2C_BUS1, .deselect_on_exit = true, },
+ { .adap_id = PCA954x_I2C_BUS2, .deselect_on_exit = true, },
+ { .adap_id = PCA954x_I2C_BUS3, .deselect_on_exit = true, },
+};
+
+static struct pca954x_platform_data cardhu_pca954x_data = {
+ .modes = cardhu_pca954x_modes,
+ .num_modes = ARRAY_SIZE(cardhu_pca954x_modes),
+};
+
+static int cardhu_camera_init(void)
+{
+ int ret;
+
+ /* Boards E1198 and E1291 are of Cardhu personality
+ * and donot have TCA6416 exp for camera */
+ if ((board_info.board_id == BOARD_E1198) ||
+ (board_info.board_id == BOARD_E1291)) {
+ tegra_gpio_enable(CAM1_POWER_DWN_GPIO);
+ ret = gpio_request(CAM1_POWER_DWN_GPIO, "camera_power_en");
+ if (ret < 0)
+ pr_err("%s: gpio_request failed for gpio %s\n",
+ __func__, "CAM1_POWER_DWN_GPIO");
+ tegra_gpio_enable(CAM3_POWER_DWN_GPIO);
+ ret = gpio_request(CAM3_POWER_DWN_GPIO, "cam3_power_en");
+ if (ret < 0)
+ pr_err("%s: gpio_request failed for gpio %s\n",
+ __func__, "CAM3_POWER_DWN_GPIO");
+
+ tegra_gpio_enable(CAM2_POWER_DWN_GPIO);
+ ret = gpio_request(CAM2_POWER_DWN_GPIO, "camera2_power_en");
+ if (ret < 0)
+ pr_err("%s: gpio_request failed for gpio %s\n",
+ __func__, "CAM2_POWER_DWN_GPIO");
+
+ tegra_gpio_enable(OV5650_RESETN_GPIO);
+ ret = gpio_request(OV5650_RESETN_GPIO, "camera_reset");
+ if (ret < 0)
+ pr_err("%s: gpio_request failed for gpio %s\n",
+ __func__, "OV5650_RESETN_GPIO");
+
+ gpio_direction_output(CAM3_POWER_DWN_GPIO, 1);
+ gpio_direction_output(CAM1_POWER_DWN_GPIO, 1);
+ gpio_direction_output(CAM2_POWER_DWN_GPIO, 1);
+ mdelay(10);
+
+ gpio_direction_output(OV5650_RESETN_GPIO, 1);
+ mdelay(5);
+ gpio_direction_output(OV5650_RESETN_GPIO, 0);
+ mdelay(5);
+ gpio_direction_output(OV5650_RESETN_GPIO, 1);
+ mdelay(5);
+ }
+
+ /* To select the CSIB MUX either for cam2 or cam3 */
+ tegra_gpio_enable(CAMERA_CSI_MUX_SEL_GPIO);
+ ret = gpio_request(CAMERA_CSI_MUX_SEL_GPIO, "camera_csi_sel");
+ if (ret < 0)
+ pr_err("%s: gpio_request failed for gpio %s\n",
+ __func__, "CAMERA_CSI_MUX_SEL_GPIO");
+ gpio_direction_output(CAMERA_CSI_MUX_SEL_GPIO, 0);
+ gpio_export(CAMERA_CSI_MUX_SEL_GPIO, false);
+
+ return 0;
+}
+
+static int cardhu_left_ov5650_power_on(void)
+{
+ /* Boards E1198 and E1291 are of Cardhu personality
+ * and donot have TCA6416 exp for camera */
+ if ((board_info.board_id == BOARD_E1198) ||
+ (board_info.board_id == BOARD_E1291)) {
+
+ if (cardhu_vdd_2v8_cam1 == NULL) {
+ cardhu_vdd_2v8_cam1 = regulator_get(NULL, "vdd_2v8_cam1");
+ if (WARN_ON(IS_ERR(cardhu_vdd_2v8_cam1))) {
+ pr_err("%s: couldn't get regulator vdd_2v8_cam1: %ld\n",
+ __func__, PTR_ERR(cardhu_vdd_2v8_cam1));
+ goto reg_alloc_fail;
+ }
+ }
+ regulator_enable(cardhu_vdd_2v8_cam1);
+ mdelay(5);
+ }
+
+ /* Enable VDD_1V8_Cam1 */
+ if (cardhu_1v8_cam1 == NULL) {
+ cardhu_1v8_cam1 = regulator_get(NULL, "vdd_1v8_cam1");
+ if (WARN_ON(IS_ERR(cardhu_1v8_cam1))) {
+ pr_err("%s: couldn't get regulator vdd_1v8_cam1: %ld\n",
+ __func__, PTR_ERR(cardhu_1v8_cam1));
+ goto reg_alloc_fail;
+ }
+ }
+ regulator_enable(cardhu_1v8_cam1);
+
+ mdelay(5);
+ if ((board_info.board_id == BOARD_E1198) ||
+ (board_info.board_id == BOARD_E1291)) {
+ gpio_direction_output(CAM1_POWER_DWN_GPIO, 0);
+ mdelay(20);
+ gpio_direction_output(OV5650_RESETN_GPIO, 0);
+ mdelay(100);
+ gpio_direction_output(OV5650_RESETN_GPIO, 1);
+ }
+
+ if (board_info.board_id == BOARD_PM269) {
+ gpio_direction_output(CAM1_RST_L_GPIO, 0);
+ mdelay(100);
+ gpio_direction_output(CAM1_RST_L_GPIO, 1);
+ }
+
+ return 0;
+
+reg_alloc_fail:
+ if (cardhu_1v8_cam1) {
+ regulator_put(cardhu_1v8_cam1);
+ cardhu_1v8_cam1 = NULL;
+ }
+ if (cardhu_vdd_2v8_cam1) {
+ regulator_put(cardhu_vdd_2v8_cam1);
+ cardhu_vdd_2v8_cam1 = NULL;
+ }
+
+ return -ENODEV;
+
+}
+
+static int cardhu_left_ov5650_power_off(void)
+{
+ /* Boards E1198 and E1291 are of Cardhu personality
+ * and donot have TCA6416 exp for camera */
+ if ((board_info.board_id == BOARD_E1198) ||
+ (board_info.board_id == BOARD_E1291)) {
+ gpio_direction_output(CAM1_POWER_DWN_GPIO, 1);
+ gpio_direction_output(CAM2_POWER_DWN_GPIO, 1);
+ gpio_direction_output(CAM3_POWER_DWN_GPIO, 1);
+ }
+ if (cardhu_1v8_cam1)
+ regulator_disable(cardhu_1v8_cam1);
+ if (cardhu_vdd_2v8_cam1)
+ regulator_disable(cardhu_vdd_2v8_cam1);
+
+ return 0;
+}
+
+struct ov5650_platform_data cardhu_left_ov5650_data = {
+ .power_on = cardhu_left_ov5650_power_on,
+ .power_off = cardhu_left_ov5650_power_off,
+};
+
+#ifdef CONFIG_VIDEO_OV14810
+static int cardhu_ov14810_power_on(void)
+{
+ if (board_info.board_id == BOARD_E1198) {
+ gpio_direction_output(CAM1_POWER_DWN_GPIO, 1);
+ mdelay(20);
+ gpio_direction_output(OV14810_RESETN_GPIO, 0);
+ mdelay(100);
+ gpio_direction_output(OV14810_RESETN_GPIO, 1);
+ }
+
+ return 0;
+}
+
+static int cardhu_ov14810_power_off(void)
+{
+ if (board_info.board_id == BOARD_E1198) {
+ gpio_direction_output(CAM1_POWER_DWN_GPIO, 1);
+ gpio_direction_output(CAM2_POWER_DWN_GPIO, 1);
+ gpio_direction_output(CAM3_POWER_DWN_GPIO, 1);
+ }
+
+ return 0;
+}
+
+struct ov14810_platform_data cardhu_ov14810_data = {
+ .power_on = cardhu_ov14810_power_on,
+ .power_off = cardhu_ov14810_power_off,
+};
+
+struct ov14810_platform_data cardhu_ov14810uC_data = {
+ .power_on = NULL,
+ .power_off = NULL,
+};
+
+struct ov14810_platform_data cardhu_ov14810SlaveDev_data = {
+ .power_on = NULL,
+ .power_off = NULL,
+};
+
+static struct i2c_board_info cardhu_i2c_board_info_e1214[] = {
+ {
+ I2C_BOARD_INFO("ov14810", 0x36),
+ .platform_data = &cardhu_ov14810_data,
+ },
+ {
+ I2C_BOARD_INFO("ov14810uC", 0x67),
+ .platform_data = &cardhu_ov14810uC_data,
+ },
+ {
+ I2C_BOARD_INFO("ov14810SlaveDev", 0x69),
+ .platform_data = &cardhu_ov14810SlaveDev_data,
+ }
+};
+#endif
+
+static int cardhu_right_ov5650_power_on(void)
+{
+ /* Boards E1198 and E1291 are of Cardhu personality
+ * and donot have TCA6416 exp for camera */
+ if ((board_info.board_id == BOARD_E1198) ||
+ (board_info.board_id == BOARD_E1291)) {
+
+ gpio_direction_output(CAM1_POWER_DWN_GPIO, 0);
+ gpio_direction_output(CAM2_POWER_DWN_GPIO, 0);
+ mdelay(10);
+
+ if (cardhu_vdd_2v8_cam2 == NULL) {
+ cardhu_vdd_2v8_cam2 = regulator_get(NULL, "vdd_2v8_cam2");
+ if (WARN_ON(IS_ERR(cardhu_vdd_2v8_cam2))) {
+ pr_err("%s: couldn't get regulator vdd_2v8_cam2: %ld\n",
+ __func__, PTR_ERR(cardhu_vdd_2v8_cam2));
+ goto reg_alloc_fail;
+ }
+ }
+ regulator_enable(cardhu_vdd_2v8_cam2);
+ mdelay(5);
+ }
+
+ /* Enable VDD_1V8_Cam2 */
+ if (cardhu_1v8_cam2 == NULL) {
+ cardhu_1v8_cam2 = regulator_get(NULL, "vdd_1v8_cam2");
+ if (WARN_ON(IS_ERR(cardhu_1v8_cam2))) {
+ pr_err("%s: couldn't get regulator vdd_1v8_cam2: %ld\n",
+ __func__, PTR_ERR(cardhu_1v8_cam2));
+ goto reg_alloc_fail;
+ }
+ }
+ regulator_enable(cardhu_1v8_cam2);
+
+ mdelay(5);
+
+ if (board_info.board_id == BOARD_PM269) {
+ gpio_direction_output(CAM2_RST_L_GPIO, 0);
+ mdelay(100);
+ gpio_direction_output(CAM2_RST_L_GPIO, 1);
+ }
+
+ return 0;
+
+reg_alloc_fail:
+ if (cardhu_1v8_cam2) {
+ regulator_put(cardhu_1v8_cam2);
+ cardhu_1v8_cam2 = NULL;
+ }
+ if (cardhu_vdd_2v8_cam2) {
+ regulator_put(cardhu_vdd_2v8_cam2);
+ cardhu_vdd_2v8_cam2 = NULL;
+ }
+
+ return -ENODEV;
+
+}
+
+static int cardhu_right_ov5650_power_off(void)
+{
+ /* Boards E1198 and E1291 are of Cardhu personality
+ * and donot have TCA6416 exp for camera */
+ if ((board_info.board_id == BOARD_E1198) ||
+ (board_info.board_id == BOARD_E1291)) {
+ gpio_direction_output(CAM1_POWER_DWN_GPIO, 1);
+ gpio_direction_output(CAM2_POWER_DWN_GPIO, 1);
+ gpio_direction_output(CAM3_POWER_DWN_GPIO, 1);
+ }
+
+ if (cardhu_1v8_cam2)
+ regulator_disable(cardhu_1v8_cam2);
+ if (cardhu_vdd_2v8_cam2)
+ regulator_disable(cardhu_vdd_2v8_cam2);
+
+ return 0;
+}
+
+static void cardhu_ov5650_synchronize_sensors(void)
+{
+ if (board_info.board_id == BOARD_E1198) {
+ gpio_direction_output(CAM1_POWER_DWN_GPIO, 1);
+ mdelay(50);
+ gpio_direction_output(CAM1_POWER_DWN_GPIO, 0);
+ mdelay(50);
+ }
+ else if (board_info.board_id == BOARD_E1291) {
+ gpio_direction_output(CAM1_POWER_DWN_GPIO, 1);
+ gpio_direction_output(CAM2_POWER_DWN_GPIO, 1);
+ mdelay(50);
+ gpio_direction_output(CAM1_POWER_DWN_GPIO, 0);
+ gpio_direction_output(CAM2_POWER_DWN_GPIO, 0);
+ mdelay(50);
+ }
+ else
+ pr_err("%s: UnSupported BoardId\n", __func__);
+}
+
+struct ov5650_platform_data cardhu_right_ov5650_data = {
+ .power_on = cardhu_right_ov5650_power_on,
+ .power_off = cardhu_right_ov5650_power_off,
+ .synchronize_sensors = cardhu_ov5650_synchronize_sensors,
+};
+
+static int cardhu_ov2710_power_on(void)
+{
+ /* CSI-B and front sensor are muxed on verbier */
+ gpio_direction_output(CAMERA_CSI_MUX_SEL_GPIO, 1);
+
+ /* Boards E1198 and E1291 are of Cardhu personality
+ * and donot have TCA6416 exp for camera */
+ if ((board_info.board_id == BOARD_E1198) ||
+ (board_info.board_id == BOARD_E1291)) {
+
+ gpio_direction_output(CAM1_POWER_DWN_GPIO, 0);
+ gpio_direction_output(CAM2_POWER_DWN_GPIO, 0);
+ gpio_direction_output(CAM3_POWER_DWN_GPIO, 0);
+ mdelay(10);
+
+ if (cardhu_vdd_cam3 == NULL) {
+ cardhu_vdd_cam3 = regulator_get(NULL, "vdd_cam3");
+ if (WARN_ON(IS_ERR(cardhu_vdd_cam3))) {
+ pr_err("%s: couldn't get regulator vdd_cam3: %ld\n",
+ __func__, PTR_ERR(cardhu_vdd_cam3));
+ goto reg_alloc_fail;
+ }
+ }
+ regulator_enable(cardhu_vdd_cam3);
+ }
+
+ /* Enable VDD_1V8_Cam3 */
+ if (cardhu_1v8_cam3 == NULL) {
+ cardhu_1v8_cam3 = regulator_get(NULL, "vdd_1v8_cam3");
+ if (WARN_ON(IS_ERR(cardhu_1v8_cam3))) {
+ pr_err("%s: couldn't get regulator vdd_1v8_cam3: %ld\n",
+ __func__, PTR_ERR(cardhu_1v8_cam3));
+ goto reg_alloc_fail;
+ }
+ }
+ regulator_enable(cardhu_1v8_cam3);
+ mdelay(5);
+
+ return 0;
+
+reg_alloc_fail:
+ if (cardhu_1v8_cam3) {
+ regulator_put(cardhu_1v8_cam3);
+ cardhu_1v8_cam3 = NULL;
+ }
+ if (cardhu_vdd_cam3) {
+ regulator_put(cardhu_vdd_cam3);
+ cardhu_vdd_cam3 = NULL;
+ }
+
+ return -ENODEV;
+}
+
+static int cardhu_ov2710_power_off(void)
+{
+ gpio_direction_output(CAMERA_CSI_MUX_SEL_GPIO, 1);
+
+ /* Boards E1198 and E1291 are of Cardhu personality
+ * and donot have TCA6416 exp for camera */
+ if ((board_info.board_id == BOARD_E1198) ||
+ (board_info.board_id == BOARD_E1291)) {
+ gpio_direction_output(CAM1_POWER_DWN_GPIO, 1);
+ gpio_direction_output(CAM2_POWER_DWN_GPIO, 1);
+ gpio_direction_output(CAM3_POWER_DWN_GPIO, 1);
+ }
+
+ if (cardhu_1v8_cam3)
+ regulator_disable(cardhu_1v8_cam3);
+ if (cardhu_vdd_cam3)
+ regulator_disable(cardhu_vdd_cam3);
+
+ return 0;
+}
+
+struct ov2710_platform_data cardhu_ov2710_data = {
+ .power_on = cardhu_ov2710_power_on,
+ .power_off = cardhu_ov2710_power_off,
+};
+
+static const struct i2c_board_info cardhu_i2c3_board_info[] = {
+ {
+ I2C_BOARD_INFO("pca9546", 0x70),
+ .platform_data = &cardhu_pca954x_data,
+ },
+};
+
+static struct sh532u_platform_data sh532u_left_pdata = {
+ .num = 1,
+ .sync = 2,
+ .dev_name = "focuser",
+ .gpio_reset = TEGRA_GPIO_PBB0,
+};
+
+static struct sh532u_platform_data sh532u_right_pdata = {
+ .num = 2,
+ .sync = 1,
+ .dev_name = "focuser",
+ .gpio_reset = TEGRA_GPIO_PBB0,
+};
+
+static struct nvc_torch_pin_state cardhu_tps61050_pinstate = {
+ .mask = 0x0008, /*VGP3*/
+ .values = 0x0008,
+};
+
+static struct tps61050_platform_data cardhu_tps61050_pdata = {
+ .dev_name = "torch",
+ .pinstate = &cardhu_tps61050_pinstate,
+};
+
+static const struct i2c_board_info cardhu_i2c_board_info_tps61050[] = {
+ {
+ I2C_BOARD_INFO("tps61050", 0x33),
+ .platform_data = &cardhu_tps61050_pdata,
+ },
+};
+
+static struct i2c_board_info cardhu_i2c6_board_info[] = {
+ {
+ I2C_BOARD_INFO("ov5650L", 0x36),
+ .platform_data = &cardhu_left_ov5650_data,
+ },
+ {
+ I2C_BOARD_INFO("sh532u", 0x72),
+ .platform_data = &sh532u_left_pdata,
+ },
+};
+
+static struct i2c_board_info cardhu_i2c7_board_info[] = {
+ {
+ I2C_BOARD_INFO("ov5650R", 0x36),
+ .platform_data = &cardhu_right_ov5650_data,
+ },
+ {
+ I2C_BOARD_INFO("sh532u", 0x72),
+ .platform_data = &sh532u_right_pdata,
+ },
+};
+
+static struct i2c_board_info cardhu_i2c8_board_info[] = {
+ {
+ I2C_BOARD_INFO("ov2710", 0x36),
+ .platform_data = &cardhu_ov2710_data,
+ },
+};
+
+#ifndef CONFIG_TEGRA_INTERNAL_TSENSOR_EDP_SUPPORT
+static int nct_get_temp(void *_data, long *temp)
+{
+ struct nct1008_data *data = _data;
+ return nct1008_thermal_get_temp(data, temp);
+}
+
+static int nct_set_limits(void *_data,
+ long lo_limit_milli,
+ long hi_limit_milli)
+{
+ struct nct1008_data *data = _data;
+ return nct1008_thermal_set_limits(data,
+ lo_limit_milli,
+ hi_limit_milli);
+}
+
+static int nct_set_alert(void *_data,
+ void (*alert_func)(void *),
+ void *alert_data)
+{
+ struct nct1008_data *data = _data;
+ return nct1008_thermal_set_alert(data, alert_func, alert_data);
+}
+
+static int nct_set_shutdown_temp(void *_data, long shutdown_temp)
+{
+ struct nct1008_data *data = _data;
+ return nct1008_thermal_set_shutdown_temp(data, shutdown_temp);
+}
+static void nct1008_probe_callback(struct nct1008_data *data)
+{
+ struct tegra_thermal_device *thermal_device;
+
+ thermal_device = kzalloc(sizeof(struct tegra_thermal_device),
+ GFP_KERNEL);
+ if (!thermal_device) {
+ pr_err("unable to allocate thermal device\n");
+ return;
+ }
+
+ thermal_device->data = data;
+ thermal_device->offset = TDIODE_OFFSET;
+ thermal_device->get_temp = nct_get_temp;
+ thermal_device->set_limits = nct_set_limits;
+ thermal_device->set_alert = nct_set_alert;
+ thermal_device->set_shutdown_temp = nct_set_shutdown_temp;
+
+ tegra_thermal_set_device(thermal_device);
+}
+#endif
+
+static struct nct1008_platform_data cardhu_nct1008_pdata = {
+ .supported_hwrev = true,
+ .ext_range = true,
+ .conv_rate = 0x08,
+ .offset = 8, /* 4 * 2C. Bug 844025 - 1C for device accuracies */
+#ifndef CONFIG_TEGRA_INTERNAL_TSENSOR_EDP_SUPPORT
+ .probe_callback = nct1008_probe_callback,
+#endif
+};
+
+static struct i2c_board_info cardhu_i2c4_bq27510_board_info[] = {
+ {
+ I2C_BOARD_INFO("bq27510", 0x55),
+ },
+};
+
+static struct i2c_board_info cardhu_i2c4_nct1008_board_info[] = {
+ {
+ I2C_BOARD_INFO("nct1008", 0x4C),
+ .platform_data = &cardhu_nct1008_pdata,
+ .irq = -1,
+ }
+};
+
+static int cardhu_nct1008_init(void)
+{
+ int nct1008_port = -1;
+ int ret;
+
+ if ((board_info.board_id == BOARD_E1198) ||
+ (board_info.board_id == BOARD_E1291) ||
+ (board_info.board_id == BOARD_E1257) ||
+ (board_info.board_id == BOARD_PM269) ||
+ (board_info.board_id == BOARD_PM305) ||
+ (board_info.board_id == BOARD_PM311)) {
+ nct1008_port = TEGRA_GPIO_PCC2;
+ } else if ((board_info.board_id == BOARD_E1186) ||
+ (board_info.board_id == BOARD_E1187) ||
+ (board_info.board_id == BOARD_E1256)) {
+ /* FIXME: seems to be conflicting with usb3 vbus on E1186 */
+ /* nct1008_port = TEGRA_GPIO_PH7; */
+ }
+
+ if (nct1008_port >= 0) {
+ /* FIXME: enable irq when throttling is supported */
+ cardhu_i2c4_nct1008_board_info[0].irq = TEGRA_GPIO_TO_IRQ(nct1008_port);
+
+ ret = gpio_request(nct1008_port, "temp_alert");
+ if (ret < 0)
+ return ret;
+
+ ret = gpio_direction_input(nct1008_port);
+ if (ret < 0)
+ gpio_free(nct1008_port);
+ else
+ tegra_gpio_enable(nct1008_port);
+ }
+
+ return ret;
+}
+
+#if defined(CONFIG_GPIO_PCA953X)
+static struct pca953x_platform_data cardhu_pmu_tca6416_data = {
+ .gpio_base = PMU_TCA6416_GPIO_BASE,
+};
+
+static const struct i2c_board_info cardhu_i2c4_board_info_tca6416[] = {
+ {
+ I2C_BOARD_INFO("tca6416", 0x20),
+ .platform_data = &cardhu_pmu_tca6416_data,
+ },
+};
+
+static struct pca953x_platform_data cardhu_cam_tca6416_data = {
+ .gpio_base = CAM_TCA6416_GPIO_BASE,
+};
+
+static const struct i2c_board_info cardhu_i2c2_board_info_tca6416[] = {
+ {
+ I2C_BOARD_INFO("tca6416", 0x20),
+ .platform_data = &cardhu_cam_tca6416_data,
+ },
+};
+
+static int __init pmu_tca6416_init(void)
+{
+ if ((board_info.board_id == BOARD_E1198) ||
+ (board_info.board_id == BOARD_E1291))
+ return 0;
+
+ pr_info("Registering pmu pca6416\n");
+ i2c_register_board_info(4, cardhu_i2c4_board_info_tca6416,
+ ARRAY_SIZE(cardhu_i2c4_board_info_tca6416));
+ return 0;
+}
+
+static int __init cam_tca6416_init(void)
+{
+ /* Boards E1198 and E1291 are of Cardhu personality
+ * and donot have TCA6416 exp for camera */
+ if ((board_info.board_id == BOARD_E1198) ||
+ (board_info.board_id == BOARD_E1291))
+ return 0;
+
+ pr_info("Registering cam pca6416\n");
+ i2c_register_board_info(2, cardhu_i2c2_board_info_tca6416,
+ ARRAY_SIZE(cardhu_i2c2_board_info_tca6416));
+ return 0;
+}
+#else
+static int __init pmu_tca6416_init(void)
+{
+ return 0;
+}
+
+static int __init cam_tca6416_init(void)
+{
+ return 0;
+}
+#endif
+
+#ifdef CONFIG_MPU_SENSORS_MPU3050
+static struct mpu_platform_data mpu3050_data = {
+ .int_config = 0x10,
+ .level_shifter = 0,
+ .orientation = MPU_GYRO_ORIENTATION, /* Located in board_[platformname].h */
+};
+
+static struct ext_slave_platform_data mpu3050_accel_data = {
+ .address = MPU_ACCEL_ADDR,
+ .irq = 0,
+ .adapt_num = MPU_ACCEL_BUS_NUM,
+ .bus = EXT_SLAVE_BUS_SECONDARY,
+ .orientation = MPU_ACCEL_ORIENTATION, /* Located in board_[platformname].h */
+};
+
+static struct ext_slave_platform_data mpu_compass_data = {
+ .address = MPU_COMPASS_ADDR,
+ .irq = 0,
+ .adapt_num = MPU_COMPASS_BUS_NUM,
+ .bus = EXT_SLAVE_BUS_PRIMARY,
+ .orientation = MPU_COMPASS_ORIENTATION, /* Located in board_[platformname].h */
+};
+
+static struct i2c_board_info __initdata inv_mpu_i2c2_board_info[] = {
+ {
+ I2C_BOARD_INFO(MPU_GYRO_NAME, MPU_GYRO_ADDR),
+ .irq = TEGRA_GPIO_TO_IRQ(MPU_GYRO_IRQ_GPIO),
+ .platform_data = &mpu3050_data,
+ },
+ {
+ I2C_BOARD_INFO(MPU_ACCEL_NAME, MPU_ACCEL_ADDR),
+#if MPU_ACCEL_IRQ_GPIO
+ .irq = TEGRA_GPIO_TO_IRQ(MPU_ACCEL_IRQ_GPIO),
+#endif
+ .platform_data = &mpu3050_accel_data,
+ },
+ {
+ I2C_BOARD_INFO(MPU_COMPASS_NAME, MPU_COMPASS_ADDR),
+#if MPU_COMPASS_IRQ_GPIO
+ .irq = TEGRA_GPIO_TO_IRQ(MPU_COMPASS_IRQ_GPIO),
+#endif
+ .platform_data = &mpu_compass_data,
+ },
+};
+
+static void mpuirq_init(void)
+{
+ int ret = 0;
+
+ pr_info("*** MPU START *** mpuirq_init...\n");
+
+#if MPU_ACCEL_IRQ_GPIO
+ /* ACCEL-IRQ assignment */
+ tegra_gpio_enable(MPU_ACCEL_IRQ_GPIO);
+ ret = gpio_request(MPU_ACCEL_IRQ_GPIO, MPU_ACCEL_NAME);
+ if (ret < 0) {
+ pr_err("%s: gpio_request failed %d\n", __func__, ret);
+ return;
+ }
+
+ ret = gpio_direction_input(MPU_ACCEL_IRQ_GPIO);
+ if (ret < 0) {
+ pr_err("%s: gpio_direction_input failed %d\n", __func__, ret);
+ gpio_free(MPU_ACCEL_IRQ_GPIO);
+ return;
+ }
+#endif
+
+ /* MPU-IRQ assignment */
+ tegra_gpio_enable(MPU_GYRO_IRQ_GPIO);
+ ret = gpio_request(MPU_GYRO_IRQ_GPIO, MPU_GYRO_NAME);
+ if (ret < 0) {
+ pr_err("%s: gpio_request failed %d\n", __func__, ret);
+ return;
+ }
+
+ ret = gpio_direction_input(MPU_GYRO_IRQ_GPIO);
+ if (ret < 0) {
+ pr_err("%s: gpio_direction_input failed %d\n", __func__, ret);
+ gpio_free(MPU_GYRO_IRQ_GPIO);
+ return;
+ }
+ pr_info("*** MPU END *** mpuirq_init...\n");
+
+ i2c_register_board_info(MPU_GYRO_BUS_NUM, inv_mpu_i2c2_board_info,
+ ARRAY_SIZE(inv_mpu_i2c2_board_info));
+}
+#endif
+
+
+static struct i2c_board_info cardhu_i2c2_isl_board_info[] = {
+ {
+ I2C_BOARD_INFO("isl29028", 0x44),
+ }
+};
+
+int __init cardhu_sensors_init(void)
+{
+ int err;
+
+ tegra_get_board_info(&board_info);
+
+ cardhu_camera_init();
+ cam_tca6416_init();
+
+ i2c_register_board_info(2, cardhu_i2c3_board_info,
+ ARRAY_SIZE(cardhu_i2c3_board_info));
+
+ i2c_register_board_info(2, cardhu_i2c_board_info_tps61050,
+ ARRAY_SIZE(cardhu_i2c_board_info_tps61050));
+
+#ifdef CONFIG_VIDEO_OV14810
+ /* This is disabled by default; To enable this change Kconfig;
+ * there should be some way to detect dynamically which board
+ * is connected (E1211/E1214), till that time sensor selection
+ * logic is static;
+ * e1214 corresponds to ov14810 sensor */
+ i2c_register_board_info(2, cardhu_i2c_board_info_e1214,
+ ARRAY_SIZE(cardhu_i2c_board_info_e1214));
+#else
+ /* Left camera is on PCA954x's I2C BUS0, Right camera is on BUS1 &
+ * Front camera is on BUS2 */
+ i2c_register_board_info(PCA954x_I2C_BUS0, cardhu_i2c6_board_info,
+ ARRAY_SIZE(cardhu_i2c6_board_info));
+
+ i2c_register_board_info(PCA954x_I2C_BUS1, cardhu_i2c7_board_info,
+ ARRAY_SIZE(cardhu_i2c7_board_info));
+
+ i2c_register_board_info(PCA954x_I2C_BUS2, cardhu_i2c8_board_info,
+ ARRAY_SIZE(cardhu_i2c8_board_info));
+
+#endif
+ pmu_tca6416_init();
+
+ if (board_info.board_id == BOARD_E1291)
+ i2c_register_board_info(4, cardhu_i2c4_bq27510_board_info,
+ ARRAY_SIZE(cardhu_i2c4_bq27510_board_info));
+
+ i2c_register_board_info(2, cardhu_i2c2_isl_board_info,
+ ARRAY_SIZE(cardhu_i2c2_isl_board_info));
+
+ err = cardhu_nct1008_init();
+ if (err)
+ return err;
+
+ i2c_register_board_info(4, cardhu_i2c4_nct1008_board_info,
+ ARRAY_SIZE(cardhu_i2c4_nct1008_board_info));
+
+#ifdef CONFIG_MPU_SENSORS_MPU3050
+ mpuirq_init();
+#endif
+ return 0;
+}
+
+#if defined(CONFIG_GPIO_PCA953X)
+struct ov5650_gpios {
+ const char *name;
+ int gpio;
+ int enabled;
+};
+
+#define OV5650_GPIO(_name, _gpio, _enabled) \
+ { \
+ .name = _name, \
+ .gpio = _gpio, \
+ .enabled = _enabled, \
+ }
+
+static struct ov5650_gpios ov5650_gpio_keys[] = {
+ [0] = OV5650_GPIO("cam1_pwdn", CAM1_PWR_DN_GPIO, 0),
+ [1] = OV5650_GPIO("cam1_rst_lo", CAM1_RST_L_GPIO, 1),
+ [2] = OV5650_GPIO("cam1_af_pwdn_lo", CAM1_AF_PWR_DN_L_GPIO, 0),
+ [3] = OV5650_GPIO("cam1_ldo_shdn_lo", CAM1_LDO_SHUTDN_L_GPIO, 1),
+ [4] = OV5650_GPIO("cam2_pwdn", CAM2_PWR_DN_GPIO, 0),
+ [5] = OV5650_GPIO("cam2_rst_lo", CAM2_RST_L_GPIO, 1),
+ [6] = OV5650_GPIO("cam2_af_pwdn_lo", CAM2_AF_PWR_DN_L_GPIO, 0),
+ [7] = OV5650_GPIO("cam2_ldo_shdn_lo", CAM2_LDO_SHUTDN_L_GPIO, 1),
+ [8] = OV5650_GPIO("cam3_pwdn", CAM_FRONT_PWR_DN_GPIO, 0),
+ [9] = OV5650_GPIO("cam3_rst_lo", CAM_FRONT_RST_L_GPIO, 1),
+ [10] = OV5650_GPIO("cam3_af_pwdn_lo", CAM_FRONT_AF_PWR_DN_L_GPIO, 0),
+ [11] = OV5650_GPIO("cam3_ldo_shdn_lo", CAM_FRONT_LDO_SHUTDN_L_GPIO, 1),
+ [12] = OV5650_GPIO("cam_led_exp", CAM_FRONT_LED_EXP, 1),
+ [13] = OV5650_GPIO("cam_led_rear_exp", CAM_SNN_LED_REAR_EXP, 1),
+ [14] = OV5650_GPIO("cam_i2c_mux_rst", CAM_I2C_MUX_RST_EXP, 1),
+};
+
+int __init cardhu_ov5650_late_init(void)
+{
+ int ret;
+ int i;
+
+ if (!machine_is_cardhu())
+ return 0;
+
+ if ((board_info.board_id == BOARD_E1198) ||
+ (board_info.board_id == BOARD_E1291))
+ return 0;
+
+ printk("%s: \n", __func__);
+ for (i = 0; i < ARRAY_SIZE(ov5650_gpio_keys); i++) {
+ ret = gpio_request(ov5650_gpio_keys[i].gpio,
+ ov5650_gpio_keys[i].name);
+ if (ret < 0) {
+ printk("%s: gpio_request failed for gpio #%d\n",
+ __func__, i);
+ goto fail;
+ }
+ printk("%s: enable - %d\n", __func__, i);
+ gpio_direction_output(ov5650_gpio_keys[i].gpio,
+ ov5650_gpio_keys[i].enabled);
+ gpio_export(ov5650_gpio_keys[i].gpio, false);
+ }
+
+ return 0;
+
+fail:
+ while (i--)
+ gpio_free(ov5650_gpio_keys[i].gpio);
+ return ret;
+}
+
+late_initcall(cardhu_ov5650_late_init);
+#endif
diff --git a/arch/arm/mach-tegra/board-cardhu.c b/arch/arm/mach-tegra/board-cardhu.c
new file mode 100644
index 000000000000..0d69eb6165b7
--- /dev/null
+++ b/arch/arm/mach-tegra/board-cardhu.c
@@ -0,0 +1,1010 @@
+/*
+ * arch/arm/mach-tegra/board-cardhu.c
+ *
+ * Copyright (c) 2011, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/serial_8250.h>
+#include <linux/i2c.h>
+#include <linux/i2c/panjit_ts.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/i2c-tegra.h>
+#include <linux/gpio.h>
+#include <linux/input.h>
+#include <linux/platform_data/tegra_usb.h>
+#include <linux/spi/spi.h>
+#include <linux/i2c/atmel_mxt_ts.h>
+#include <linux/tegra_uart.h>
+#include <linux/memblock.h>
+#include <linux/spi-tegra.h>
+
+#include <sound/wm8903.h>
+
+#include <mach/clk.h>
+#include <mach/iomap.h>
+#include <mach/irqs.h>
+#include <mach/pinmux.h>
+#include <mach/iomap.h>
+#include <mach/io.h>
+#include <mach/i2s.h>
+#include <mach/tegra_wm8903_pdata.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <mach/usb_phy.h>
+#include <linux/nfc/pn544.h>
+#include <mach/thermal.h>
+
+#include "board.h"
+#include "clock.h"
+#include "board-cardhu.h"
+#include "devices.h"
+#include "gpio-names.h"
+#include "fuse.h"
+#include "pm.h"
+#include "baseband-xmm-power.h"
+
+/* All units are in millicelsius */
+static struct tegra_thermal_data thermal_data = {
+ .temp_throttle = 85000,
+ .temp_shutdown = 90000,
+ .temp_offset = TDIODE_OFFSET, /* temps based on tdiode */
+#ifdef CONFIG_TEGRA_EDP_LIMITS
+ .edp_offset = TDIODE_OFFSET, /* edp based on tdiode */
+ .hysteresis_edp = 3000,
+#endif
+#ifdef CONFIG_TEGRA_THERMAL_SYSFS
+ .tc1 = 0,
+ .tc2 = 1,
+ .passive_delay = 2000,
+#else
+ .hysteresis_throttle = 1000,
+#endif
+};
+
+/* !!!TODO: Change for cardhu (Taken from Ventana) */
+static struct tegra_utmip_config utmi_phy_config[] = {
+ [0] = {
+ .hssync_start_delay = 0,
+ .idle_wait_delay = 17,
+ .elastic_limit = 16,
+ .term_range_adj = 6,
+ .xcvr_setup = 15,
+ .xcvr_setup_offset = 0,
+ .xcvr_use_fuses = 1,
+ .xcvr_lsfslew = 2,
+ .xcvr_lsrslew = 2,
+ },
+ [1] = {
+ .hssync_start_delay = 0,
+ .idle_wait_delay = 17,
+ .elastic_limit = 16,
+ .term_range_adj = 6,
+ .xcvr_setup = 15,
+ .xcvr_setup_offset = 0,
+ .xcvr_use_fuses = 1,
+ .xcvr_lsfslew = 2,
+ .xcvr_lsrslew = 2,
+ },
+ [2] = {
+ .hssync_start_delay = 0,
+ .idle_wait_delay = 17,
+ .elastic_limit = 16,
+ .term_range_adj = 6,
+ .xcvr_setup = 8,
+ .xcvr_setup_offset = 0,
+ .xcvr_use_fuses = 1,
+ .xcvr_lsfslew = 2,
+ .xcvr_lsrslew = 2,
+ },
+};
+
+static struct resource cardhu_bcm4329_rfkill_resources[] = {
+ {
+ .name = "bcm4329_nshutdown_gpio",
+ .start = TEGRA_GPIO_PU0,
+ .end = TEGRA_GPIO_PU0,
+ .flags = IORESOURCE_IO,
+ },
+};
+
+static struct platform_device cardhu_bcm4329_rfkill_device = {
+ .name = "bcm4329_rfkill",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(cardhu_bcm4329_rfkill_resources),
+ .resource = cardhu_bcm4329_rfkill_resources,
+};
+
+static struct resource cardhu_bluesleep_resources[] = {
+ [0] = {
+ .name = "gpio_host_wake",
+ .start = TEGRA_GPIO_PU6,
+ .end = TEGRA_GPIO_PU6,
+ .flags = IORESOURCE_IO,
+ },
+ [1] = {
+ .name = "gpio_ext_wake",
+ .start = TEGRA_GPIO_PU1,
+ .end = TEGRA_GPIO_PU1,
+ .flags = IORESOURCE_IO,
+ },
+ [2] = {
+ .name = "host_wake",
+ .start = TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PU6),
+ .end = TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PU6),
+ .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE,
+ },
+};
+
+static struct platform_device cardhu_bluesleep_device = {
+ .name = "bluesleep",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(cardhu_bluesleep_resources),
+ .resource = cardhu_bluesleep_resources,
+};
+
+static noinline void __init cardhu_setup_bluesleep(void)
+{
+ platform_device_register(&cardhu_bluesleep_device);
+ tegra_gpio_enable(TEGRA_GPIO_PU6);
+ tegra_gpio_enable(TEGRA_GPIO_PU1);
+ return;
+}
+
+static __initdata struct tegra_clk_init_table cardhu_clk_init_table[] = {
+ /* name parent rate enabled */
+ { "pll_m", NULL, 0, false},
+ { "hda", "pll_p", 108000000, false},
+ { "hda2codec_2x","pll_p", 48000000, false},
+ { "pwm", "pll_p", 3187500, false},
+ { "blink", "clk_32k", 32768, true},
+ { "i2s1", "pll_a_out0", 0, false},
+ { "i2s3", "pll_a_out0", 0, false},
+ { "spdif_out", "pll_a_out0", 0, false},
+ { "d_audio", "pll_a_out0", 0, false},
+ { "dam0", "pll_a_out0", 0, false},
+ { "dam1", "pll_a_out0", 0, false},
+ { "dam2", "pll_a_out0", 0, false},
+ { "vi_sensor", "pll_p", 150000000, false},
+ { "i2c1", "pll_p", 3200000, false},
+ { "i2c2", "pll_p", 3200000, false},
+ { "i2c3", "pll_p", 3200000, false},
+ { "i2c4", "pll_p", 3200000, false},
+ { "i2c5", "pll_p", 3200000, false},
+ { NULL, NULL, 0, 0},
+};
+
+static struct pn544_i2c_platform_data nfc_pdata = {
+ .irq_gpio = TEGRA_GPIO_PX0,
+ .ven_gpio = TEGRA_GPIO_PP3,
+ .firm_gpio = TEGRA_GPIO_PO7,
+ };
+
+static struct i2c_board_info __initdata cardhu_i2c_bus3_board_info[] = {
+ {
+ I2C_BOARD_INFO("pn544", 0x28),
+ .platform_data = &nfc_pdata,
+ .irq = TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PX0),
+ },
+};
+static struct tegra_i2c_platform_data cardhu_i2c1_platform_data = {
+ .adapter_nr = 0,
+ .bus_count = 1,
+ .bus_clk_rate = { 100000, 0 },
+ .scl_gpio = {TEGRA_GPIO_PC4, 0},
+ .sda_gpio = {TEGRA_GPIO_PC5, 0},
+ .arb_recovery = arb_lost_recovery,
+};
+
+static struct tegra_i2c_platform_data cardhu_i2c2_platform_data = {
+ .adapter_nr = 1,
+ .bus_count = 1,
+ .bus_clk_rate = { 100000, 0 },
+ .is_clkon_always = true,
+ .scl_gpio = {TEGRA_GPIO_PT5, 0},
+ .sda_gpio = {TEGRA_GPIO_PT6, 0},
+ .arb_recovery = arb_lost_recovery,
+};
+
+static struct tegra_i2c_platform_data cardhu_i2c3_platform_data = {
+ .adapter_nr = 2,
+ .bus_count = 1,
+ .bus_clk_rate = { 100000, 0 },
+ .scl_gpio = {TEGRA_GPIO_PBB1, 0},
+ .sda_gpio = {TEGRA_GPIO_PBB2, 0},
+ .arb_recovery = arb_lost_recovery,
+};
+
+static struct tegra_i2c_platform_data cardhu_i2c4_platform_data = {
+ .adapter_nr = 3,
+ .bus_count = 1,
+ .bus_clk_rate = { 100000, 0 },
+ .scl_gpio = {TEGRA_GPIO_PV4, 0},
+ .sda_gpio = {TEGRA_GPIO_PV5, 0},
+ .arb_recovery = arb_lost_recovery,
+};
+
+static struct tegra_i2c_platform_data cardhu_i2c5_platform_data = {
+ .adapter_nr = 4,
+ .bus_count = 1,
+ .bus_clk_rate = { 400000, 0 },
+ .scl_gpio = {TEGRA_GPIO_PZ6, 0},
+ .sda_gpio = {TEGRA_GPIO_PZ7, 0},
+ .arb_recovery = arb_lost_recovery,
+};
+
+
+#if 0
+struct tegra_wired_jack_conf audio_wr_jack_conf = {
+ .hp_det_n = TEGRA_GPIO_PW2,
+ .en_mic_ext = TEGRA_GPIO_PX1,
+ .en_mic_int = TEGRA_GPIO_PX0,
+};
+#endif
+
+static struct wm8903_platform_data cardhu_wm8903_pdata = {
+ .irq_active_low = 0,
+ .micdet_cfg = 0,
+ .micdet_delay = 100,
+ .gpio_base = CARDHU_GPIO_WM8903(0),
+ .gpio_cfg = {
+ (WM8903_GPn_FN_DMIC_LR_CLK_OUTPUT << WM8903_GP1_FN_SHIFT),
+ (WM8903_GPn_FN_DMIC_LR_CLK_OUTPUT << WM8903_GP2_FN_SHIFT) |
+ WM8903_GP2_DIR,
+ 0,
+ WM8903_GPIO_NO_CONFIG,
+ WM8903_GPIO_NO_CONFIG,
+ },
+};
+
+static struct i2c_board_info __initdata wm8903_board_info = {
+ I2C_BOARD_INFO("wm8903", 0x1a),
+ .platform_data = &cardhu_wm8903_pdata,
+ .irq = TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_CDC_IRQ),
+};
+
+static void cardhu_i2c_init(void)
+{
+ tegra_i2c_device1.dev.platform_data = &cardhu_i2c1_platform_data;
+ tegra_i2c_device2.dev.platform_data = &cardhu_i2c2_platform_data;
+ tegra_i2c_device3.dev.platform_data = &cardhu_i2c3_platform_data;
+ tegra_i2c_device4.dev.platform_data = &cardhu_i2c4_platform_data;
+ tegra_i2c_device5.dev.platform_data = &cardhu_i2c5_platform_data;
+
+ platform_device_register(&tegra_i2c_device5);
+ platform_device_register(&tegra_i2c_device4);
+ platform_device_register(&tegra_i2c_device3);
+ platform_device_register(&tegra_i2c_device2);
+ platform_device_register(&tegra_i2c_device1);
+
+ i2c_register_board_info(4, &wm8903_board_info, 1);
+ i2c_register_board_info(2, cardhu_i2c_bus3_board_info, 1);
+}
+
+static struct platform_device *cardhu_uart_devices[] __initdata = {
+ &tegra_uarta_device,
+ &tegra_uartb_device,
+ &tegra_uartc_device,
+ &tegra_uartd_device,
+ &tegra_uarte_device,
+};
+static struct uart_clk_parent uart_parent_clk[] = {
+ [0] = {.name = "clk_m"},
+ [1] = {.name = "pll_p"},
+#ifndef CONFIG_TEGRA_PLLM_RESTRICTED
+ [2] = {.name = "pll_m"},
+#endif
+};
+
+static struct tegra_uart_platform_data cardhu_uart_pdata;
+
+static void __init uart_debug_init(void)
+{
+ struct board_info board_info;
+ int debug_port_id;
+
+ tegra_get_board_info(&board_info);
+
+ debug_port_id = get_tegra_uart_debug_port_id();
+ if (debug_port_id < 0) {
+ debug_port_id = 0;
+ /* UARTB is debug port
+ * for SLT - E1186/E1187/PM269
+ * for E1256/E1257
+ */
+ if (((board_info.sku & SKU_SLT_ULPI_SUPPORT) &&
+ ((board_info.board_id == BOARD_E1186) ||
+ (board_info.board_id == BOARD_E1187) ||
+ (board_info.board_id == BOARD_PM269))) ||
+ (board_info.board_id == BOARD_E1256) ||
+ (board_info.board_id == BOARD_E1257))
+ debug_port_id = 1;
+ }
+ switch (debug_port_id) {
+ case 0:
+ /* UARTA is the debug port. */
+ pr_info("Selecting UARTA as the debug console\n");
+ cardhu_uart_devices[0] = &debug_uarta_device;
+ debug_uart_clk = clk_get_sys("serial8250.0", "uarta");
+ debug_uart_port_base = ((struct plat_serial8250_port *)(
+ debug_uarta_device.dev.platform_data))->mapbase;
+ break;
+
+ case 1:
+ /* UARTB is the debug port. */
+ pr_info("Selecting UARTB as the debug console\n");
+ cardhu_uart_devices[1] = &debug_uartb_device;
+ debug_uart_clk = clk_get_sys("serial8250.0", "uartb");
+ debug_uart_port_base = ((struct plat_serial8250_port *)(
+ debug_uartb_device.dev.platform_data))->mapbase;
+ break;
+
+ case 2:
+ /* UARTC is the debug port. */
+ pr_info("Selecting UARTC as the debug console\n");
+ cardhu_uart_devices[2] = &debug_uartc_device;
+ debug_uart_clk = clk_get_sys("serial8250.0", "uartc");
+ debug_uart_port_base = ((struct plat_serial8250_port *)(
+ debug_uartc_device.dev.platform_data))->mapbase;
+ break;
+
+ case 3:
+ /* UARTD is the debug port. */
+ pr_info("Selecting UARTD as the debug console\n");
+ cardhu_uart_devices[3] = &debug_uartd_device;
+ debug_uart_clk = clk_get_sys("serial8250.0", "uartd");
+ debug_uart_port_base = ((struct plat_serial8250_port *)(
+ debug_uartd_device.dev.platform_data))->mapbase;
+ break;
+
+ case 4:
+ /* UARTE is the debug port. */
+ pr_info("Selecting UARTE as the debug console\n");
+ cardhu_uart_devices[4] = &debug_uarte_device;
+ debug_uart_clk = clk_get_sys("serial8250.0", "uarte");
+ debug_uart_port_base = ((struct plat_serial8250_port *)(
+ debug_uarte_device.dev.platform_data))->mapbase;
+ break;
+
+ default:
+ pr_info("The debug console id %d is invalid, Assuming UARTA", debug_port_id);
+ cardhu_uart_devices[0] = &debug_uarta_device;
+ debug_uart_clk = clk_get_sys("serial8250.0", "uarta");
+ debug_uart_port_base = ((struct plat_serial8250_port *)(
+ debug_uarta_device.dev.platform_data))->mapbase;
+ break;
+ }
+ return;
+}
+
+static void __init cardhu_uart_init(void)
+{
+ struct clk *c;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(uart_parent_clk); ++i) {
+ c = tegra_get_clock_by_name(uart_parent_clk[i].name);
+ if (IS_ERR_OR_NULL(c)) {
+ pr_err("Not able to get the clock for %s\n",
+ uart_parent_clk[i].name);
+ continue;
+ }
+ uart_parent_clk[i].parent_clk = c;
+ uart_parent_clk[i].fixed_clk_rate = clk_get_rate(c);
+ }
+ cardhu_uart_pdata.parent_clk_list = uart_parent_clk;
+ cardhu_uart_pdata.parent_clk_count = ARRAY_SIZE(uart_parent_clk);
+ tegra_uarta_device.dev.platform_data = &cardhu_uart_pdata;
+ tegra_uartb_device.dev.platform_data = &cardhu_uart_pdata;
+ tegra_uartc_device.dev.platform_data = &cardhu_uart_pdata;
+ tegra_uartd_device.dev.platform_data = &cardhu_uart_pdata;
+ tegra_uarte_device.dev.platform_data = &cardhu_uart_pdata;
+
+ /* Register low speed only if it is selected */
+ if (!is_tegra_debug_uartport_hs()) {
+ uart_debug_init();
+ /* Clock enable for the debug channel */
+ if (!IS_ERR_OR_NULL(debug_uart_clk)) {
+ pr_info("The debug console clock name is %s\n",
+ debug_uart_clk->name);
+ c = tegra_get_clock_by_name("pll_p");
+ if (IS_ERR_OR_NULL(c))
+ pr_err("Not getting the parent clock pll_p\n");
+ else
+ clk_set_parent(debug_uart_clk, c);
+
+ clk_enable(debug_uart_clk);
+ clk_set_rate(debug_uart_clk, clk_get_rate(c));
+ } else {
+ pr_err("Not getting the clock %s for debug console\n",
+ debug_uart_clk->name);
+ }
+ }
+
+ platform_add_devices(cardhu_uart_devices,
+ ARRAY_SIZE(cardhu_uart_devices));
+}
+
+static struct platform_device tegra_camera = {
+ .name = "tegra_camera",
+ .id = -1,
+};
+
+static struct platform_device *cardhu_spi_devices[] __initdata = {
+ &tegra_spi_device4,
+};
+
+struct spi_clk_parent spi_parent_clk[] = {
+ [0] = {.name = "pll_p"},
+#ifndef CONFIG_TEGRA_PLLM_RESTRICTED
+ [1] = {.name = "pll_m"},
+ [2] = {.name = "clk_m"},
+#else
+ [1] = {.name = "clk_m"},
+#endif
+};
+
+static struct tegra_spi_platform_data cardhu_spi_pdata = {
+ .is_dma_based = true,
+ .max_dma_buffer = (16 * 1024),
+ .is_clkon_always = false,
+ .max_rate = 100000000,
+};
+
+static void __init cardhu_spi_init(void)
+{
+ int i;
+ struct clk *c;
+ struct board_info board_info;
+
+ tegra_get_board_info(&board_info);
+
+ for (i = 0; i < ARRAY_SIZE(spi_parent_clk); ++i) {
+ c = tegra_get_clock_by_name(spi_parent_clk[i].name);
+ if (IS_ERR_OR_NULL(c)) {
+ pr_err("Not able to get the clock for %s\n",
+ spi_parent_clk[i].name);
+ continue;
+ }
+ spi_parent_clk[i].parent_clk = c;
+ spi_parent_clk[i].fixed_clk_rate = clk_get_rate(c);
+ }
+ cardhu_spi_pdata.parent_clk_list = spi_parent_clk;
+ cardhu_spi_pdata.parent_clk_count = ARRAY_SIZE(spi_parent_clk);
+ tegra_spi_device4.dev.platform_data = &cardhu_spi_pdata;
+ platform_add_devices(cardhu_spi_devices,
+ ARRAY_SIZE(cardhu_spi_devices));
+
+ if (board_info.board_id == BOARD_E1198) {
+ tegra_spi_device2.dev.platform_data = &cardhu_spi_pdata;
+ platform_device_register(&tegra_spi_device2);
+ }
+}
+
+static struct resource tegra_rtc_resources[] = {
+ [0] = {
+ .start = TEGRA_RTC_BASE,
+ .end = TEGRA_RTC_BASE + TEGRA_RTC_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = INT_RTC,
+ .end = INT_RTC,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device tegra_rtc_device = {
+ .name = "tegra_rtc",
+ .id = -1,
+ .resource = tegra_rtc_resources,
+ .num_resources = ARRAY_SIZE(tegra_rtc_resources),
+};
+
+static struct tegra_wm8903_platform_data cardhu_audio_pdata = {
+ .gpio_spkr_en = TEGRA_GPIO_SPKR_EN,
+ .gpio_hp_det = TEGRA_GPIO_HP_DET,
+ .gpio_hp_mute = -1,
+ .gpio_int_mic_en = -1,
+ .gpio_ext_mic_en = -1,
+};
+
+static struct platform_device cardhu_audio_device = {
+ .name = "tegra-snd-wm8903",
+ .id = 0,
+ .dev = {
+ .platform_data = &cardhu_audio_pdata,
+ },
+};
+
+static struct resource ram_console_resources[] = {
+ {
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct platform_device ram_console_device = {
+ .name = "ram_console",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(ram_console_resources),
+ .resource = ram_console_resources,
+};
+
+static struct platform_device *cardhu_devices[] __initdata = {
+ &tegra_pmu_device,
+ &tegra_rtc_device,
+ &tegra_udc_device,
+#if defined(CONFIG_TEGRA_IOVMM_SMMU)
+ &tegra_smmu_device,
+#endif
+ &tegra_wdt_device,
+#if defined(CONFIG_TEGRA_AVP)
+ &tegra_avp_device,
+#endif
+ &tegra_camera,
+#if defined(CONFIG_CRYPTO_DEV_TEGRA_SE)
+ &tegra_se_device,
+#endif
+ &tegra_ahub_device,
+ &tegra_dam_device0,
+ &tegra_dam_device1,
+ &tegra_dam_device2,
+ &tegra_i2s_device1,
+ &tegra_i2s_device3,
+ &tegra_spdif_device,
+ &spdif_dit_device,
+ &bluetooth_dit_device,
+ &cardhu_bcm4329_rfkill_device,
+ &tegra_pcm_device,
+ &cardhu_audio_device,
+ &tegra_hda_device,
+#if defined(CONFIG_CRYPTO_DEV_TEGRA_AES)
+ &tegra_aes_device,
+#endif
+ &ram_console_device,
+};
+
+#define MXT_CONFIG_CRC 0xD62DE8
+static const u8 config[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xFF, 0xFF, 0x32, 0x0A, 0x00, 0x14, 0x14, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x8B, 0x00, 0x00,
+ 0x1B, 0x2A, 0x00, 0x20, 0x3C, 0x04, 0x05, 0x00,
+ 0x02, 0x01, 0x00, 0x0A, 0x0A, 0x0A, 0x0A, 0xFF,
+ 0x02, 0x55, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x64, 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23,
+ 0x00, 0x00, 0x00, 0x05, 0x0A, 0x15, 0x1E, 0x00,
+ 0x00, 0x04, 0xFF, 0x03, 0x3F, 0x64, 0x64, 0x01,
+ 0x0A, 0x14, 0x28, 0x4B, 0x00, 0x02, 0x00, 0x64,
+ 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08, 0x10, 0x3C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+#define MXT_CONFIG_CRC_SKU2000 0xA24D9A
+static const u8 config_sku2000[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xFF, 0xFF, 0x32, 0x0A, 0x00, 0x14, 0x14, 0x19,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x8B, 0x00, 0x00,
+ 0x1B, 0x2A, 0x00, 0x20, 0x3A, 0x04, 0x05, 0x00, //23=thr 2 di
+ 0x04, 0x04, 0x41, 0x0A, 0x0A, 0x0A, 0x0A, 0xFF,
+ 0x02, 0x55, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x00, //0A=limit
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23,
+ 0x00, 0x00, 0x00, 0x05, 0x0A, 0x15, 0x1E, 0x00,
+ 0x00, 0x04, 0x00, 0x03, 0x3F, 0x64, 0x64, 0x01,
+ 0x0A, 0x14, 0x28, 0x4B, 0x00, 0x02, 0x00, 0x64,
+ 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08, 0x10, 0x3C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static struct mxt_platform_data atmel_mxt_info = {
+ .x_line = 27,
+ .y_line = 42,
+ .x_size = 768,
+ .y_size = 1366,
+ .blen = 0x20,
+ .threshold = 0x3C,
+ .voltage = 3300000, /* 3.3V */
+ .orient = 5,
+ .config = config,
+ .config_length = 157,
+ .config_crc = MXT_CONFIG_CRC,
+ .irqflags = IRQF_TRIGGER_FALLING,
+/* .read_chg = &read_chg, */
+ .read_chg = NULL,
+};
+
+static struct i2c_board_info __initdata atmel_i2c_info[] = {
+ {
+ I2C_BOARD_INFO("atmel_mxt_ts", 0x5A),
+ .irq = TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PH4),
+ .platform_data = &atmel_mxt_info,
+ }
+};
+
+static int __init cardhu_touch_init(void)
+{
+ struct board_info BoardInfo;
+
+ tegra_gpio_enable(TEGRA_GPIO_PH4);
+ tegra_gpio_enable(TEGRA_GPIO_PH6);
+
+ gpio_request(TEGRA_GPIO_PH4, "atmel-irq");
+ gpio_direction_input(TEGRA_GPIO_PH4);
+
+ gpio_request(TEGRA_GPIO_PH6, "atmel-reset");
+ gpio_direction_output(TEGRA_GPIO_PH6, 0);
+ msleep(1);
+ gpio_set_value(TEGRA_GPIO_PH6, 1);
+ msleep(100);
+
+ tegra_get_board_info(&BoardInfo);
+ if ((BoardInfo.sku & SKU_TOUCH_MASK) == SKU_TOUCH_2000) {
+ atmel_mxt_info.config = config_sku2000;
+ atmel_mxt_info.config_crc = MXT_CONFIG_CRC_SKU2000;
+ }
+
+ i2c_register_board_info(1, atmel_i2c_info, 1);
+
+ return 0;
+}
+
+static struct tegra_uhsic_config uhsic_phy_config = {
+ .enable_gpio = EN_HSIC_GPIO,
+ .reset_gpio = -1,
+ .sync_start_delay = 9,
+ .idle_wait_delay = 17,
+ .term_range_adj = 0,
+ .elastic_underrun_limit = 16,
+ .elastic_overrun_limit = 16,
+};
+
+static struct tegra_ehci_platform_data tegra_ehci_uhsic_pdata = {
+ .phy_type = TEGRA_USB_PHY_TYPE_HSIC,
+ .phy_config = &uhsic_phy_config,
+ .operating_mode = TEGRA_USB_HOST,
+ .power_down_on_bus_suspend = 1,
+};
+
+static struct tegra_ehci_platform_data tegra_ehci_pdata[] = {
+ [0] = {
+ .phy_config = &utmi_phy_config[0],
+ .operating_mode = TEGRA_USB_HOST,
+ .power_down_on_bus_suspend = 1,
+ },
+ [1] = {
+ .phy_config = &utmi_phy_config[1],
+ .operating_mode = TEGRA_USB_HOST,
+ .power_down_on_bus_suspend = 1,
+ },
+ [2] = {
+ .phy_config = &utmi_phy_config[2],
+ .operating_mode = TEGRA_USB_HOST,
+ .power_down_on_bus_suspend = 1,
+ .hotplug = 1,
+ },
+};
+
+static struct tegra_otg_platform_data tegra_otg_pdata = {
+ .ehci_device = &tegra_ehci1_device,
+ .ehci_pdata = &tegra_ehci_pdata[0],
+};
+
+#ifdef CONFIG_USB_SUPPORT
+static struct usb_phy_plat_data tegra_usb_phy_pdata[] = {
+ [0] = {
+ .instance = 0,
+ .vbus_gpio = -1,
+ .vbus_reg_supply = "vdd_vbus_micro_usb",
+ },
+ [1] = {
+ .instance = 1,
+ .vbus_gpio = -1,
+ },
+ [2] = {
+ .instance = 2,
+ .vbus_gpio = -1,
+ .vbus_reg_supply = "vdd_vbus_typea_usb",
+ },
+};
+
+static int cardu_usb_hsic_postsupend(void)
+{
+#ifdef CONFIG_TEGRA_BB_XMM_POWER
+ baseband_xmm_set_power_status(BBXMM_PS_L2);
+#endif
+ return 0;
+}
+
+static int cardu_usb_hsic_preresume(void)
+{
+#ifdef CONFIG_TEGRA_BB_XMM_POWER
+ baseband_xmm_set_power_status(BBXMM_PS_L2TOL0);
+#endif
+ return 0;
+}
+
+static int cardu_usb_hsic_phy_ready(void)
+{
+#ifdef CONFIG_TEGRA_BB_XMM_POWER
+ baseband_xmm_set_power_status(BBXMM_PS_L0);
+#endif
+ return 0;
+}
+
+static int cardu_usb_hsic_phy_off(void)
+{
+#ifdef CONFIG_TEGRA_BB_XMM_POWER
+ baseband_xmm_set_power_status(BBXMM_PS_L3);
+#endif
+ return 0;
+}
+
+static void cardhu_usb_init(void)
+{
+ struct board_info bi;
+
+ tegra_get_board_info(&bi);
+
+ tegra_usb_phy_init(tegra_usb_phy_pdata,
+ ARRAY_SIZE(tegra_usb_phy_pdata));
+
+ tegra_otg_device.dev.platform_data = &tegra_otg_pdata;
+ platform_device_register(&tegra_otg_device);
+ if (bi.board_id == BOARD_PM267) {
+ uhsic_phy_config.reset_gpio =
+ PM267_SMSC4640_HSIC_HUB_RESET_GPIO;
+ tegra_ehci2_device.dev.platform_data = &tegra_ehci_uhsic_pdata;
+ platform_device_register(&tegra_ehci2_device);
+ } else if (bi.board_id == BOARD_E1256) {
+ tegra_ehci2_device.dev.platform_data = &tegra_ehci_uhsic_pdata;
+ platform_device_register(&tegra_ehci2_device);
+ } else if (bi.board_id == BOARD_E1186) {
+ /* for baseband devices do not switch off phy during suspend */
+ tegra_ehci_uhsic_pdata.power_down_on_bus_suspend = 0;
+ uhsic_phy_config.postsuspend = cardu_usb_hsic_postsupend;
+ uhsic_phy_config.preresume = cardu_usb_hsic_preresume;
+ uhsic_phy_config.usb_phy_ready = cardu_usb_hsic_phy_ready;
+ uhsic_phy_config.post_phy_off = cardu_usb_hsic_phy_off;
+ tegra_ehci2_device.dev.platform_data = &tegra_ehci_uhsic_pdata;
+ /* baseband registration happens in baseband-xmm-power */
+ } else {
+ tegra_ehci2_device.dev.platform_data = &tegra_ehci_pdata[1];
+ platform_device_register(&tegra_ehci2_device);
+ }
+
+ tegra_ehci3_device.dev.platform_data = &tegra_ehci_pdata[2];
+ platform_device_register(&tegra_ehci3_device);
+
+}
+#else
+static void cardhu_usb_init(void) { }
+#endif
+
+static void cardhu_gps_init(void)
+{
+ tegra_gpio_enable(TEGRA_GPIO_PU2);
+ tegra_gpio_enable(TEGRA_GPIO_PU3);
+}
+
+static void cardhu_nfc_init(void)
+{
+ tegra_gpio_enable(TEGRA_GPIO_PX0);
+ tegra_gpio_enable(TEGRA_GPIO_PP3);
+ tegra_gpio_enable(TEGRA_GPIO_PO7);
+}
+
+static struct baseband_power_platform_data tegra_baseband_power_data = {
+ .baseband_type = BASEBAND_XMM,
+ .modem = {
+ .xmm = {
+ .bb_rst = XMM_GPIO_BB_RST,
+ .bb_on = XMM_GPIO_BB_ON,
+ .ipc_bb_wake = XMM_GPIO_IPC_BB_WAKE,
+ .ipc_ap_wake = XMM_GPIO_IPC_AP_WAKE,
+ .ipc_hsic_active = XMM_GPIO_IPC_HSIC_ACTIVE,
+ .ipc_hsic_sus_req = XMM_GPIO_IPC_HSIC_SUS_REQ,
+ .hsic_device = &tegra_ehci2_device,
+ },
+ },
+};
+
+static struct platform_device tegra_baseband_power_device = {
+ .name = "baseband_xmm_power",
+ .id = -1,
+ .dev = {
+ .platform_data = &tegra_baseband_power_data,
+ },
+};
+
+static struct platform_device tegra_baseband_power2_device = {
+ .name = "baseband_xmm_power2",
+ .id = -1,
+ .dev = {
+ .platform_data = &tegra_baseband_power_data,
+ },
+};
+
+static void cardhu_modem_init(void)
+{
+ struct board_info board_info;
+ int w_disable_gpio, ret;
+
+ tegra_get_board_info(&board_info);
+ switch (board_info.board_id) {
+ case BOARD_E1291:
+ case BOARD_E1198:
+ if (((board_info.board_id == BOARD_E1291) &&
+ (board_info.fab < BOARD_FAB_A03)) ||
+ ((board_info.board_id == BOARD_E1198) &&
+ (board_info.fab < BOARD_FAB_A02))) {
+ w_disable_gpio = TEGRA_GPIO_PH5;
+ } else {
+ w_disable_gpio = TEGRA_GPIO_PDD5;
+ }
+ tegra_gpio_enable(w_disable_gpio);
+ ret = gpio_request(w_disable_gpio, "w_disable_gpio");
+ if (ret < 0)
+ pr_err("%s: gpio_request failed for gpio %d\n",
+ __func__, w_disable_gpio);
+ else
+ gpio_direction_input(w_disable_gpio);
+
+ /* E1291-A04 & E1198:A02: Set PERST signal to high */
+ if (((board_info.board_id == BOARD_E1291) &&
+ (board_info.fab >= BOARD_FAB_A04)) ||
+ ((board_info.board_id == BOARD_E1198) &&
+ (board_info.fab >= BOARD_FAB_A02))) {
+ ret = gpio_request(TEGRA_GPIO_PH7, "modem_perst");
+ if (ret < 0) {
+ pr_err("%s(): Error in allocating gpio "
+ "TEGRA_GPIO_PH7\n", __func__);
+ break;
+ }
+ gpio_direction_output(TEGRA_GPIO_PH7, 1);
+ tegra_gpio_enable(TEGRA_GPIO_PH7);
+ }
+ break;
+ case BOARD_E1186:
+ tegra_gpio_enable(
+ tegra_baseband_power_data.modem.xmm.bb_rst);
+ tegra_gpio_enable(
+ tegra_baseband_power_data.modem.xmm.bb_on);
+ tegra_gpio_enable(
+ tegra_baseband_power_data.modem.xmm.ipc_bb_wake);
+ tegra_gpio_enable(
+ tegra_baseband_power_data.modem.xmm.ipc_ap_wake);
+ tegra_gpio_enable(
+ tegra_baseband_power_data.modem.xmm.ipc_hsic_active);
+ tegra_gpio_enable(
+ tegra_baseband_power_data.modem.xmm.ipc_hsic_sus_req);
+ platform_device_register(&tegra_baseband_power_device);
+ platform_device_register(&tegra_baseband_power2_device);
+ break;
+ default:
+ break;
+ }
+
+}
+
+#ifdef CONFIG_SATA_AHCI_TEGRA
+static void cardhu_sata_init(void)
+{
+ platform_device_register(&tegra_sata_device);
+}
+#else
+static void cardhu_sata_init(void) { }
+#endif
+
+static void __init tegra_cardhu_init(void)
+{
+ tegra_thermal_init(&thermal_data);
+ tegra_clk_init_from_table(cardhu_clk_init_table);
+ cardhu_pinmux_init();
+ cardhu_i2c_init();
+ cardhu_spi_init();
+ cardhu_usb_init();
+#ifdef CONFIG_TEGRA_EDP_LIMITS
+ cardhu_edp_init();
+#endif
+ cardhu_uart_init();
+ cardhu_tsensor_init();
+ platform_add_devices(cardhu_devices, ARRAY_SIZE(cardhu_devices));
+ cardhu_sdhci_init();
+ cardhu_regulator_init();
+ cardhu_gpio_switch_regulator_init();
+ cardhu_suspend_init();
+ cardhu_power_off_init();
+ cardhu_touch_init();
+ cardhu_gps_init();
+ cardhu_modem_init();
+ cardhu_kbc_init();
+ cardhu_scroll_init();
+ cardhu_keys_init();
+ cardhu_panel_init();
+ cardhu_pmon_init();
+ cardhu_sensors_init();
+ cardhu_setup_bluesleep();
+ cardhu_sata_init();
+ //audio_wired_jack_init();
+ cardhu_pins_state_init();
+ cardhu_emc_init();
+ tegra_release_bootloader_fb();
+ cardhu_nfc_init();
+}
+
+static void __init cardhu_ramconsole_reserve(unsigned long size)
+{
+ struct resource *res;
+ long ret;
+
+ res = platform_get_resource(&ram_console_device, IORESOURCE_MEM, 0);
+ if (!res) {
+ pr_err("Failed to find memory resource for ram console\n");
+ return;
+ }
+ res->start = memblock_end_of_DRAM() - size;
+ res->end = res->start + size - 1;
+ ret = memblock_remove(res->start, size);
+ if (ret) {
+ ram_console_device.resource = NULL;
+ ram_console_device.num_resources = 0;
+ pr_err("Failed to reserve memory block for ram console\n");
+ }
+}
+
+static void __init tegra_cardhu_reserve(void)
+{
+#if defined(CONFIG_NVMAP_CONVERT_CARVEOUT_TO_IOVMM)
+ /* support 1920X1200 with 24bpp */
+ tegra_reserve(0, SZ_8M + SZ_1M, SZ_8M + SZ_1M);
+#else
+ tegra_reserve(SZ_128M, SZ_8M, SZ_8M);
+#endif
+ cardhu_ramconsole_reserve(SZ_1M);
+}
+
+MACHINE_START(CARDHU, "cardhu")
+ .boot_params = 0x80000100,
+ .map_io = tegra_map_common_io,
+ .reserve = tegra_cardhu_reserve,
+ .init_early = tegra_init_early,
+ .init_irq = tegra_init_irq,
+ .timer = &tegra_timer,
+ .init_machine = tegra_cardhu_init,
+MACHINE_END
diff --git a/arch/arm/mach-tegra/board-cardhu.h b/arch/arm/mach-tegra/board-cardhu.h
new file mode 100644
index 000000000000..cacbd65dea0d
--- /dev/null
+++ b/arch/arm/mach-tegra/board-cardhu.h
@@ -0,0 +1,248 @@
+/*
+ * arch/arm/mach-tegra/board-cardhu.h
+ *
+ * Copyright (c) 2011, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _MACH_TEGRA_BOARD_CARDHU_H
+#define _MACH_TEGRA_BOARD_CARDHU_H
+
+#include <mach/gpio.h>
+#include <mach/irqs.h>
+#include <linux/mfd/tps6591x.h>
+#include <linux/mfd/ricoh583.h>
+
+/* Processor Board ID */
+#define BOARD_E1187 0x0B57
+#define BOARD_E1186 0x0B56
+#define BOARD_E1198 0x0B62
+#define BOARD_E1256 0x0C38
+#define BOARD_E1257 0x0C39
+#define BOARD_E1291 0x0C5B
+#define BOARD_PM267 0x0243
+#define BOARD_PM269 0x0245
+#define BOARD_E1208 0x0C08
+#define BOARD_PM305 0x0305
+#define BOARD_PM311 0x030B
+#define BOARD_PMU_PM298 0x0262
+#define BOARD_PMU_PM299 0x0263
+
+/* SKU Information */
+#define SKU_DCDC_TPS62361_SUPPORT 0x1
+#define SKU_SLT_ULPI_SUPPORT 0x2
+#define SKU_T30S_SUPPORT 0x4
+#define SKU_TOUCHSCREEN_MECH_FIX 0x0100
+
+#define SKU_TOUCH_MASK 0xFF00
+#define SKU_TOUCH_2000 0x0B00
+
+#define SKU_MEMORY_TYPE_BIT 0x4
+#define SKU_MEMORY_TYPE_MASK 0x3
+/* If BOARD_PM269 */
+#define SKU_MEMORY_SAMSUNG_EC 0x0
+#define SKU_MEMORY_ELPIDA 0x1
+#define SKU_MEMORY_SAMSUNG_EB 0x2
+/* If other BOARD_ variants */
+#define SKU_MEMORY_CARDHU_1GB_1R 0x0
+#define SKU_MEMORY_CARDHU_2GB_2R 0x1
+#define SKU_MEMORY_CARDHU_2GB_1R 0x2
+#define MEMORY_TYPE(sku) (((sku) >> SKU_MEMORY_TYPE_BIT) & SKU_MEMORY_TYPE_MASK)
+
+/* Board Fab version */
+#define BOARD_FAB_A00 0x0
+#define BOARD_FAB_A01 0x1
+#define BOARD_FAB_A02 0x2
+#define BOARD_FAB_A03 0x3
+#define BOARD_FAB_A04 0x4
+#define BOARD_FAB_A05 0x5
+
+/* Display Board ID */
+#define BOARD_DISPLAY_PM313 0x030D
+#define BOARD_DISPLAY_E1247 0x0C2F
+
+/* External peripheral act as gpio */
+/* TPS6591x GPIOs */
+#define TPS6591X_GPIO_BASE TEGRA_NR_GPIOS
+#define TPS6591X_GPIO_0 (TPS6591X_GPIO_BASE + TPS6591X_GPIO_GP0)
+#define TPS6591X_GPIO_1 (TPS6591X_GPIO_BASE + TPS6591X_GPIO_GP1)
+#define TPS6591X_GPIO_2 (TPS6591X_GPIO_BASE + TPS6591X_GPIO_GP2)
+#define TPS6591X_GPIO_3 (TPS6591X_GPIO_BASE + TPS6591X_GPIO_GP3)
+#define TPS6591X_GPIO_4 (TPS6591X_GPIO_BASE + TPS6591X_GPIO_GP4)
+#define TPS6591X_GPIO_5 (TPS6591X_GPIO_BASE + TPS6591X_GPIO_GP5)
+#define TPS6591X_GPIO_6 (TPS6591X_GPIO_BASE + TPS6591X_GPIO_GP6)
+#define TPS6591X_GPIO_7 (TPS6591X_GPIO_BASE + TPS6591X_GPIO_GP7)
+#define TPS6591X_GPIO_8 (TPS6591X_GPIO_BASE + TPS6591X_GPIO_GP8)
+#define TPS6591X_GPIO_END (TPS6591X_GPIO_BASE + TPS6591X_GPIO_NR)
+
+/* RICOH583 GPIO */
+#define RICOH583_GPIO_BASE TEGRA_NR_GPIOS
+#define RICOH583_GPIO_END (RICOH583_GPIO_BASE + 8)
+
+/* MAX77663 GPIO */
+#define MAX77663_GPIO_BASE TEGRA_NR_GPIOS
+#define MAX77663_GPIO_END (MAX77663_GPIO_BASE + MAX77663_GPIO_NR)
+
+/* PMU_TCA6416 GPIOs */
+#define PMU_TCA6416_GPIO_BASE (TPS6591X_GPIO_END)
+#define PMU_TCA6416_GPIO_PORT00 (PMU_TCA6416_GPIO_BASE + 0)
+#define PMU_TCA6416_GPIO_PORT01 (PMU_TCA6416_GPIO_BASE + 1)
+#define PMU_TCA6416_GPIO_PORT02 (PMU_TCA6416_GPIO_BASE + 2)
+#define PMU_TCA6416_GPIO_PORT03 (PMU_TCA6416_GPIO_BASE + 3)
+#define PMU_TCA6416_GPIO_PORT04 (PMU_TCA6416_GPIO_BASE + 4)
+#define PMU_TCA6416_GPIO_PORT05 (PMU_TCA6416_GPIO_BASE + 5)
+#define PMU_TCA6416_GPIO_PORT06 (PMU_TCA6416_GPIO_BASE + 6)
+#define PMU_TCA6416_GPIO_PORT07 (PMU_TCA6416_GPIO_BASE + 7)
+#define PMU_TCA6416_GPIO_PORT10 (PMU_TCA6416_GPIO_BASE + 8)
+#define PMU_TCA6416_GPIO_PORT11 (PMU_TCA6416_GPIO_BASE + 9)
+#define PMU_TCA6416_GPIO_PORT12 (PMU_TCA6416_GPIO_BASE + 10)
+#define PMU_TCA6416_GPIO_PORT13 (PMU_TCA6416_GPIO_BASE + 11)
+#define PMU_TCA6416_GPIO_PORT14 (PMU_TCA6416_GPIO_BASE + 12)
+#define PMU_TCA6416_GPIO_PORT15 (PMU_TCA6416_GPIO_BASE + 13)
+#define PMU_TCA6416_GPIO_PORT16 (PMU_TCA6416_GPIO_BASE + 14)
+#define PMU_TCA6416_GPIO_PORT17 (PMU_TCA6416_GPIO_BASE + 15)
+#define PMU_TCA6416_GPIO_END (PMU_TCA6416_GPIO_BASE + 16)
+
+/* PMU_TCA6416 GPIO assignment */
+#define EN_HSIC_GPIO PMU_TCA6416_GPIO_PORT11 /* PMU_GPIO25 */
+#define PM267_SMSC4640_HSIC_HUB_RESET_GPIO PMU_TCA6416_GPIO_PORT17 /* PMU_GPIO31 */
+
+/* CAM_TCA6416 GPIOs */
+#define CAM_TCA6416_GPIO_BASE PMU_TCA6416_GPIO_END
+#define CAM1_PWR_DN_GPIO CAM_TCA6416_GPIO_BASE + 0
+#define CAM1_RST_L_GPIO CAM_TCA6416_GPIO_BASE + 1
+#define CAM1_AF_PWR_DN_L_GPIO CAM_TCA6416_GPIO_BASE + 2
+#define CAM1_LDO_SHUTDN_L_GPIO CAM_TCA6416_GPIO_BASE + 3
+#define CAM2_PWR_DN_GPIO CAM_TCA6416_GPIO_BASE + 4
+#define CAM2_RST_L_GPIO CAM_TCA6416_GPIO_BASE + 5
+#define CAM2_AF_PWR_DN_L_GPIO CAM_TCA6416_GPIO_BASE + 6
+#define CAM2_LDO_SHUTDN_L_GPIO CAM_TCA6416_GPIO_BASE + 7
+#define CAM_FRONT_PWR_DN_GPIO CAM_TCA6416_GPIO_BASE + 8
+#define CAM_FRONT_RST_L_GPIO CAM_TCA6416_GPIO_BASE + 9
+#define CAM_FRONT_AF_PWR_DN_L_GPIO CAM_TCA6416_GPIO_BASE + 10
+#define CAM_FRONT_LDO_SHUTDN_L_GPIO CAM_TCA6416_GPIO_BASE + 11
+#define CAM_FRONT_LED_EXP CAM_TCA6416_GPIO_BASE + 12
+#define CAM_SNN_LED_REAR_EXP CAM_TCA6416_GPIO_BASE + 13
+/* PIN 19 NOT USED and is reserved */
+#define CAM_NOT_USED CAM_TCA6416_GPIO_BASE + 14
+#define CAM_I2C_MUX_RST_EXP CAM_TCA6416_GPIO_BASE + 15
+#define CAM_TCA6416_GPIO_END CAM_TCA6416_GPIO_BASE + 16
+
+/* WM8903 GPIOs */
+#define CARDHU_GPIO_WM8903(_x_) (CAM_TCA6416_GPIO_END + (_x_))
+#define CARDHU_GPIO_WM8903_END CARDHU_GPIO_WM8903(4)
+
+/* Audio-related GPIOs */
+#define TEGRA_GPIO_CDC_IRQ TEGRA_GPIO_PW3
+#define TEGRA_GPIO_SPKR_EN CARDHU_GPIO_WM8903(2)
+#define TEGRA_GPIO_HP_DET TEGRA_GPIO_PW2
+
+/* CAMERA RELATED GPIOs on CARDHU */
+#define OV5650_RESETN_GPIO TEGRA_GPIO_PBB0
+#define CAM1_POWER_DWN_GPIO TEGRA_GPIO_PBB5
+#define CAM2_POWER_DWN_GPIO TEGRA_GPIO_PBB6
+#define CAM3_POWER_DWN_GPIO TEGRA_GPIO_PBB7
+#define CAMERA_CSI_CAM_SEL_GPIO TEGRA_GPIO_PBB4
+#define CAMERA_CSI_MUX_SEL_GPIO TEGRA_GPIO_PCC1
+#define CAM1_LDO_EN_GPIO TEGRA_GPIO_PR6
+#define CAM2_LDO_EN_GPIO TEGRA_GPIO_PR7
+#define CAM3_LDO_EN_GPIO TEGRA_GPIO_PS0
+#define OV14810_RESETN_GPIO TEGRA_GPIO_PBB0
+
+#define CAMERA_FLASH_SYNC_GPIO TEGRA_GPIO_PBB3
+#define CAMERA_FLASH_MAX_TORCH_AMP 7
+#define CAMERA_FLASH_MAX_FLASH_AMP 7
+
+/* PCA954x I2C bus expander bus addresses */
+#define PCA954x_I2C_BUS_BASE 6
+#define PCA954x_I2C_BUS0 (PCA954x_I2C_BUS_BASE + 0)
+#define PCA954x_I2C_BUS1 (PCA954x_I2C_BUS_BASE + 1)
+#define PCA954x_I2C_BUS2 (PCA954x_I2C_BUS_BASE + 2)
+#define PCA954x_I2C_BUS3 (PCA954x_I2C_BUS_BASE + 3)
+
+#define AC_PRESENT_GPIO TPS6591X_GPIO_4
+
+/*****************Interrupt tables ******************/
+/* External peripheral act as interrupt controller */
+/* TPS6591x IRQs */
+#define TPS6591X_IRQ_BASE TEGRA_NR_IRQS
+#define TPS6591X_IRQ_END (TPS6591X_IRQ_BASE + 18)
+
+/* RICOH583 IRQs */
+#define RICOH583_IRQ_BASE TEGRA_NR_IRQS
+#define RICOH583_IRQ_END (RICOH583_IRQ_BASE + RICOH583_NR_IRQS)
+
+/* MAX77663 IRQs */
+#define MAX77663_IRQ_BASE TEGRA_NR_IRQS
+#define MAX77663_IRQ_END (MAX77663_IRQ_BASE + MAX77663_IRQ_NR)
+
+int cardhu_charge_init(void);
+int cardhu_regulator_init(void);
+int cardhu_suspend_init(void);
+int cardhu_sdhci_init(void);
+int cardhu_pinmux_init(void);
+int cardhu_panel_init(void);
+int cardhu_sensors_init(void);
+int cardhu_kbc_init(void);
+int cardhu_scroll_init(void);
+int cardhu_keys_init(void);
+int cardhu_gpio_switch_regulator_init(void);
+int cardhu_pins_state_init(void);
+int cardhu_emc_init(void);
+int cardhu_power_off_init(void);
+int cardhu_edp_init(void);
+int cardhu_pmon_init(void);
+int cardhu_pm298_gpio_switch_regulator_init(void);
+int cardhu_pm298_regulator_init(void);
+int cardhu_pm299_gpio_switch_regulator_init(void);
+int cardhu_pm299_regulator_init(void);
+void __init cardhu_tsensor_init(void);
+
+/* Invensense MPU Definitions */
+#define MPU_GYRO_NAME "mpu3050"
+#define MPU_GYRO_IRQ_GPIO TEGRA_GPIO_PX1
+#define MPU_GYRO_ADDR 0x68
+#define MPU_GYRO_BUS_NUM 2
+#define MPU_GYRO_ORIENTATION { 0, -1, 0, -1, 0, 0, 0, 0, -1 }
+#define MPU_ACCEL_NAME "kxtf9"
+#define MPU_ACCEL_IRQ_GPIO TEGRA_GPIO_PL1
+#define MPU_ACCEL_ADDR 0x0F
+#define MPU_ACCEL_BUS_NUM 2
+#define MPU_ACCEL_ORIENTATION { 0, -1, 0, -1, 0, 0, 0, 0, -1 }
+#define MPU_COMPASS_NAME "ak8975"
+#define MPU_COMPASS_IRQ_GPIO 0
+#define MPU_COMPASS_ADDR 0x0C
+#define MPU_COMPASS_BUS_NUM 2
+#define MPU_COMPASS_ORIENTATION { 1, 0, 0, 0, 1, 0, 0, 0, 1 }
+
+/* Baseband GPIO addresses */
+#define BB_GPIO_BB_EN TEGRA_GPIO_PR5
+#define BB_GPIO_BB_RST TEGRA_GPIO_PS4
+#define BB_GPIO_SPI_INT TEGRA_GPIO_PS6
+#define BB_GPIO_SPI_SS TEGRA_GPIO_PV0
+#define BB_GPIO_AWR TEGRA_GPIO_PS7
+#define BB_GPIO_CWR TEGRA_GPIO_PU5
+
+#define XMM_GPIO_BB_ON BB_GPIO_BB_EN
+#define XMM_GPIO_BB_RST BB_GPIO_BB_RST
+#define XMM_GPIO_IPC_HSIC_ACTIVE BB_GPIO_SPI_INT
+#define XMM_GPIO_IPC_HSIC_SUS_REQ BB_GPIO_SPI_SS
+#define XMM_GPIO_IPC_BB_WAKE BB_GPIO_AWR
+#define XMM_GPIO_IPC_AP_WAKE BB_GPIO_CWR
+
+#define TDIODE_OFFSET (10000) /* in millicelsius */
+
+#endif
diff --git a/arch/arm/mach-tegra/board-enterprise-baseband.c b/arch/arm/mach-tegra/board-enterprise-baseband.c
new file mode 100644
index 000000000000..0084c06f6a8c
--- /dev/null
+++ b/arch/arm/mach-tegra/board-enterprise-baseband.c
@@ -0,0 +1,235 @@
+/*
+ * arch/arm/mach-tegra/board-enterprise-baseband.c
+ *
+ * Copyright (c) 2011, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/resource.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/err.h>
+#include <linux/wakelock.h>
+#include <linux/platform_data/tegra_usb.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <mach/pinmux.h>
+#include <mach/usb_phy.h>
+#include <mach/tegra_usb_modem_power.h>
+#include "devices.h"
+#include "gpio-names.h"
+
+/* Tegra3 BB GPIO */
+#define MODEM_PWR_ON TEGRA_GPIO_PE0
+#define MODEM_RESET TEGRA_GPIO_PE1
+#define BB_RST_OUT TEGRA_GPIO_PV1
+
+/* Icera BB GPIO */
+#define AP2MDM_ACK TEGRA_GPIO_PE3
+#define MDM2AP_ACK TEGRA_GPIO_PU5
+#define AP2MDM_ACK2 TEGRA_GPIO_PE2
+#define MDM2AP_ACK2 TEGRA_GPIO_PV0
+
+static struct wake_lock mdm_wake_lock;
+
+static struct gpio modem_gpios[] = {
+ {MODEM_PWR_ON, GPIOF_OUT_INIT_LOW, "MODEM PWR ON"},
+ {MODEM_RESET, GPIOF_IN, "MODEM RESET"},
+ {BB_RST_OUT, GPIOF_IN, "BB RST OUT"},
+ {MDM2AP_ACK, GPIOF_IN, "MDM2AP_ACK"},
+ {AP2MDM_ACK2, GPIOF_OUT_INIT_HIGH, "AP2MDM ACK2"},
+ {AP2MDM_ACK, GPIOF_OUT_INIT_LOW, "AP2MDM ACK"},
+ {TEGRA_GPIO_PY3, GPIOF_IN, "ULPI_STP"},
+ {TEGRA_GPIO_PO1, GPIOF_OUT_INIT_LOW, "ULPI_D0"},
+ {TEGRA_GPIO_PO2, GPIOF_OUT_INIT_LOW, "ULPI_D1"},
+};
+
+static int baseband_phy_on(void);
+static int baseband_phy_off(void);
+static void baseband_phy_restore_start(void);
+static void baseband_phy_restore_end(void);
+
+static struct tegra_ulpi_trimmer e1219_trimmer = { 10, 1, 1, 1 };
+
+static struct tegra_ulpi_config ehci2_null_ulpi_phy_config = {
+ .trimmer = &e1219_trimmer,
+ .post_phy_on = baseband_phy_on,
+ .pre_phy_off = baseband_phy_off,
+ .phy_restore_start = baseband_phy_restore_start,
+ .phy_restore_end = baseband_phy_restore_end,
+};
+
+static struct tegra_ehci_platform_data ehci2_null_ulpi_platform_data = {
+ .operating_mode = TEGRA_USB_HOST,
+ .power_down_on_bus_suspend = 0,
+ .phy_config = &ehci2_null_ulpi_phy_config,
+ .phy_type = TEGRA_USB_PHY_TYPE_NULL_ULPI,
+};
+
+static int __init tegra_null_ulpi_init(void)
+{
+ tegra_ehci2_device.dev.platform_data = &ehci2_null_ulpi_platform_data;
+ platform_device_register(&tegra_ehci2_device);
+ return 0;
+}
+
+static irqreturn_t mdm_start_thread(int irq, void *data)
+{
+ if (gpio_get_value(BB_RST_OUT)) {
+ pr_info("BB_RST_OUT high\n");
+ } else {
+ pr_info("BB_RST_OUT low\n");
+ /* hold wait lock to complete the enumeration */
+ wake_lock_timeout(&mdm_wake_lock, HZ * 10);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int baseband_phy_on(void)
+{
+ static bool phy_init = false;
+
+ if (!phy_init) {
+ /* set AP2MDM_ACK2 low */
+ gpio_set_value(AP2MDM_ACK2, 0);
+ phy_init = true;
+ }
+ pr_info("%s\n", __func__);
+ return 0;
+}
+
+static int baseband_phy_off(void)
+{
+ pr_info("%s\n", __func__);
+ return 0;
+}
+
+static void baseband_phy_restore_start(void)
+{
+ /* set AP2MDM_ACK2 high */
+ gpio_set_value(AP2MDM_ACK2, 1);
+}
+
+static void baseband_phy_restore_end(void)
+{
+ /* set AP2MDM_ACK2 low */
+ gpio_set_value(AP2MDM_ACK2, 0);
+}
+
+static void baseband_start(void)
+{
+ /*
+ * Leave baseband powered OFF.
+ * User-space daemons will take care of powering it up.
+ */
+ pr_info("%s\n", __func__);
+ gpio_set_value(MODEM_PWR_ON, 0);
+}
+
+static void baseband_reset(void)
+{
+ /* Initiate power cycle on baseband sub system */
+ pr_info("%s\n", __func__);
+ gpio_set_value(MODEM_PWR_ON, 0);
+ mdelay(200);
+ gpio_set_value(MODEM_PWR_ON, 1);
+}
+
+static int baseband_init(void)
+{
+ int irq;
+ int ret;
+
+ ret = gpio_request_array(modem_gpios, ARRAY_SIZE(modem_gpios));
+ if (ret)
+ return ret;
+
+ /* enable pull-up for ULPI STP */
+ tegra_pinmux_set_pullupdown(TEGRA_PINGROUP_ULPI_STP,
+ TEGRA_PUPD_PULL_UP);
+
+ /* enable pull-up for MDM2AP_ACK2 */
+ tegra_pinmux_set_pullupdown(TEGRA_PINGROUP_GPIO_PV0,
+ TEGRA_PUPD_PULL_UP);
+
+ tegra_gpio_enable(MODEM_PWR_ON);
+ tegra_gpio_enable(MODEM_RESET);
+ tegra_gpio_enable(AP2MDM_ACK2);
+ tegra_gpio_enable(BB_RST_OUT);
+ tegra_gpio_enable(AP2MDM_ACK);
+ tegra_gpio_enable(MDM2AP_ACK);
+ tegra_gpio_enable(TEGRA_GPIO_PY3);
+ tegra_gpio_enable(TEGRA_GPIO_PO1);
+ tegra_gpio_enable(TEGRA_GPIO_PO2);
+
+ /* export GPIO for user space access through sysfs */
+ gpio_export(MODEM_PWR_ON, false);
+
+ /* phy init */
+ tegra_null_ulpi_init();
+
+ wake_lock_init(&mdm_wake_lock, WAKE_LOCK_SUSPEND, "mdm_lock");
+
+ /* enable IRQ for BB_RST_OUT */
+ irq = gpio_to_irq(BB_RST_OUT);
+
+ ret = request_threaded_irq(irq, NULL, mdm_start_thread,
+ IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
+ "mdm_start", NULL);
+ if (ret < 0) {
+ pr_err("%s: request_threaded_irq error\n", __func__);
+ return ret;
+ }
+
+ ret = enable_irq_wake(irq);
+ if (ret) {
+ pr_err("%s: enable_irq_wake error\n", __func__);
+ free_irq(irq, NULL);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct tegra_modem_operations baseband_operations = {
+ .init = baseband_init,
+ .start = baseband_start,
+ .reset = baseband_reset,
+};
+
+static struct tegra_usb_modem_power_platform_data baseband_pdata = {
+ .ops = &baseband_operations,
+ .wake_gpio = MDM2AP_ACK2,
+ .flags = IRQF_TRIGGER_FALLING,
+};
+
+static struct platform_device icera_baseband_device = {
+ .name = "tegra_usb_modem_power",
+ .id = -1,
+ .dev = {
+ .platform_data = &baseband_pdata,
+ },
+};
+
+int __init enterprise_modem_init(void)
+{
+ platform_device_register(&icera_baseband_device);
+ return 0;
+}
diff --git a/arch/arm/mach-tegra/board-enterprise-kbc.c b/arch/arm/mach-tegra/board-enterprise-kbc.c
new file mode 100644
index 000000000000..0c4a5587b605
--- /dev/null
+++ b/arch/arm/mach-tegra/board-enterprise-kbc.c
@@ -0,0 +1,104 @@
+/*
+ * arch/arm/mach-tegra/board-enterprise-kbc.c
+ * Keys configuration for Nvidia tegra3 enterprise platform.
+ *
+ * Copyright (C) 2011 NVIDIA, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <mach/io.h>
+#include <mach/iomap.h>
+#include <mach/kbc.h>
+
+#include "board.h"
+#include "board-enterprise.h"
+#include "devices.h"
+
+#define ENTERPRISE_ROW_COUNT 3
+#define ENTERPRISE_COL_COUNT 3
+
+static const u32 kbd_keymap[] = {
+ KEY(0, 0, KEY_POWER),
+
+ KEY(1, 0, KEY_HOME),
+ KEY(1, 1, KEY_BACK),
+ KEY(1, 2, KEY_VOLUMEDOWN),
+
+ KEY(2, 0, KEY_MENU),
+ KEY(2, 1, KEY_SEARCH),
+ KEY(2, 2, KEY_VOLUMEUP),
+};
+
+static const struct matrix_keymap_data keymap_data = {
+ .keymap = kbd_keymap,
+ .keymap_size = ARRAY_SIZE(kbd_keymap),
+};
+
+static struct tegra_kbc_wake_key enterprise_wake_cfg[] = {
+ [0] = {
+ .row = 0,
+ .col = 0,
+ },
+ [1] = {
+ .row = 1,
+ .col = 0,
+ },
+ [2] = {
+ .row = 1,
+ .col = 1,
+ },
+ [3] = {
+ .row = 2,
+ .col = 0,
+ },
+};
+
+static struct tegra_kbc_platform_data enterprise_kbc_platform_data = {
+ .debounce_cnt = 20,
+ .repeat_cnt = 1,
+ .scan_count = 30,
+ .wakeup = true,
+ .keymap_data = &keymap_data,
+ .wake_cnt = 4,
+ .wake_cfg = &enterprise_wake_cfg[0],
+};
+
+int __init enterprise_kbc_init(void)
+{
+ struct tegra_kbc_platform_data *data = &enterprise_kbc_platform_data;
+ int i;
+ tegra_kbc_device.dev.platform_data = &enterprise_kbc_platform_data;
+ pr_info("Registering tegra-kbc\n");
+
+ BUG_ON((KBC_MAX_ROW + KBC_MAX_COL) > KBC_MAX_GPIO);
+ for (i = 0; i < ENTERPRISE_ROW_COUNT; i++) {
+ data->pin_cfg[i].num = i;
+ data->pin_cfg[i].is_row = true;
+ data->pin_cfg[i].en = true;
+ }
+ for (i = 0; i < ENTERPRISE_COL_COUNT; i++) {
+ data->pin_cfg[i + KBC_PIN_GPIO_16].num = i;
+ data->pin_cfg[i + KBC_PIN_GPIO_16].en = true;
+ }
+
+ platform_device_register(&tegra_kbc_device);
+ pr_info("Registering successful tegra-kbc\n");
+ return 0;
+}
+
diff --git a/arch/arm/mach-tegra/board-enterprise-memory.c b/arch/arm/mach-tegra/board-enterprise-memory.c
new file mode 100644
index 000000000000..3212894cb049
--- /dev/null
+++ b/arch/arm/mach-tegra/board-enterprise-memory.c
@@ -0,0 +1,629 @@
+/*
+ * Copyright (C) 2011 NVIDIA, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+
+#include "board-enterprise.h"
+#include "tegra3_emc.h"
+
+
+static const struct tegra_emc_table enterprise_emc_tables_h5tc2g[] = {
+ {
+ 0x31, /* Rev 3.1 */
+ 25500, /* SDRAM frequency */
+ {
+ 0x00000001, /* EMC_RC */
+ 0x00000003, /* EMC_RFC */
+ 0x00000002, /* EMC_RAS */
+ 0x00000002, /* EMC_RP */
+ 0x00000004, /* EMC_R2W */
+ 0x00000004, /* EMC_W2R */
+ 0x00000001, /* EMC_R2P */
+ 0x00000005, /* EMC_W2P */
+ 0x00000002, /* EMC_RD_RCD */
+ 0x00000002, /* EMC_WR_RCD */
+ 0x00000001, /* EMC_RRD */
+ 0x00000001, /* EMC_REXT */
+ 0x00000000, /* EMC_WEXT */
+ 0x00000001, /* EMC_WDV */
+ 0x00000003, /* EMC_QUSE */
+ 0x00000001, /* EMC_QRST */
+ 0x00000009, /* EMC_QSAFE */
+ 0x0000000a, /* EMC_RDV */
+ 0x0000005e, /* EMC_REFRESH */
+ 0x00000000, /* EMC_BURST_REFRESH_NUM */
+ 0x00000017, /* EMC_PRE_REFRESH_REQ_CNT */
+ 0x00000001, /* EMC_PDEX2WR */
+ 0x00000001, /* EMC_PDEX2RD */
+ 0x00000002, /* EMC_PCHG2PDEN */
+ 0x00000000, /* EMC_ACT2PDEN */
+ 0x00000001, /* EMC_AR2PDEN */
+ 0x00000007, /* EMC_RW2PDEN */
+ 0x00000004, /* EMC_TXSR */
+ 0x00000004, /* EMC_TXSRDLL */
+ 0x00000003, /* EMC_TCKE */
+ 0x00000008, /* EMC_TFAW */
+ 0x00000004, /* EMC_TRPAB */
+ 0x00000004, /* EMC_TCLKSTABLE */
+ 0x00000002, /* EMC_TCLKSTOP */
+ 0x00000068, /* EMC_TREFBW */
+ 0x00000004, /* EMC_QUSE_EXTRA */
+ 0x00000004, /* EMC_FBIO_CFG6 */
+ 0x00000000, /* EMC_ODT_WRITE */
+ 0x00000000, /* EMC_ODT_READ */
+ 0x00004282, /* EMC_FBIO_CFG5 */
+ 0x00780084, /* EMC_CFG_DIG_DLL */
+ 0x00008000, /* EMC_CFG_DIG_DLL_PERIOD */
+ 0x00090000, /* EMC_DLL_XFORM_DQS0 */
+ 0x00090000, /* EMC_DLL_XFORM_DQS1 */
+ 0x00090000, /* EMC_DLL_XFORM_DQS2 */
+ 0x00090000, /* EMC_DLL_XFORM_DQS3 */
+ 0x00000010, /* EMC_DLL_XFORM_DQS4 */
+ 0x00000010, /* EMC_DLL_XFORM_DQS5 */
+ 0x00000010, /* EMC_DLL_XFORM_DQS6 */
+ 0x00000010, /* EMC_DLL_XFORM_DQS7 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE0 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE1 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE2 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE3 */
+ 0x00000008, /* EMC_DLL_XFORM_QUSE4 */
+ 0x00000008, /* EMC_DLL_XFORM_QUSE5 */
+ 0x00000008, /* EMC_DLL_XFORM_QUSE6 */
+ 0x00000008, /* EMC_DLL_XFORM_QUSE7 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS0 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS1 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS2 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS3 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS4 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS5 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS6 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS7 */
+ 0x00088000, /* EMC_DLL_XFORM_DQ0 */
+ 0x00088000, /* EMC_DLL_XFORM_DQ1 */
+ 0x00088000, /* EMC_DLL_XFORM_DQ2 */
+ 0x00088000, /* EMC_DLL_XFORM_DQ3 */
+ 0x00100220, /* EMC_XM2CMDPADCTRL */
+ 0x0800201c, /* EMC_XM2DQSPADCTRL2 */
+ 0x00000000, /* EMC_XM2DQPADCTRL2 */
+ 0x77ffc004, /* EMC_XM2CLKPADCTRL */
+ 0x01f1f008, /* EMC_XM2COMPPADCTRL */
+ 0x00000000, /* EMC_XM2VTTGENPADCTRL */
+ 0x00000007, /* EMC_XM2VTTGENPADCTRL2 */
+ 0x08000068, /* EMC_XM2QUSEPADCTRL */
+ 0x08000000, /* EMC_XM2DQSPADCTRL3 */
+ 0x00000802, /* EMC_CTT_TERM_CTRL */
+ 0x00064000, /* EMC_ZCAL_INTERVAL */
+ 0x0000000a, /* EMC_ZCAL_WAIT_CNT */
+ 0x00090009, /* EMC_MRS_WAIT_CNT */
+ 0xa0f10000, /* EMC_AUTO_CAL_CONFIG */
+ 0x00000000, /* EMC_CTT */
+ 0x00000000, /* EMC_CTT_DURATION */
+ 0x800001c2, /* EMC_DYN_SELF_REF_CONTROL */
+ 0x00020001, /* MC_EMEM_ARB_CFG */
+ 0x80000008, /* MC_EMEM_ARB_OUTSTANDING_REQ */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_RCD */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_RP */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_RC */
+ 0x00000000, /* MC_EMEM_ARB_TIMING_RAS */
+ 0x00000003, /* MC_EMEM_ARB_TIMING_FAW */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_RRD */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_RAP2PRE */
+ 0x00000004, /* MC_EMEM_ARB_TIMING_WAP2PRE */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_R2R */
+ 0x00000000, /* MC_EMEM_ARB_TIMING_W2W */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_R2W */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_W2R */
+ 0x02020001, /* MC_EMEM_ARB_DA_TURNS */
+ 0x00060402, /* MC_EMEM_ARB_DA_COVERS */
+ 0x74030303, /* MC_EMEM_ARB_MISC0 */
+ 0x001f0000, /* MC_EMEM_ARB_RING1_THROTTLE */
+ 0x50000000, /* EMC_FBIO_SPARE */
+ 0xff00ff00, /* EMC_CFG_RSV */
+ },
+ 0x00000009, /* EMC_ZCAL_WAIT_CNT after clock change */
+ 0x001fffff, /* EMC_AUTO_CAL_INTERVAL */
+ 0x00000001, /* EMC_CFG.PERIODIC_QRST */
+ 0x00000000, /* Mode Register 0 */
+ 0x00010022, /* Mode Register 1 */
+ 0x00020001, /* Mode Register 2 */
+ },
+ {
+ 0x31, /* Rev 3.1 */
+ 51000, /* SDRAM frequency */
+ {
+ 0x00000003, /* EMC_RC */
+ 0x00000006, /* EMC_RFC */
+ 0x00000002, /* EMC_RAS */
+ 0x00000002, /* EMC_RP */
+ 0x00000004, /* EMC_R2W */
+ 0x00000004, /* EMC_W2R */
+ 0x00000001, /* EMC_R2P */
+ 0x00000005, /* EMC_W2P */
+ 0x00000002, /* EMC_RD_RCD */
+ 0x00000002, /* EMC_WR_RCD */
+ 0x00000001, /* EMC_RRD */
+ 0x00000001, /* EMC_REXT */
+ 0x00000000, /* EMC_WEXT */
+ 0x00000001, /* EMC_WDV */
+ 0x00000003, /* EMC_QUSE */
+ 0x00000001, /* EMC_QRST */
+ 0x00000009, /* EMC_QSAFE */
+ 0x0000000a, /* EMC_RDV */
+ 0x000000c0, /* EMC_REFRESH */
+ 0x00000000, /* EMC_BURST_REFRESH_NUM */
+ 0x00000030, /* EMC_PRE_REFRESH_REQ_CNT */
+ 0x00000001, /* EMC_PDEX2WR */
+ 0x00000001, /* EMC_PDEX2RD */
+ 0x00000002, /* EMC_PCHG2PDEN */
+ 0x00000000, /* EMC_ACT2PDEN */
+ 0x00000001, /* EMC_AR2PDEN */
+ 0x00000007, /* EMC_RW2PDEN */
+ 0x00000008, /* EMC_TXSR */
+ 0x00000008, /* EMC_TXSRDLL */
+ 0x00000003, /* EMC_TCKE */
+ 0x00000008, /* EMC_TFAW */
+ 0x00000004, /* EMC_TRPAB */
+ 0x00000004, /* EMC_TCLKSTABLE */
+ 0x00000002, /* EMC_TCLKSTOP */
+ 0x000000d5, /* EMC_TREFBW */
+ 0x00000004, /* EMC_QUSE_EXTRA */
+ 0x00000004, /* EMC_FBIO_CFG6 */
+ 0x00000000, /* EMC_ODT_WRITE */
+ 0x00000000, /* EMC_ODT_READ */
+ 0x00004282, /* EMC_FBIO_CFG5 */
+ 0x00780084, /* EMC_CFG_DIG_DLL */
+ 0x00008000, /* EMC_CFG_DIG_DLL_PERIOD */
+ 0x00090000, /* EMC_DLL_XFORM_DQS0 */
+ 0x00090000, /* EMC_DLL_XFORM_DQS1 */
+ 0x00090000, /* EMC_DLL_XFORM_DQS2 */
+ 0x00090000, /* EMC_DLL_XFORM_DQS3 */
+ 0x00000010, /* EMC_DLL_XFORM_DQS4 */
+ 0x00000010, /* EMC_DLL_XFORM_DQS5 */
+ 0x00000010, /* EMC_DLL_XFORM_DQS6 */
+ 0x00000010, /* EMC_DLL_XFORM_DQS7 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE0 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE1 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE2 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE3 */
+ 0x00000018, /* EMC_DLL_XFORM_QUSE4 */
+ 0x00000018, /* EMC_DLL_XFORM_QUSE5 */
+ 0x00000018, /* EMC_DLL_XFORM_QUSE6 */
+ 0x00000018, /* EMC_DLL_XFORM_QUSE7 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS0 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS1 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS2 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS3 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS4 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS5 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS6 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS7 */
+ 0x00088000, /* EMC_DLL_XFORM_DQ0 */
+ 0x00088000, /* EMC_DLL_XFORM_DQ1 */
+ 0x00088000, /* EMC_DLL_XFORM_DQ2 */
+ 0x00088000, /* EMC_DLL_XFORM_DQ3 */
+ 0x00100220, /* EMC_XM2CMDPADCTRL */
+ 0x0800201c, /* EMC_XM2DQSPADCTRL2 */
+ 0x00000000, /* EMC_XM2DQPADCTRL2 */
+ 0x77ffc004, /* EMC_XM2CLKPADCTRL */
+ 0x01f1f008, /* EMC_XM2COMPPADCTRL */
+ 0x00000000, /* EMC_XM2VTTGENPADCTRL */
+ 0x00000007, /* EMC_XM2VTTGENPADCTRL2 */
+ 0x08000068, /* EMC_XM2QUSEPADCTRL */
+ 0x08000000, /* EMC_XM2DQSPADCTRL3 */
+ 0x00000802, /* EMC_CTT_TERM_CTRL */
+ 0x00064000, /* EMC_ZCAL_INTERVAL */
+ 0x00000013, /* EMC_ZCAL_WAIT_CNT */
+ 0x00090009, /* EMC_MRS_WAIT_CNT */
+ 0xa0f10000, /* EMC_AUTO_CAL_CONFIG */
+ 0x00000000, /* EMC_CTT */
+ 0x00000000, /* EMC_CTT_DURATION */
+ 0x80000287, /* EMC_DYN_SELF_REF_CONTROL */
+ 0x00010001, /* MC_EMEM_ARB_CFG */
+ 0x8000000a, /* MC_EMEM_ARB_OUTSTANDING_REQ */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_RCD */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_RP */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_RC */
+ 0x00000000, /* MC_EMEM_ARB_TIMING_RAS */
+ 0x00000003, /* MC_EMEM_ARB_TIMING_FAW */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_RRD */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_RAP2PRE */
+ 0x00000004, /* MC_EMEM_ARB_TIMING_WAP2PRE */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_R2R */
+ 0x00000000, /* MC_EMEM_ARB_TIMING_W2W */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_R2W */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_W2R */
+ 0x02020001, /* MC_EMEM_ARB_DA_TURNS */
+ 0x00060402, /* MC_EMEM_ARB_DA_COVERS */
+ 0x72c30303, /* MC_EMEM_ARB_MISC0 */
+ 0x001f0000, /* MC_EMEM_ARB_RING1_THROTTLE */
+ 0x50000000, /* EMC_FBIO_SPARE */
+ 0xff00ff00, /* EMC_CFG_RSV */
+ },
+ 0x00000009, /* EMC_ZCAL_WAIT_CNT after clock change */
+ 0x001fffff, /* EMC_AUTO_CAL_INTERVAL */
+ 0x00000001, /* EMC_CFG.PERIODIC_QRST */
+ 0x00000000, /* Mode Register 0 */
+ 0x00010022, /* Mode Register 1 */
+ 0x00020001, /* Mode Register 2 */
+ },
+ {
+ 0x31, /* Rev 3.1 */
+ 102000, /* SDRAM frequency */
+ {
+ 0x00000006, /* EMC_RC */
+ 0x0000000d, /* EMC_RFC */
+ 0x00000004, /* EMC_RAS */
+ 0x00000002, /* EMC_RP */
+ 0x00000004, /* EMC_R2W */
+ 0x00000004, /* EMC_W2R */
+ 0x00000001, /* EMC_R2P */
+ 0x00000005, /* EMC_W2P */
+ 0x00000002, /* EMC_RD_RCD */
+ 0x00000002, /* EMC_WR_RCD */
+ 0x00000001, /* EMC_RRD */
+ 0x00000001, /* EMC_REXT */
+ 0x00000000, /* EMC_WEXT */
+ 0x00000001, /* EMC_WDV */
+ 0x00000003, /* EMC_QUSE */
+ 0x00000001, /* EMC_QRST */
+ 0x00000009, /* EMC_QSAFE */
+ 0x0000000a, /* EMC_RDV */
+ 0x00000181, /* EMC_REFRESH */
+ 0x00000000, /* EMC_BURST_REFRESH_NUM */
+ 0x00000060, /* EMC_PRE_REFRESH_REQ_CNT */
+ 0x00000001, /* EMC_PDEX2WR */
+ 0x00000001, /* EMC_PDEX2RD */
+ 0x00000002, /* EMC_PCHG2PDEN */
+ 0x00000000, /* EMC_ACT2PDEN */
+ 0x00000001, /* EMC_AR2PDEN */
+ 0x00000007, /* EMC_RW2PDEN */
+ 0x0000000f, /* EMC_TXSR */
+ 0x0000000f, /* EMC_TXSRDLL */
+ 0x00000003, /* EMC_TCKE */
+ 0x00000008, /* EMC_TFAW */
+ 0x00000004, /* EMC_TRPAB */
+ 0x00000004, /* EMC_TCLKSTABLE */
+ 0x00000002, /* EMC_TCLKSTOP */
+ 0x000001a9, /* EMC_TREFBW */
+ 0x00000004, /* EMC_QUSE_EXTRA */
+ 0x00000006, /* EMC_FBIO_CFG6 */
+ 0x00000000, /* EMC_ODT_WRITE */
+ 0x00000000, /* EMC_ODT_READ */
+ 0x00004282, /* EMC_FBIO_CFG5 */
+ 0x00780084, /* EMC_CFG_DIG_DLL */
+ 0x00008000, /* EMC_CFG_DIG_DLL_PERIOD */
+ 0x00090000, /* EMC_DLL_XFORM_DQS0 */
+ 0x00090000, /* EMC_DLL_XFORM_DQS1 */
+ 0x00090000, /* EMC_DLL_XFORM_DQS2 */
+ 0x00090000, /* EMC_DLL_XFORM_DQS3 */
+ 0x00000010, /* EMC_DLL_XFORM_DQS4 */
+ 0x00000010, /* EMC_DLL_XFORM_DQS5 */
+ 0x00000010, /* EMC_DLL_XFORM_DQS6 */
+ 0x00000010, /* EMC_DLL_XFORM_DQS7 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE0 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE1 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE2 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE3 */
+ 0x00000008, /* EMC_DLL_XFORM_QUSE4 */
+ 0x00000008, /* EMC_DLL_XFORM_QUSE5 */
+ 0x00000008, /* EMC_DLL_XFORM_QUSE6 */
+ 0x00000008, /* EMC_DLL_XFORM_QUSE7 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS0 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS1 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS2 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS3 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS4 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS5 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS6 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS7 */
+ 0x00088000, /* EMC_DLL_XFORM_DQ0 */
+ 0x00088000, /* EMC_DLL_XFORM_DQ1 */
+ 0x00088000, /* EMC_DLL_XFORM_DQ2 */
+ 0x00088000, /* EMC_DLL_XFORM_DQ3 */
+ 0x00100220, /* EMC_XM2CMDPADCTRL */
+ 0x0800201c, /* EMC_XM2DQSPADCTRL2 */
+ 0x00000000, /* EMC_XM2DQPADCTRL2 */
+ 0x77ffc004, /* EMC_XM2CLKPADCTRL */
+ 0x01f1f008, /* EMC_XM2COMPPADCTRL */
+ 0x00000000, /* EMC_XM2VTTGENPADCTRL */
+ 0x00000007, /* EMC_XM2VTTGENPADCTRL2 */
+ 0x08000068, /* EMC_XM2QUSEPADCTRL */
+ 0x08000000, /* EMC_XM2DQSPADCTRL3 */
+ 0x00000802, /* EMC_CTT_TERM_CTRL */
+ 0x00064000, /* EMC_ZCAL_INTERVAL */
+ 0x00000025, /* EMC_ZCAL_WAIT_CNT */
+ 0x00090009, /* EMC_MRS_WAIT_CNT */
+ 0xa0f10000, /* EMC_AUTO_CAL_CONFIG */
+ 0x00000000, /* EMC_CTT */
+ 0x00000000, /* EMC_CTT_DURATION */
+ 0x8000040b, /* EMC_DYN_SELF_REF_CONTROL */
+ 0x00000001, /* MC_EMEM_ARB_CFG */
+ 0x80000013, /* MC_EMEM_ARB_OUTSTANDING_REQ */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_RCD */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_RP */
+ 0x00000003, /* MC_EMEM_ARB_TIMING_RC */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_RAS */
+ 0x00000003, /* MC_EMEM_ARB_TIMING_FAW */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_RRD */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_RAP2PRE */
+ 0x00000004, /* MC_EMEM_ARB_TIMING_WAP2PRE */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_R2R */
+ 0x00000000, /* MC_EMEM_ARB_TIMING_W2W */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_R2W */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_W2R */
+ 0x02020001, /* MC_EMEM_ARB_DA_TURNS */
+ 0x00060403, /* MC_EMEM_ARB_DA_COVERS */
+ 0x72430504, /* MC_EMEM_ARB_MISC0 */
+ 0x001f0000, /* MC_EMEM_ARB_RING1_THROTTLE */
+ 0x50000000, /* EMC_FBIO_SPARE */
+ 0xff00ff00, /* EMC_CFG_RSV */
+ },
+ 0x0000000a, /* EMC_ZCAL_WAIT_CNT after clock change */
+ 0x001fffff, /* EMC_AUTO_CAL_INTERVAL */
+ 0x00000001, /* EMC_CFG.PERIODIC_QRST */
+ 0x00000000, /* Mode Register 0 */
+ 0x00010022, /* Mode Register 1 */
+ 0x00020001, /* Mode Register 2 */
+ },
+ {
+ 0x31, /* Rev 3.1 */
+ 204000, /* SDRAM frequency */
+ {
+ 0x0000000c, /* EMC_RC */
+ 0x0000001a, /* EMC_RFC */
+ 0x00000008, /* EMC_RAS */
+ 0x00000003, /* EMC_RP */
+ 0x00000005, /* EMC_R2W */
+ 0x00000004, /* EMC_W2R */
+ 0x00000001, /* EMC_R2P */
+ 0x00000006, /* EMC_W2P */
+ 0x00000003, /* EMC_RD_RCD */
+ 0x00000003, /* EMC_WR_RCD */
+ 0x00000002, /* EMC_RRD */
+ 0x00000002, /* EMC_REXT */
+ 0x00000000, /* EMC_WEXT */
+ 0x00000001, /* EMC_WDV */
+ 0x00000003, /* EMC_QUSE */
+ 0x00000001, /* EMC_QRST */
+ 0x0000000b, /* EMC_QSAFE */
+ 0x0000000a, /* EMC_RDV */
+ 0x00000303, /* EMC_REFRESH */
+ 0x00000000, /* EMC_BURST_REFRESH_NUM */
+ 0x000000c0, /* EMC_PRE_REFRESH_REQ_CNT */
+ 0x00000001, /* EMC_PDEX2WR */
+ 0x00000001, /* EMC_PDEX2RD */
+ 0x00000003, /* EMC_PCHG2PDEN */
+ 0x00000000, /* EMC_ACT2PDEN */
+ 0x00000001, /* EMC_AR2PDEN */
+ 0x00000007, /* EMC_RW2PDEN */
+ 0x0000001d, /* EMC_TXSR */
+ 0x0000001d, /* EMC_TXSRDLL */
+ 0x00000004, /* EMC_TCKE */
+ 0x0000000b, /* EMC_TFAW */
+ 0x00000005, /* EMC_TRPAB */
+ 0x00000004, /* EMC_TCLKSTABLE */
+ 0x00000002, /* EMC_TCLKSTOP */
+ 0x00000351, /* EMC_TREFBW */
+ 0x00000004, /* EMC_QUSE_EXTRA */
+ 0x00000006, /* EMC_FBIO_CFG6 */
+ 0x00000000, /* EMC_ODT_WRITE */
+ 0x00000000, /* EMC_ODT_READ */
+ 0x00004282, /* EMC_FBIO_CFG5 */
+ 0x00440084, /* EMC_CFG_DIG_DLL */
+ 0x00008000, /* EMC_CFG_DIG_DLL_PERIOD */
+ 0x0007c000, /* EMC_DLL_XFORM_DQS0 */
+ 0x0007c000, /* EMC_DLL_XFORM_DQS1 */
+ 0x0007c000, /* EMC_DLL_XFORM_DQS2 */
+ 0x0007c000, /* EMC_DLL_XFORM_DQS3 */
+ 0x00000010, /* EMC_DLL_XFORM_DQS4 */
+ 0x00000010, /* EMC_DLL_XFORM_DQS5 */
+ 0x00000010, /* EMC_DLL_XFORM_DQS6 */
+ 0x00000010, /* EMC_DLL_XFORM_DQS7 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE0 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE1 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE2 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE3 */
+ 0x00000018, /* EMC_DLL_XFORM_QUSE4 */
+ 0x00000018, /* EMC_DLL_XFORM_QUSE5 */
+ 0x00000018, /* EMC_DLL_XFORM_QUSE6 */
+ 0x00000018, /* EMC_DLL_XFORM_QUSE7 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS0 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS1 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS2 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS3 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS4 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS5 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS6 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS7 */
+ 0x00088000, /* EMC_DLL_XFORM_DQ0 */
+ 0x00088000, /* EMC_DLL_XFORM_DQ1 */
+ 0x00088000, /* EMC_DLL_XFORM_DQ2 */
+ 0x00088000, /* EMC_DLL_XFORM_DQ3 */
+ 0x000f0220, /* EMC_XM2CMDPADCTRL */
+ 0x0800201c, /* EMC_XM2DQSPADCTRL2 */
+ 0x00000000, /* EMC_XM2DQPADCTRL2 */
+ 0x77ffc004, /* EMC_XM2CLKPADCTRL */
+ 0x01f1f008, /* EMC_XM2COMPPADCTRL */
+ 0x00000000, /* EMC_XM2VTTGENPADCTRL */
+ 0x00000007, /* EMC_XM2VTTGENPADCTRL2 */
+ 0x08000068, /* EMC_XM2QUSEPADCTRL */
+ 0x08000000, /* EMC_XM2DQSPADCTRL3 */
+ 0x00000802, /* EMC_CTT_TERM_CTRL */
+ 0x00064000, /* EMC_ZCAL_INTERVAL */
+ 0x0000004a, /* EMC_ZCAL_WAIT_CNT */
+ 0x00090009, /* EMC_MRS_WAIT_CNT */
+ 0xa0f10000, /* EMC_AUTO_CAL_CONFIG */
+ 0x00000000, /* EMC_CTT */
+ 0x00000000, /* EMC_CTT_DURATION */
+ 0x80000713, /* EMC_DYN_SELF_REF_CONTROL */
+ 0x00000003, /* MC_EMEM_ARB_CFG */
+ 0x80000025, /* MC_EMEM_ARB_OUTSTANDING_REQ */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_RCD */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_RP */
+ 0x00000006, /* MC_EMEM_ARB_TIMING_RC */
+ 0x00000003, /* MC_EMEM_ARB_TIMING_RAS */
+ 0x00000005, /* MC_EMEM_ARB_TIMING_FAW */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_RRD */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_RAP2PRE */
+ 0x00000004, /* MC_EMEM_ARB_TIMING_WAP2PRE */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_R2R */
+ 0x00000000, /* MC_EMEM_ARB_TIMING_W2W */
+ 0x00000003, /* MC_EMEM_ARB_TIMING_R2W */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_W2R */
+ 0x02030001, /* MC_EMEM_ARB_DA_TURNS */
+ 0x00070506, /* MC_EMEM_ARB_DA_COVERS */
+ 0x71e40a07, /* MC_EMEM_ARB_MISC0 */
+ 0x001f0000, /* MC_EMEM_ARB_RING1_THROTTLE */
+ 0x50000000, /* EMC_FBIO_SPARE */
+ 0xff00ff00, /* EMC_CFG_RSV */
+ },
+ 0x00000013, /* EMC_ZCAL_WAIT_CNT after clock change */
+ 0x001fffff, /* EMC_AUTO_CAL_INTERVAL */
+ 0x00000001, /* EMC_CFG.PERIODIC_QRST */
+ 0x00000000, /* Mode Register 0 */
+ 0x00010042, /* Mode Register 1 */
+ 0x00020001, /* Mode Register 2 */
+ },
+ {
+ 0x31, /* Rev 3.1 */
+ 400000, /* SDRAM frequency */
+ {
+ 0x00000017, /* EMC_RC */
+ 0x00000033, /* EMC_RFC */
+ 0x00000010, /* EMC_RAS */
+ 0x00000007, /* EMC_RP */
+ 0x00000007, /* EMC_R2W */
+ 0x00000007, /* EMC_W2R */
+ 0x00000002, /* EMC_R2P */
+ 0x0000000a, /* EMC_W2P */
+ 0x00000007, /* EMC_RD_RCD */
+ 0x00000007, /* EMC_WR_RCD */
+ 0x00000003, /* EMC_RRD */
+ 0x00000002, /* EMC_REXT */
+ 0x00000000, /* EMC_WEXT */
+ 0x00000003, /* EMC_WDV */
+ 0x00000007, /* EMC_QUSE */
+ 0x00000004, /* EMC_QRST */
+ 0x0000000b, /* EMC_QSAFE */
+ 0x0000000e, /* EMC_RDV */
+ 0x000005e9, /* EMC_REFRESH */
+ 0x00000000, /* EMC_BURST_REFRESH_NUM */
+ 0x0000017a, /* EMC_PRE_REFRESH_REQ_CNT */
+ 0x00000002, /* EMC_PDEX2WR */
+ 0x00000002, /* EMC_PDEX2RD */
+ 0x00000007, /* EMC_PCHG2PDEN */
+ 0x00000000, /* EMC_ACT2PDEN */
+ 0x00000001, /* EMC_AR2PDEN */
+ 0x0000000c, /* EMC_RW2PDEN */
+ 0x00000038, /* EMC_TXSR */
+ 0x00000038, /* EMC_TXSRDLL */
+ 0x00000006, /* EMC_TCKE */
+ 0x00000014, /* EMC_TFAW */
+ 0x00000009, /* EMC_TRPAB */
+ 0x00000004, /* EMC_TCLKSTABLE */
+ 0x00000002, /* EMC_TCLKSTOP */
+ 0x00000680, /* EMC_TREFBW */
+ 0x00000000, /* EMC_QUSE_EXTRA */
+ 0x00000006, /* EMC_FBIO_CFG6 */
+ 0x00000000, /* EMC_ODT_WRITE */
+ 0x00000000, /* EMC_ODT_READ */
+ 0x00006282, /* EMC_FBIO_CFG5 */
+ 0x001d0084, /* EMC_CFG_DIG_DLL */
+ 0x00008000, /* EMC_CFG_DIG_DLL_PERIOD */
+ 0x00024000, /* EMC_DLL_XFORM_DQS0 */
+ 0x00024000, /* EMC_DLL_XFORM_DQS1 */
+ 0x00024000, /* EMC_DLL_XFORM_DQS2 */
+ 0x00024000, /* EMC_DLL_XFORM_DQS3 */
+ 0x00000010, /* EMC_DLL_XFORM_DQS4 */
+ 0x00000010, /* EMC_DLL_XFORM_DQS5 */
+ 0x00000010, /* EMC_DLL_XFORM_DQS6 */
+ 0x00000010, /* EMC_DLL_XFORM_DQS7 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE0 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE1 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE2 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE3 */
+ 0x00000008, /* EMC_DLL_XFORM_QUSE4 */
+ 0x00000008, /* EMC_DLL_XFORM_QUSE5 */
+ 0x00000008, /* EMC_DLL_XFORM_QUSE6 */
+ 0x00000008, /* EMC_DLL_XFORM_QUSE7 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS0 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS1 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS2 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS3 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS4 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS5 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS6 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS7 */
+ 0x00048000, /* EMC_DLL_XFORM_DQ0 */
+ 0x00048000, /* EMC_DLL_XFORM_DQ1 */
+ 0x00048000, /* EMC_DLL_XFORM_DQ2 */
+ 0x00048000, /* EMC_DLL_XFORM_DQ3 */
+ 0x00060220, /* EMC_XM2CMDPADCTRL */
+ 0x0800003d, /* EMC_XM2DQSPADCTRL2 */
+ 0x00000000, /* EMC_XM2DQPADCTRL2 */
+ 0x77ffc004, /* EMC_XM2CLKPADCTRL */
+ 0x01f1f408, /* EMC_XM2COMPPADCTRL */
+ 0x00000000, /* EMC_XM2VTTGENPADCTRL */
+ 0x00000007, /* EMC_XM2VTTGENPADCTRL2 */
+ 0x08000068, /* EMC_XM2QUSEPADCTRL */
+ 0x08000000, /* EMC_XM2DQSPADCTRL3 */
+ 0x00000802, /* EMC_CTT_TERM_CTRL */
+ 0x00064000, /* EMC_ZCAL_INTERVAL */
+ 0x00000090, /* EMC_ZCAL_WAIT_CNT */
+ 0x000c000c, /* EMC_MRS_WAIT_CNT */
+ 0xa0f10000, /* EMC_AUTO_CAL_CONFIG */
+ 0x00000000, /* EMC_CTT */
+ 0x00000000, /* EMC_CTT_DURATION */
+ 0x80000ce6, /* EMC_DYN_SELF_REF_CONTROL */
+ 0x00000006, /* MC_EMEM_ARB_CFG */
+ 0x80000048, /* MC_EMEM_ARB_OUTSTANDING_REQ */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_RCD */
+ 0x00000003, /* MC_EMEM_ARB_TIMING_RP */
+ 0x0000000c, /* MC_EMEM_ARB_TIMING_RC */
+ 0x00000007, /* MC_EMEM_ARB_TIMING_RAS */
+ 0x00000009, /* MC_EMEM_ARB_TIMING_FAW */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_RRD */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_RAP2PRE */
+ 0x00000006, /* MC_EMEM_ARB_TIMING_WAP2PRE */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_R2R */
+ 0x00000000, /* MC_EMEM_ARB_TIMING_W2W */
+ 0x00000004, /* MC_EMEM_ARB_TIMING_R2W */
+ 0x00000004, /* MC_EMEM_ARB_TIMING_W2R */
+ 0x04040001, /* MC_EMEM_ARB_DA_TURNS */
+ 0x000d090c, /* MC_EMEM_ARB_DA_COVERS */
+ 0x71c6120d, /* MC_EMEM_ARB_MISC0 */
+ 0x001f0000, /* MC_EMEM_ARB_RING1_THROTTLE */
+ 0x10000000, /* EMC_FBIO_SPARE */
+ 0xff00ff00, /* EMC_CFG_RSV */
+ },
+ 0x00000024, /* EMC_ZCAL_WAIT_CNT after clock change */
+ 0x001fffff, /* EMC_AUTO_CAL_INTERVAL */
+ 0x00000001, /* EMC_CFG.PERIODIC_QRST */
+ 0x00000000, /* Mode Register 0 */
+ 0x00010082, /* Mode Register 1 */
+ 0x00020004, /* Mode Register 2 */
+ },
+};
+
+int enterprise_emc_init(void)
+{
+ tegra_init_emc(enterprise_emc_tables_h5tc2g,
+ ARRAY_SIZE(enterprise_emc_tables_h5tc2g));
+ return 0;
+}
diff --git a/arch/arm/mach-tegra/board-enterprise-panel.c b/arch/arm/mach-tegra/board-enterprise-panel.c
new file mode 100644
index 000000000000..3adc9b8e5fad
--- /dev/null
+++ b/arch/arm/mach-tegra/board-enterprise-panel.c
@@ -0,0 +1,826 @@
+/*
+ * arch/arm/mach-tegra/board-enterprise-panel.c
+ *
+ * Copyright (c) 2011, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/resource.h>
+#include <asm/mach-types.h>
+#include <linux/platform_device.h>
+#include <linux/earlysuspend.h>
+#include <linux/tegra_pwm_bl.h>
+#include <asm/atomic.h>
+#include <linux/nvhost.h>
+#include <mach/nvmap.h>
+#include <mach/irqs.h>
+#include <mach/iomap.h>
+#include <mach/dc.h>
+#include <mach/fb.h>
+#include <mach/hardware.h>
+
+#include "board.h"
+#include "board-enterprise.h"
+#include "devices.h"
+#include "gpio-names.h"
+
+#define DC_CTRL_MODE TEGRA_DC_OUT_ONE_SHOT_MODE
+
+/* Select panel to be used. */
+#define AVDD_LCD PMU_TCA6416_GPIO_PORT17
+#define DSI_PANEL_RESET 1
+
+#define enterprise_lvds_shutdown TEGRA_GPIO_PL2
+#define enterprise_hdmi_hpd TEGRA_GPIO_PN7
+
+#define enterprise_dsi_panel_reset TEGRA_GPIO_PW0
+
+#define enterprise_lcd_2d_3d TEGRA_GPIO_PH1
+#define ENTERPRISE_STEREO_3D 0
+#define ENTERPRISE_STEREO_2D 1
+
+#define enterprise_lcd_swp_pl TEGRA_GPIO_PH2
+#define ENTERPRISE_STEREO_LANDSCAPE 0
+#define ENTERPRISE_STEREO_PORTRAIT 1
+
+#define enterprise_lcd_te TEGRA_GPIO_PJ1
+
+#ifdef CONFIG_TEGRA_DC
+static struct regulator *enterprise_dsi_reg = NULL;
+
+static struct regulator *enterprise_hdmi_reg;
+static struct regulator *enterprise_hdmi_pll;
+static struct regulator *enterprise_hdmi_vddio;
+#endif
+
+static atomic_t sd_brightness = ATOMIC_INIT(255);
+
+static tegra_dc_bl_output enterprise_bl_output_measured = {
+ 1, 5, 9, 10, 11, 12, 12, 13,
+ 13, 14, 14, 15, 15, 16, 16, 17,
+ 17, 18, 18, 19, 19, 20, 21, 21,
+ 22, 22, 23, 24, 24, 25, 26, 26,
+ 27, 27, 28, 29, 29, 31, 31, 32,
+ 32, 33, 34, 35, 36, 36, 37, 38,
+ 39, 39, 40, 41, 41, 42, 43, 43,
+ 44, 45, 45, 46, 47, 47, 48, 49,
+ 49, 50, 51, 51, 52, 53, 53, 54,
+ 55, 56, 56, 57, 58, 59, 60, 61,
+ 61, 62, 63, 64, 65, 65, 66, 67,
+ 67, 68, 69, 69, 70, 71, 71, 72,
+ 73, 73, 74, 74, 75, 76, 76, 77,
+ 77, 78, 79, 79, 80, 81, 82, 83,
+ 83, 84, 85, 85, 86, 86, 88, 89,
+ 90, 91, 91, 92, 93, 93, 94, 95,
+ 95, 96, 97, 97, 98, 99, 99, 100,
+ 101, 101, 102, 103, 103, 104, 105, 105,
+ 107, 107, 108, 109, 110, 111, 111, 112,
+ 113, 113, 114, 115, 115, 116, 117, 117,
+ 118, 119, 119, 120, 121, 122, 123, 124,
+ 124, 125, 126, 126, 127, 128, 129, 129,
+ 130, 131, 131, 132, 133, 133, 134, 135,
+ 135, 136, 137, 137, 138, 139, 139, 140,
+ 142, 142, 143, 144, 145, 146, 147, 147,
+ 148, 149, 149, 150, 151, 152, 153, 153,
+ 153, 154, 155, 156, 157, 158, 158, 159,
+ 160, 161, 162, 163, 163, 164, 165, 165,
+ 166, 166, 167, 168, 169, 169, 170, 170,
+ 171, 172, 173, 173, 174, 175, 175, 176,
+ 176, 178, 178, 179, 180, 181, 182, 182,
+ 183, 184, 185, 186, 186, 187, 188, 188
+};
+
+static p_tegra_dc_bl_output bl_output;
+
+static bool kernel_1st_panel_init = true;
+
+static int enterprise_backlight_notify(struct device *unused, int brightness)
+{
+ int cur_sd_brightness = atomic_read(&sd_brightness);
+ int orig_brightness = brightness;
+
+ /* SD brightness is a percentage, 8-bit value. */
+ brightness = (brightness * cur_sd_brightness) / 255;
+
+ /* Apply any backlight response curve */
+ if (brightness > 255)
+ pr_info("Error: Brightness > 255!\n");
+ else
+ brightness = bl_output[brightness];
+
+ return brightness;
+}
+
+static int enterprise_disp1_check_fb(struct device *dev, struct fb_info *info);
+
+/*
+ * In case which_pwm is TEGRA_PWM_PM0,
+ * gpio_conf_to_sfio should be TEGRA_GPIO_PW0: set LCD_CS1_N pin to SFIO
+ * In case which_pwm is TEGRA_PWM_PM1,
+ * gpio_conf_to_sfio should be TEGRA_GPIO_PW1: set LCD_M1 pin to SFIO
+ */
+static struct platform_tegra_pwm_backlight_data enterprise_disp1_backlight_data = {
+ .which_dc = 0,
+ .which_pwm = TEGRA_PWM_PM1,
+ .gpio_conf_to_sfio = TEGRA_GPIO_PW1,
+ .switch_to_sfio = &tegra_gpio_disable,
+ .max_brightness = 255,
+ .dft_brightness = 224,
+ .notify = enterprise_backlight_notify,
+ .period = 0xFF,
+ .clk_div = 0x3FF,
+ .clk_select = 0,
+ /* Only toggle backlight on fb blank notifications for disp1 */
+ .check_fb = enterprise_disp1_check_fb,
+};
+
+static struct platform_device enterprise_disp1_backlight_device = {
+ .name = "tegra-pwm-bl",
+ .id = -1,
+ .dev = {
+ .platform_data = &enterprise_disp1_backlight_data,
+ },
+};
+
+#ifdef CONFIG_TEGRA_DC
+static int enterprise_hdmi_vddio_enable(void)
+{
+ int ret;
+ if (!enterprise_hdmi_vddio) {
+ enterprise_hdmi_vddio = regulator_get(NULL, "hdmi_5v0");
+ if (IS_ERR_OR_NULL(enterprise_hdmi_vddio)) {
+ ret = PTR_ERR(enterprise_hdmi_vddio);
+ pr_err("hdmi: couldn't get regulator hdmi_5v0\n");
+ enterprise_hdmi_vddio = NULL;
+ return ret;
+ }
+ }
+ ret = regulator_enable(enterprise_hdmi_vddio);
+ if (ret < 0) {
+ pr_err("hdmi: couldn't enable regulator hdmi_5v0\n");
+ regulator_put(enterprise_hdmi_vddio);
+ enterprise_hdmi_vddio = NULL;
+ return ret;
+ }
+ return ret;
+}
+
+static int enterprise_hdmi_vddio_disable(void)
+{
+ if (enterprise_hdmi_vddio) {
+ regulator_disable(enterprise_hdmi_vddio);
+ regulator_put(enterprise_hdmi_vddio);
+ enterprise_hdmi_vddio = NULL;
+ }
+ return 0;
+}
+
+static int enterprise_hdmi_enable(void)
+{
+ int ret;
+ if (!enterprise_hdmi_reg) {
+ enterprise_hdmi_reg = regulator_get(NULL, "avdd_hdmi");
+ if (IS_ERR_OR_NULL(enterprise_hdmi_reg)) {
+ pr_err("hdmi: couldn't get regulator avdd_hdmi\n");
+ enterprise_hdmi_reg = NULL;
+ return PTR_ERR(enterprise_hdmi_reg);
+ }
+ }
+ ret = regulator_enable(enterprise_hdmi_reg);
+ if (ret < 0) {
+ pr_err("hdmi: couldn't enable regulator avdd_hdmi\n");
+ return ret;
+ }
+ if (!enterprise_hdmi_pll) {
+ enterprise_hdmi_pll = regulator_get(NULL, "avdd_hdmi_pll");
+ if (IS_ERR_OR_NULL(enterprise_hdmi_pll)) {
+ pr_err("hdmi: couldn't get regulator avdd_hdmi_pll\n");
+ enterprise_hdmi_pll = NULL;
+ regulator_put(enterprise_hdmi_reg);
+ enterprise_hdmi_reg = NULL;
+ return PTR_ERR(enterprise_hdmi_pll);
+ }
+ }
+ ret = regulator_enable(enterprise_hdmi_pll);
+ if (ret < 0) {
+ pr_err("hdmi: couldn't enable regulator avdd_hdmi_pll\n");
+ return ret;
+ }
+ return 0;
+}
+
+static int enterprise_hdmi_disable(void)
+{
+
+ regulator_disable(enterprise_hdmi_reg);
+ regulator_put(enterprise_hdmi_reg);
+ enterprise_hdmi_reg = NULL;
+
+ regulator_disable(enterprise_hdmi_pll);
+ regulator_put(enterprise_hdmi_pll);
+ enterprise_hdmi_pll = NULL;
+
+ return 0;
+}
+static struct resource enterprise_disp1_resources[] = {
+ {
+ .name = "irq",
+ .start = INT_DISPLAY_GENERAL,
+ .end = INT_DISPLAY_GENERAL,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "regs",
+ .start = TEGRA_DISPLAY_BASE,
+ .end = TEGRA_DISPLAY_BASE + TEGRA_DISPLAY_SIZE-1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .name = "fbmem",
+ .start = 0, /* Filled in by enterprise_panel_init() */
+ .end = 0, /* Filled in by enterprise_panel_init() */
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .name = "dsi_regs",
+ .start = TEGRA_DSI_BASE,
+ .end = TEGRA_DSI_BASE + TEGRA_DSI_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct resource enterprise_disp2_resources[] = {
+ {
+ .name = "irq",
+ .start = INT_DISPLAY_B_GENERAL,
+ .end = INT_DISPLAY_B_GENERAL,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "regs",
+ .start = TEGRA_DISPLAY2_BASE,
+ .end = TEGRA_DISPLAY2_BASE + TEGRA_DISPLAY2_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .name = "fbmem",
+ .flags = IORESOURCE_MEM,
+ .start = 0,
+ .end = 0,
+ },
+ {
+ .name = "hdmi_regs",
+ .start = TEGRA_HDMI_BASE,
+ .end = TEGRA_HDMI_BASE + TEGRA_HDMI_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct tegra_dc_sd_settings enterprise_sd_settings = {
+ .enable = 1, /* Normal mode operation */
+ .use_auto_pwm = false,
+ .hw_update_delay = 0,
+ .bin_width = -1,
+ .aggressiveness = 1,
+ .phase_in_adjustments = true,
+ .use_vid_luma = false,
+ /* Default video coefficients */
+ .coeff = {5, 9, 2},
+ .fc = {0, 0},
+ /* Immediate backlight changes */
+ .blp = {1024, 255},
+ /* Gammas: R: 2.2 G: 2.2 B: 2.2 */
+ /* Default BL TF */
+ .bltf = {
+ {
+ {57, 65, 74, 83},
+ {93, 103, 114, 126},
+ {138, 151, 165, 179},
+ {194, 209, 225, 242},
+ },
+ {
+ {58, 66, 75, 84},
+ {94, 105, 116, 127},
+ {140, 153, 166, 181},
+ {196, 211, 227, 244},
+ },
+ {
+ {60, 68, 77, 87},
+ {97, 107, 119, 130},
+ {143, 156, 170, 184},
+ {199, 215, 231, 248},
+ },
+ {
+ {64, 73, 82, 91},
+ {102, 113, 124, 137},
+ {149, 163, 177, 192},
+ {207, 223, 240, 255},
+ },
+ },
+ /* Default LUT */
+ .lut = {
+ {
+ {250, 250, 250},
+ {194, 194, 194},
+ {149, 149, 149},
+ {113, 113, 113},
+ {82, 82, 82},
+ {56, 56, 56},
+ {34, 34, 34},
+ {15, 15, 15},
+ {0, 0, 0},
+ },
+ {
+ {246, 246, 246},
+ {191, 191, 191},
+ {147, 147, 147},
+ {111, 111, 111},
+ {80, 80, 80},
+ {55, 55, 55},
+ {33, 33, 33},
+ {14, 14, 14},
+ {0, 0, 0},
+ },
+ {
+ {239, 239, 239},
+ {185, 185, 185},
+ {142, 142, 142},
+ {107, 107, 107},
+ {77, 77, 77},
+ {52, 52, 52},
+ {30, 30, 30},
+ {12, 12, 12},
+ {0, 0, 0},
+ },
+ {
+ {224, 224, 224},
+ {173, 173, 173},
+ {133, 133, 133},
+ {99, 99, 99},
+ {70, 70, 70},
+ {46, 46, 46},
+ {25, 25, 25},
+ {7, 7, 7},
+ {0, 0, 0},
+ },
+ },
+ .sd_brightness = &sd_brightness,
+ .bl_device = &enterprise_disp1_backlight_device,
+};
+
+static struct tegra_fb_data enterprise_hdmi_fb_data = {
+ .win = 0,
+ .xres = 1366,
+ .yres = 768,
+ .bits_per_pixel = 32,
+ .flags = TEGRA_FB_FLIP_ON_PROBE,
+};
+
+static struct tegra_dc_out enterprise_disp2_out = {
+ .type = TEGRA_DC_OUT_HDMI,
+ .flags = TEGRA_DC_OUT_HOTPLUG_HIGH,
+ .parent_clk = "pll_d2_out0",
+
+ .dcc_bus = 3,
+ .hotplug_gpio = enterprise_hdmi_hpd,
+
+ .max_pixclock = KHZ2PICOS(148500),
+
+ .align = TEGRA_DC_ALIGN_MSB,
+ .order = TEGRA_DC_ORDER_RED_BLUE,
+
+ .enable = enterprise_hdmi_enable,
+ .disable = enterprise_hdmi_disable,
+ .postsuspend = enterprise_hdmi_vddio_disable,
+ .hotplug_init = enterprise_hdmi_vddio_enable,
+};
+
+static struct tegra_dc_platform_data enterprise_disp2_pdata = {
+ .flags = 0,
+ .default_out = &enterprise_disp2_out,
+ .fb = &enterprise_hdmi_fb_data,
+ .emc_clk_rate = 300000000,
+};
+
+static int enterprise_dsi_panel_enable(void)
+{
+ int ret;
+
+ if (enterprise_dsi_reg == NULL) {
+ enterprise_dsi_reg = regulator_get(NULL, "avdd_dsi_csi");
+ if (IS_ERR_OR_NULL(enterprise_dsi_reg)) {
+ pr_err("dsi: Could not get regulator avdd_dsi_csi\n");
+ enterprise_dsi_reg = NULL;
+ return PTR_ERR(enterprise_dsi_reg);
+ }
+ }
+ ret = regulator_enable(enterprise_dsi_reg);
+ if (ret < 0) {
+ printk(KERN_ERR
+ "DSI regulator avdd_dsi_csi could not be enabled\n");
+ return ret;
+ }
+
+#if DSI_PANEL_RESET
+ if (kernel_1st_panel_init != true) {
+ ret = gpio_request(enterprise_dsi_panel_reset, "panel reset");
+ if (ret < 0)
+ return ret;
+
+ ret = gpio_direction_output(enterprise_dsi_panel_reset, 0);
+ if (ret < 0) {
+ gpio_free(enterprise_dsi_panel_reset);
+ return ret;
+ }
+ tegra_gpio_enable(enterprise_dsi_panel_reset);
+
+ gpio_set_value(enterprise_dsi_panel_reset, 0);
+ udelay(2000);
+ gpio_set_value(enterprise_dsi_panel_reset, 1);
+ mdelay(20);
+ }
+#endif
+
+ return ret;
+}
+
+static int enterprise_dsi_panel_disable(void)
+{
+#if DSI_PANEL_RESET
+ if (kernel_1st_panel_init != true) {
+ tegra_gpio_disable(enterprise_dsi_panel_reset);
+ gpio_free(enterprise_dsi_panel_reset);
+ } else
+ kernel_1st_panel_init = false;
+#endif
+ return 0;
+}
+#endif
+
+static void enterprise_stereo_set_mode(int mode)
+{
+ switch (mode) {
+ case TEGRA_DC_STEREO_MODE_2D:
+ gpio_set_value(TEGRA_GPIO_PH1, ENTERPRISE_STEREO_2D);
+ break;
+ case TEGRA_DC_STEREO_MODE_3D:
+ gpio_set_value(TEGRA_GPIO_PH1, ENTERPRISE_STEREO_3D);
+ break;
+ }
+}
+
+static void enterprise_stereo_set_orientation(int mode)
+{
+ switch (mode) {
+ case TEGRA_DC_STEREO_LANDSCAPE:
+ gpio_set_value(TEGRA_GPIO_PH2, ENTERPRISE_STEREO_LANDSCAPE);
+ break;
+ case TEGRA_DC_STEREO_PORTRAIT:
+ gpio_set_value(TEGRA_GPIO_PH2, ENTERPRISE_STEREO_PORTRAIT);
+ break;
+ }
+}
+
+#ifdef CONFIG_TEGRA_DC
+static int enterprise_dsi_panel_postsuspend(void)
+{
+ int err = 0;
+
+ if (enterprise_dsi_reg) {
+ err = regulator_disable(enterprise_dsi_reg);
+ if (err < 0)
+ printk(KERN_ERR
+ "DSI regulator avdd_dsi_csi disable failed\n");
+ regulator_put(enterprise_dsi_reg);
+ enterprise_dsi_reg = NULL;
+ }
+
+ return err;
+}
+#endif
+
+static struct tegra_dsi_cmd dsi_init_cmd[]= {
+ DSI_CMD_SHORT(0x05, 0x11, 0x00),
+ DSI_DLY_MS(20),
+#if(DC_CTRL_MODE & TEGRA_DC_OUT_ONE_SHOT_MODE)
+ DSI_CMD_SHORT(0x15, 0x35, 0x00),
+#endif
+ DSI_CMD_SHORT(0x05, 0x29, 0x00),
+ DSI_DLY_MS(20),
+};
+
+static struct tegra_dsi_cmd dsi_early_suspend_cmd[] = {
+ DSI_CMD_SHORT(0x05, 0x28, 0x00),
+ DSI_DLY_MS(20),
+#if(DC_CTRL_MODE & TEGRA_DC_OUT_ONE_SHOT_MODE)
+ DSI_CMD_SHORT(0x05, 0x34, 0x00),
+#endif
+};
+
+static struct tegra_dsi_cmd dsi_late_resume_cmd[] = {
+#if(DC_CTRL_MODE & TEGRA_DC_OUT_ONE_SHOT_MODE)
+ DSI_CMD_SHORT(0x15, 0x35, 0x00),
+#endif
+ DSI_CMD_SHORT(0x05, 0x29, 0x00),
+ DSI_DLY_MS(20),
+};
+
+static struct tegra_dsi_cmd dsi_suspend_cmd[] = {
+ DSI_CMD_SHORT(0x05, 0x28, 0x00),
+ DSI_DLY_MS(20),
+#if(DC_CTRL_MODE & TEGRA_DC_OUT_ONE_SHOT_MODE)
+ DSI_CMD_SHORT(0x05, 0x34, 0x00),
+#endif
+ DSI_CMD_SHORT(0x05, 0x10, 0x00),
+ DSI_DLY_MS(5),
+};
+
+struct tegra_dsi_out enterprise_dsi = {
+ .n_data_lanes = 2,
+ .pixel_format = TEGRA_DSI_PIXEL_FORMAT_24BIT_P,
+#if(DC_CTRL_MODE & TEGRA_DC_OUT_ONE_SHOT_MODE)
+ /* For one-shot mode, mismatch between freq of DC and TE signal
+ * may cause frame drop. We increase refreash rate a little bit
+ * more than target value to avoid missing TE signal.
+ */
+ .refresh_rate = 66,
+#else
+ .refresh_rate = 60,
+#endif
+ .virtual_channel = TEGRA_DSI_VIRTUAL_CHANNEL_0,
+
+ .panel_has_frame_buffer = true,
+ .dsi_instance = 0,
+
+ .panel_reset = DSI_PANEL_RESET,
+ .power_saving_suspend = true,
+ .n_init_cmd = ARRAY_SIZE(dsi_init_cmd),
+ .dsi_init_cmd = dsi_init_cmd,
+
+ .n_early_suspend_cmd = ARRAY_SIZE(dsi_early_suspend_cmd),
+ .dsi_early_suspend_cmd = dsi_early_suspend_cmd,
+
+ .n_late_resume_cmd = ARRAY_SIZE(dsi_late_resume_cmd),
+ .dsi_late_resume_cmd = dsi_late_resume_cmd,
+
+ .n_suspend_cmd = ARRAY_SIZE(dsi_suspend_cmd),
+ .dsi_suspend_cmd = dsi_suspend_cmd,
+
+ .video_data_type = TEGRA_DSI_VIDEO_TYPE_COMMAND_MODE,
+ .lp_cmd_mode_freq_khz = 20000,
+
+ /* TODO: Get the vender recommended freq */
+ .lp_read_cmd_mode_freq_khz = 200000,
+};
+
+static struct tegra_stereo_out enterprise_stereo = {
+ .set_mode = &enterprise_stereo_set_mode,
+ .set_orientation = &enterprise_stereo_set_orientation,
+};
+
+#ifdef CONFIG_TEGRA_DC
+static struct tegra_dc_mode enterprise_dsi_modes[] = {
+ {
+ .pclk = 10000000,
+ .h_ref_to_sync = 4,
+ .v_ref_to_sync = 1,
+ .h_sync_width = 16,
+ .v_sync_width = 1,
+ .h_back_porch = 32,
+ .v_back_porch = 1,
+ .h_active = 540,
+ .v_active = 960,
+ .h_front_porch = 32,
+ .v_front_porch = 2,
+ },
+};
+
+static struct tegra_fb_data enterprise_dsi_fb_data = {
+ .win = 0,
+ .xres = 540,
+ .yres = 960,
+ .bits_per_pixel = 32,
+ .flags = TEGRA_FB_FLIP_ON_PROBE,
+};
+
+static struct tegra_dc_out enterprise_disp1_out = {
+ .align = TEGRA_DC_ALIGN_MSB,
+ .order = TEGRA_DC_ORDER_RED_BLUE,
+ .sd_settings = &enterprise_sd_settings,
+
+ .flags = DC_CTRL_MODE,
+
+ .type = TEGRA_DC_OUT_DSI,
+
+ .modes = enterprise_dsi_modes,
+ .n_modes = ARRAY_SIZE(enterprise_dsi_modes),
+
+ .dsi = &enterprise_dsi,
+ .stereo = &enterprise_stereo,
+
+ .enable = enterprise_dsi_panel_enable,
+ .disable = enterprise_dsi_panel_disable,
+ .postsuspend = enterprise_dsi_panel_postsuspend,
+
+ .width = 53,
+ .height = 95,
+};
+static struct tegra_dc_platform_data enterprise_disp1_pdata = {
+ .flags = TEGRA_DC_FLAG_ENABLED,
+ .default_out = &enterprise_disp1_out,
+ .emc_clk_rate = 204000000,
+ .fb = &enterprise_dsi_fb_data,
+};
+
+static struct nvhost_device enterprise_disp1_device = {
+ .name = "tegradc",
+ .id = 0,
+ .resource = enterprise_disp1_resources,
+ .num_resources = ARRAY_SIZE(enterprise_disp1_resources),
+ .dev = {
+ .platform_data = &enterprise_disp1_pdata,
+ },
+};
+
+static int enterprise_disp1_check_fb(struct device *dev, struct fb_info *info)
+{
+ return info->device == &enterprise_disp1_device.dev;
+}
+
+static struct nvhost_device enterprise_disp2_device = {
+ .name = "tegradc",
+ .id = 1,
+ .resource = enterprise_disp2_resources,
+ .num_resources = ARRAY_SIZE(enterprise_disp2_resources),
+ .dev = {
+ .platform_data = &enterprise_disp2_pdata,
+ },
+};
+#endif
+
+static struct nvmap_platform_carveout enterprise_carveouts[] = {
+ [0] = NVMAP_HEAP_CARVEOUT_IRAM_INIT,
+ [1] = {
+ .name = "generic-0",
+ .usage_mask = NVMAP_HEAP_CARVEOUT_GENERIC,
+ .base = 0, /* Filled in by enterprise_panel_init() */
+ .size = 0, /* Filled in by enterprise_panel_init() */
+ .buddy_size = SZ_32K,
+ },
+};
+
+static struct nvmap_platform_data enterprise_nvmap_data = {
+ .carveouts = enterprise_carveouts,
+ .nr_carveouts = ARRAY_SIZE(enterprise_carveouts),
+};
+
+static struct platform_device enterprise_nvmap_device = {
+ .name = "tegra-nvmap",
+ .id = -1,
+ .dev = {
+ .platform_data = &enterprise_nvmap_data,
+ },
+};
+
+static struct platform_device *enterprise_gfx_devices[] __initdata = {
+ &enterprise_nvmap_device,
+#ifdef CONFIG_TEGRA_GRHOST
+ &tegra_grhost_device,
+#endif
+ &tegra_pwfm0_device,
+};
+
+static struct platform_device *enterprise_bl_devices[] = {
+ &enterprise_disp1_backlight_device,
+};
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+/* put early_suspend/late_resume handlers here for the display in order
+ * to keep the code out of the display driver, keeping it closer to upstream
+ */
+struct early_suspend enterprise_panel_early_suspender;
+
+static void enterprise_panel_early_suspend(struct early_suspend *h)
+{
+ unsigned i;
+
+ /* power down LCD, add use a black screen for HDMI */
+ if (num_registered_fb > 0)
+ fb_blank(registered_fb[0], FB_BLANK_POWERDOWN);
+ if (num_registered_fb > 1)
+ fb_blank(registered_fb[1], FB_BLANK_NORMAL);
+#ifdef CONFIG_TEGRA_CONVSERVATIVE_GOV_ON_EARLYSUPSEND
+ cpufreq_save_default_governor();
+ cpufreq_set_conservative_governor();
+ cpufreq_set_conservative_governor_param(
+ SET_CONSERVATIVE_GOVERNOR_UP_THRESHOLD,
+ SET_CONSERVATIVE_GOVERNOR_DOWN_THRESHOLD);
+#endif
+}
+
+static void enterprise_panel_late_resume(struct early_suspend *h)
+{
+ unsigned i;
+#ifdef CONFIG_TEGRA_CONVSERVATIVE_GOV_ON_EARLYSUPSEND
+ cpufreq_restore_default_governor();
+#endif
+ for (i = 0; i < num_registered_fb; i++)
+ fb_blank(registered_fb[i], FB_BLANK_UNBLANK);
+}
+#endif
+
+int __init enterprise_panel_init(void)
+{
+ int err;
+ struct resource __maybe_unused *res;
+
+ bl_output = enterprise_bl_output_measured;
+
+ if (WARN_ON(ARRAY_SIZE(enterprise_bl_output_measured) != 256))
+ pr_err("bl_output array does not have 256 elements\n");
+
+ enterprise_dsi.chip_id = tegra_get_chipid();
+ enterprise_dsi.chip_rev = tegra_get_revision();
+
+ enterprise_carveouts[1].base = tegra_carveout_start;
+ enterprise_carveouts[1].size = tegra_carveout_size;
+
+ tegra_gpio_enable(enterprise_hdmi_hpd);
+ gpio_request(enterprise_hdmi_hpd, "hdmi_hpd");
+ gpio_direction_input(enterprise_hdmi_hpd);
+
+ tegra_gpio_enable(enterprise_lcd_2d_3d);
+ gpio_request(enterprise_lcd_2d_3d, "lcd_2d_3d");
+ gpio_direction_output(enterprise_lcd_2d_3d, 0);
+ enterprise_stereo_set_mode(enterprise_stereo.mode_2d_3d);
+
+ tegra_gpio_enable(enterprise_lcd_swp_pl);
+ gpio_request(enterprise_lcd_swp_pl, "lcd_swp_pl");
+ gpio_direction_output(enterprise_lcd_swp_pl, 0);
+ enterprise_stereo_set_orientation(enterprise_stereo.orientation);
+
+#if !(DC_CTRL_MODE & TEGRA_DC_OUT_ONE_SHOT_MODE)
+ tegra_gpio_enable(enterprise_lcd_te);
+ gpio_request(enterprise_lcd_swp_pl, "lcd_te");
+ gpio_direction_input(enterprise_lcd_te);
+#endif
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ enterprise_panel_early_suspender.suspend = enterprise_panel_early_suspend;
+ enterprise_panel_early_suspender.resume = enterprise_panel_late_resume;
+ enterprise_panel_early_suspender.level = EARLY_SUSPEND_LEVEL_DISABLE_FB;
+ register_early_suspend(&enterprise_panel_early_suspender);
+#endif
+
+ err = platform_add_devices(enterprise_gfx_devices,
+ ARRAY_SIZE(enterprise_gfx_devices));
+
+#if defined(CONFIG_TEGRA_GRHOST) && defined(CONFIG_TEGRA_DC)
+ res = nvhost_get_resource_byname(&enterprise_disp1_device,
+ IORESOURCE_MEM, "fbmem");
+ res->start = tegra_fb_start;
+ res->end = tegra_fb_start + tegra_fb_size - 1;
+#endif
+
+ /* Copy the bootloader fb to the fb. */
+ tegra_move_framebuffer(tegra_fb_start, tegra_bootloader_fb_start,
+ min(tegra_fb_size, tegra_bootloader_fb_size));
+
+#if defined(CONFIG_TEGRA_GRHOST) && defined(CONFIG_TEGRA_DC)
+ if (!err)
+ err = nvhost_device_register(&enterprise_disp1_device);
+
+ res = nvhost_get_resource_byname(&enterprise_disp2_device,
+ IORESOURCE_MEM, "fbmem");
+ res->start = tegra_fb2_start;
+ res->end = tegra_fb2_start + tegra_fb2_size - 1;
+ if (!err)
+ err = nvhost_device_register(&enterprise_disp2_device);
+#endif
+
+#if defined(CONFIG_TEGRA_GRHOST) && defined(CONFIG_TEGRA_NVAVP)
+ if (!err)
+ err = nvhost_device_register(&nvavp_device);
+#endif
+
+ if (!err)
+ err = platform_add_devices(enterprise_bl_devices,
+ ARRAY_SIZE(enterprise_bl_devices));
+ return err;
+}
diff --git a/arch/arm/mach-tegra/board-enterprise-pinmux.c b/arch/arm/mach-tegra/board-enterprise-pinmux.c
new file mode 100644
index 000000000000..6b10ac43c427
--- /dev/null
+++ b/arch/arm/mach-tegra/board-enterprise-pinmux.c
@@ -0,0 +1,526 @@
+/*
+ * arch/arm/mach-tegra/board-enterprise-pinmux.c
+ *
+ * Copyright (C) 2011 NVIDIA Corporation
+ *
+ * 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/init.h>
+#include <mach/pinmux.h>
+#include "board.h"
+#include "board-enterprise.h"
+#include "gpio-names.h"
+
+#define DEFAULT_DRIVE(_name) \
+ { \
+ .pingroup = TEGRA_DRIVE_PINGROUP_##_name, \
+ .hsm = TEGRA_HSM_DISABLE, \
+ .schmitt = TEGRA_SCHMITT_ENABLE, \
+ .drive = TEGRA_DRIVE_DIV_1, \
+ .pull_down = TEGRA_PULL_31, \
+ .pull_up = TEGRA_PULL_31, \
+ .slew_rising = TEGRA_SLEW_SLOWEST, \
+ .slew_falling = TEGRA_SLEW_SLOWEST, \
+ }
+/* Setting the drive strength of pins
+ * hsm: Enable High speed mode (ENABLE/DISABLE)
+ * Schimit: Enable/disable schimit (ENABLE/DISABLE)
+ * drive: low power mode (DIV_1, DIV_2, DIV_4, DIV_8)
+ * pulldn_drive - drive down (falling edge) - Driver Output Pull-Down drive
+ * strength code. Value from 0 to 31.
+ * pullup_drive - drive up (rising edge) - Driver Output Pull-Up drive
+ * strength code. Value from 0 to 31.
+ * pulldn_slew - Driver Output Pull-Up slew control code - 2bit code
+ * code 11 is least slewing of signal. code 00 is highest
+ * slewing of the signal.
+ * Value - FASTEST, FAST, SLOW, SLOWEST
+ * pullup_slew - Driver Output Pull-Down slew control code -
+ * code 11 is least slewing of signal. code 00 is highest
+ * slewing of the signal.
+ * Value - FASTEST, FAST, SLOW, SLOWEST
+ */
+#define SET_DRIVE(_name, _hsm, _schmitt, _drive, _pulldn_drive, _pullup_drive, _pulldn_slew, _pullup_slew) \
+ { \
+ .pingroup = TEGRA_DRIVE_PINGROUP_##_name, \
+ .hsm = TEGRA_HSM_##_hsm, \
+ .schmitt = TEGRA_SCHMITT_##_schmitt, \
+ .drive = TEGRA_DRIVE_##_drive, \
+ .pull_down = TEGRA_PULL_##_pulldn_drive, \
+ .pull_up = TEGRA_PULL_##_pullup_drive, \
+ .slew_rising = TEGRA_SLEW_##_pulldn_slew, \
+ .slew_falling = TEGRA_SLEW_##_pullup_slew, \
+ }
+
+/* !!!FIXME!!!! POPULATE THIS TABLE */
+static __initdata struct tegra_drive_pingroup_config enterprise_drive_pinmux[] = {
+ /* DEFAULT_DRIVE(<pin_group>), */
+ /* SET_DRIVE(ATA, DISABLE, DISABLE, DIV_1, 31, 31, FAST, FAST) */
+
+ /* All I2C pins are driven to maximum drive strength */
+ /* GEN1 I2C */
+ SET_DRIVE(DBG, DISABLE, ENABLE, DIV_1, 31, 31, FASTEST, FASTEST),
+
+ /* GEN2 I2C */
+ SET_DRIVE(AT5, DISABLE, ENABLE, DIV_1, 31, 31, FASTEST, FASTEST),
+
+ /* CAM I2C */
+ SET_DRIVE(GME, DISABLE, ENABLE, DIV_1, 31, 31, FASTEST, FASTEST),
+
+ /* DDC I2C */
+ SET_DRIVE(DDC, DISABLE, ENABLE, DIV_1, 31, 31, FASTEST, FASTEST),
+
+ /* PWR_I2C */
+ SET_DRIVE(AO1, DISABLE, ENABLE, DIV_1, 31, 31, FASTEST, FASTEST),
+
+ /* UART3 */
+ SET_DRIVE(UART3, DISABLE, ENABLE, DIV_1, 31, 31, FASTEST, FASTEST),
+};
+
+#define DEFAULT_PINMUX(_pingroup, _mux, _pupd, _tri, _io) \
+ { \
+ .pingroup = TEGRA_PINGROUP_##_pingroup, \
+ .func = TEGRA_MUX_##_mux, \
+ .pupd = TEGRA_PUPD_##_pupd, \
+ .tristate = TEGRA_TRI_##_tri, \
+ .io = TEGRA_PIN_##_io, \
+ .lock = TEGRA_PIN_LOCK_DEFAULT, \
+ .od = TEGRA_PIN_OD_DEFAULT, \
+ .ioreset = TEGRA_PIN_IO_RESET_DEFAULT, \
+ }
+
+#define I2C_PINMUX(_pingroup, _mux, _pupd, _tri, _io, _lock, _od) \
+ { \
+ .pingroup = TEGRA_PINGROUP_##_pingroup, \
+ .func = TEGRA_MUX_##_mux, \
+ .pupd = TEGRA_PUPD_##_pupd, \
+ .tristate = TEGRA_TRI_##_tri, \
+ .io = TEGRA_PIN_##_io, \
+ .lock = TEGRA_PIN_LOCK_##_lock, \
+ .od = TEGRA_PIN_OD_##_od, \
+ .ioreset = TEGRA_PIN_IO_RESET_DEFAULT, \
+ }
+
+#define VI_PINMUX(_pingroup, _mux, _pupd, _tri, _io, _lock, _ioreset) \
+ { \
+ .pingroup = TEGRA_PINGROUP_##_pingroup, \
+ .func = TEGRA_MUX_##_mux, \
+ .pupd = TEGRA_PUPD_##_pupd, \
+ .tristate = TEGRA_TRI_##_tri, \
+ .io = TEGRA_PIN_##_io, \
+ .lock = TEGRA_PIN_LOCK_##_lock, \
+ .od = TEGRA_PIN_OD_DEFAULT, \
+ .ioreset = TEGRA_PIN_IO_RESET_##_ioreset \
+ }
+
+static __initdata struct tegra_pingroup_config enterprise_pinmux[] = {
+ /* SDMMC1 pinmux */
+ DEFAULT_PINMUX(SDMMC1_CLK, SDMMC1, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC1_CMD, SDMMC1, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC1_DAT3, SDMMC1, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC1_DAT2, SDMMC1, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC1_DAT1, SDMMC1, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC1_DAT0, SDMMC1, PULL_UP, NORMAL, INPUT),
+
+ /* SDMMC3 pinmux */
+ DEFAULT_PINMUX(SDMMC3_CLK, SDMMC3, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC3_CMD, SDMMC3, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC3_DAT0, SDMMC3, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC3_DAT1, SDMMC3, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC3_DAT2, SDMMC3, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC3_DAT3, SDMMC3, PULL_UP, NORMAL, INPUT),
+
+ /* SDMMC4 pinmux */
+ DEFAULT_PINMUX(SDMMC4_CLK, SDMMC4, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC4_CMD, SDMMC4, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC4_DAT0, SDMMC4, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC4_DAT1, SDMMC4, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC4_DAT2, SDMMC4, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC4_DAT3, SDMMC4, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC4_DAT4, SDMMC4, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC4_DAT5, SDMMC4, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC4_DAT6, SDMMC4, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC4_DAT7, SDMMC4, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC4_RST_N, RSVD1, PULL_DOWN, NORMAL, INPUT),
+
+ /* I2C1 pinmux */
+ I2C_PINMUX(GEN1_I2C_SCL, I2C1, NORMAL, NORMAL, INPUT, DISABLE, ENABLE),
+ I2C_PINMUX(GEN1_I2C_SDA, I2C1, NORMAL, NORMAL, INPUT, DISABLE, ENABLE),
+
+ /* I2C2 pinmux */
+ I2C_PINMUX(GEN2_I2C_SCL, I2C2, NORMAL, NORMAL, INPUT, DISABLE, ENABLE),
+ I2C_PINMUX(GEN2_I2C_SDA, I2C2, NORMAL, NORMAL, INPUT, DISABLE, ENABLE),
+
+ /* I2C3 pinmux */
+ I2C_PINMUX(CAM_I2C_SCL, I2C3, NORMAL, NORMAL, INPUT, DISABLE, ENABLE),
+ I2C_PINMUX(CAM_I2C_SDA, I2C3, NORMAL, NORMAL, INPUT, DISABLE, ENABLE),
+
+ /* I2C4 pinmux */
+ I2C_PINMUX(DDC_SCL, I2C4, PULL_UP,NORMAL, INPUT, DISABLE, DISABLE),
+ I2C_PINMUX(DDC_SDA, I2C4, PULL_UP,NORMAL, INPUT, DISABLE, DISABLE),
+
+ /* Power I2C pinmux */
+ I2C_PINMUX(PWR_I2C_SCL, I2CPWR, NORMAL, NORMAL, INPUT, DISABLE, ENABLE),
+ I2C_PINMUX(PWR_I2C_SDA, I2CPWR, NORMAL, NORMAL, INPUT, DISABLE, ENABLE),
+
+ DEFAULT_PINMUX(ULPI_DATA0, ULPI, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(ULPI_DATA1, ULPI, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(ULPI_DATA2, ULPI, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(ULPI_DATA3, ULPI, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(ULPI_DATA4, ULPI, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(ULPI_DATA5, ULPI, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(ULPI_DATA6, ULPI, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(ULPI_DATA7, ULPI, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(ULPI_CLK, ULPI, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(ULPI_DIR, ULPI, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(ULPI_NXT, ULPI, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(ULPI_STP, ULPI, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(DAP3_FS, I2S2, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(DAP3_DIN, I2S2, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(DAP3_DOUT, I2S2, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(DAP3_SCLK, I2S2, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GPIO_PV2, RSVD1, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(GPIO_PV3, RSVD1, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(LCD_PWR1, DISPLAYA, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(LCD_PWR2, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_CS0_N, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_DC0, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_DE, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D0, DISPLAYA, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(LCD_D1, DISPLAYA, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(LCD_D2, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D3, DISPLAYA, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(LCD_D4, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D5, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D6, RSVD1, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D7, RSVD1, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(LCD_D8, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D9, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D11, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D12, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D13, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D14, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D15, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D16, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D17, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D18, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D19, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D20, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D21, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D22, RSVD1, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D23, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_CS1_N, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_M1, DISPLAYA, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(LCD_DC1, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(VI_D0, RSVD1, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(VI_D1, SDMMC2, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(VI_D2, SDMMC2, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(VI_D3, SDMMC2, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(VI_D4, VI, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(VI_D5, SDMMC2, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(VI_D7, SDMMC2, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(VI_D10, RSVD1, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(VI_MCLK, VI, PULL_UP, NORMAL, INPUT),
+
+ DEFAULT_PINMUX(UART2_RXD, IRDA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(UART2_TXD, IRDA, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(UART2_RTS_N, UARTB, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(UART2_CTS_N, UARTB, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(UART3_TXD, UARTC, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(UART3_RXD, UARTC, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(UART3_CTS_N, UARTC, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(UART3_RTS_N, UARTC, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(GPIO_PU0, UARTA, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(GPIO_PU1, UARTA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GPIO_PU2, UARTA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GPIO_PU3, UARTA, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(GPIO_PU5, PWM2, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GPIO_PU6, PWM3, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(DAP4_FS, I2S3, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(DAP4_DIN, I2S3, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(DAP4_DOUT, I2S3, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(DAP4_SCLK, I2S3, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GMI_AD8, PWM0, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(GMI_AD9, NAND, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(GMI_AD10, NAND, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(GMI_A16, UARTD, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(GMI_A17, UARTD, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GMI_A18, UARTD, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GMI_A19, UARTD, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(CAM_MCLK, VI_ALT2, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GPIO_PCC1, RSVD1, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GPIO_PBB0, RSVD1, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GPIO_PBB3, VGP3, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GPIO_PBB7, I2S4, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GPIO_PCC2, I2S4, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(JTAG_RTCK, RTCK, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(KB_ROW0, KBC, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(KB_ROW1, KBC, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(KB_ROW2, KBC, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(KB_ROW3, KBC, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(KB_ROW10, KBC, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(KB_ROW12, KBC, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(KB_COL0, KBC, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(KB_COL1, KBC, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(KB_COL2, KBC, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(KB_COL3, KBC, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(KB_COL4, KBC, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(KB_COL5, KBC, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(GPIO_PV0, RSVD, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(CLK_32K_OUT, BLINK, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(SYS_CLK_REQ, SYSCLK, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(OWR, OWR, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(DAP1_FS, I2S0, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(DAP1_DIN, I2S0, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(DAP1_DOUT, I2S0, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(DAP1_SCLK, I2S0, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(CLK1_REQ, DAP, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(CLK1_OUT, EXTPERIPH1, NORMAL, NORMAL, INPUT),
+#if 0 /* For HDA realtek Codec */
+ DEFAULT_PINMUX(SPDIF_IN, DAP2, PULL_DOWN, NORMAL, INPUT),
+#else
+ DEFAULT_PINMUX(SPDIF_IN, SPDIF, NORMAL, NORMAL, INPUT),
+#endif
+#if 0 /* For HDA realtek Codec */
+ DEFAULT_PINMUX(DAP2_FS, HDA, PULL_DOWN, NORMAL, INPUT),
+ DEFAULT_PINMUX(DAP2_DIN, HDA, PULL_DOWN, NORMAL, INPUT),
+ DEFAULT_PINMUX(DAP2_DOUT, HDA, PULL_DOWN, NORMAL, INPUT),
+ DEFAULT_PINMUX(DAP2_SCLK, HDA, PULL_DOWN, NORMAL, INPUT),
+#else
+ DEFAULT_PINMUX(DAP2_FS, I2S1, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(DAP2_DIN, I2S1, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(DAP2_DOUT, I2S1, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(DAP2_SCLK, I2S1, NORMAL, NORMAL, INPUT),
+#endif
+ DEFAULT_PINMUX(SPI2_CS1_N, SPI2, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(SPI1_MOSI, SPI1, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(SPI1_SCK, SPI1, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(SPI1_MISO, SPI1, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(PEX_L0_PRSNT_N, PCIE, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(PEX_L0_RST_N, PCIE, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(PEX_L0_CLKREQ_N, PCIE, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(PEX_WAKE_N, PCIE, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(PEX_L1_PRSNT_N, PCIE, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(PEX_L1_RST_N, PCIE, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(PEX_L1_CLKREQ_N, PCIE, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(PEX_L2_PRSNT_N, PCIE, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(PEX_L2_RST_N, PCIE, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(PEX_L2_CLKREQ_N, PCIE, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(HDMI_CEC, CEC, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(HDMI_INT, RSVD0, NORMAL, TRISTATE, INPUT),
+
+ /* Gpios */
+ /* SDMMC1 CD gpio */
+ DEFAULT_PINMUX(GMI_IORDY, RSVD1, PULL_UP, NORMAL, INPUT),
+ /* SDMMC1 WP gpio */
+ DEFAULT_PINMUX(VI_D11, RSVD1, PULL_UP, NORMAL, INPUT),
+
+ /* Touch panel GPIO */
+ /* Touch IRQ */
+ DEFAULT_PINMUX(GMI_AD12, NAND, NORMAL, NORMAL, INPUT),
+
+ /* Touch RESET */
+ DEFAULT_PINMUX(GMI_AD14, NAND, NORMAL, NORMAL, INPUT),
+
+ DEFAULT_PINMUX(GMI_AD15, NAND, PULL_UP, TRISTATE, INPUT),
+
+ /* Power rails GPIO */
+ DEFAULT_PINMUX(KB_ROW8, KBC, PULL_UP, NORMAL, INPUT),
+
+ VI_PINMUX(VI_D6, VI, NORMAL, NORMAL, OUTPUT, DISABLE, DISABLE),
+ VI_PINMUX(VI_D8, SDMMC2, NORMAL, NORMAL, INPUT, DISABLE, DISABLE),
+ VI_PINMUX(VI_D9, SDMMC2, NORMAL, NORMAL, INPUT, DISABLE, DISABLE),
+ VI_PINMUX(VI_PCLK, RSVD1, PULL_UP, TRISTATE, INPUT, DISABLE, ENABLE),
+ VI_PINMUX(VI_HSYNC, RSVD1, NORMAL, NORMAL, INPUT, DISABLE, DISABLE),
+ VI_PINMUX(VI_VSYNC, RSVD1, NORMAL, NORMAL, INPUT, DISABLE, DISABLE),
+};
+
+static __initdata struct tegra_pingroup_config enterprise_unused_pinmux[] = {
+ DEFAULT_PINMUX(CLK2_OUT, EXTPERIPH2, PULL_DOWN, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(CLK2_REQ, DAP, PULL_DOWN, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(CLK3_OUT, EXTPERIPH3, PULL_DOWN, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(CLK3_REQ, DEV3, PULL_DOWN, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(GPIO_PBB4, VGP4, PULL_DOWN, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(GPIO_PBB5, VGP5, PULL_DOWN, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(GPIO_PBB6, VGP6, PULL_DOWN, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(GPIO_PU4, PWM1, PULL_DOWN, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(GMI_AD0, GMI, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(GMI_AD1, GMI, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(GMI_AD2, GMI, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(GMI_AD3, GMI, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(GMI_AD4, GMI, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(GMI_AD5, GMI, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(GMI_AD6, GMI, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(GMI_AD7, GMI, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(GMI_AD11, GMI, PULL_DOWN, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(GMI_CS0_N, GMI, PULL_DOWN, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(GMI_CS2_N, GMI, PULL_DOWN, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(GMI_CS3_N, GMI, PULL_DOWN, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(GMI_CS6_N, GMI, PULL_DOWN, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(GMI_CS7_N, GMI, PULL_DOWN, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(GMI_DQS, GMI, PULL_DOWN, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(GMI_RST_N, GMI, PULL_DOWN, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(GMI_WAIT, GMI, PULL_DOWN, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(GMI_WP_N, GMI, PULL_DOWN, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(KB_ROW6, KBC, PULL_DOWN, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(KB_ROW7, KBC, PULL_DOWN, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(KB_ROW9, KBC, PULL_DOWN, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(KB_ROW11, KBC, PULL_DOWN, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(KB_ROW13, KBC, PULL_DOWN, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(KB_ROW14, KBC, PULL_DOWN, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(KB_ROW15, KBC, PULL_DOWN, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(LCD_PCLK, DISPLAYA, PULL_DOWN, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(LCD_WR_N, DISPLAYA, PULL_DOWN, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(LCD_HSYNC, DISPLAYA, PULL_DOWN, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(LCD_VSYNC, DISPLAYA, PULL_DOWN, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(LCD_D10, DISPLAYA, PULL_DOWN, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(LCD_PWR0, DISPLAYA, PULL_DOWN, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(LCD_SCK, DISPLAYA, PULL_DOWN, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(LCD_SDOUT, DISPLAYA, PULL_DOWN, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(LCD_SDIN, DISPLAYA, PULL_DOWN, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(CRT_HSYNC, CRT, PULL_DOWN, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(CRT_VSYNC, CRT, PULL_DOWN, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(SDMMC3_DAT4, SDMMC3, PULL_DOWN, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(SDMMC3_DAT5, SDMMC3, PULL_DOWN, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(SDMMC3_DAT6, SDMMC3, PULL_DOWN, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(SDMMC3_DAT7, SDMMC3, PULL_DOWN, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(SPDIF_OUT, DAP2, PULL_DOWN, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(SPI1_CS0_N, SPI1, PULL_DOWN, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(SPI2_SCK, SPI2, PULL_DOWN, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(SPI2_CS0_N, SPI2, PULL_DOWN, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(SPI2_MOSI, SPI2, PULL_DOWN, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(SPI2_MISO, SPI2, PULL_DOWN, TRISTATE, OUTPUT),
+};
+
+static struct tegra_gpio_table gpio_table[] = {
+ { .gpio = TEGRA_GPIO_HP_DET, .enable = true },
+};
+
+struct pin_info_low_power_mode {
+ char name[16];
+ int gpio_nr;
+ bool is_gpio;
+ bool is_input;
+ int value; /* Value if it is output*/
+};
+
+#define PIN_GPIO_LPM(_name, _gpio, _is_input, _value) \
+ { \
+ .name = _name, \
+ .gpio_nr = _gpio, \
+ .is_gpio = true, \
+ .is_input = _is_input, \
+ .value = _value, \
+ }
+static __initdata struct pin_info_low_power_mode enterprise_unused_gpio_pins[] = {
+ PIN_GPIO_LPM("CLK2_OUT", TEGRA_GPIO_PW5, 0, 0),
+ PIN_GPIO_LPM("CLK2_REQ", TEGRA_GPIO_PCC5, 0, 0),
+ PIN_GPIO_LPM("CLK3_OUT", TEGRA_GPIO_PEE0, 0, 0),
+ PIN_GPIO_LPM("CLK3_REQ", TEGRA_GPIO_PEE1, 0, 0),
+ PIN_GPIO_LPM("GPIO_PBB4", TEGRA_GPIO_PBB4, 0, 0),
+ PIN_GPIO_LPM("GPIO_PBB5", TEGRA_GPIO_PBB5, 0, 0),
+ PIN_GPIO_LPM("GPIO_PBB6", TEGRA_GPIO_PBB6, 0, 0),
+ PIN_GPIO_LPM("GPIO_PU4", TEGRA_GPIO_PU4, 0, 0),
+ PIN_GPIO_LPM("GMI_AD0", TEGRA_GPIO_PG0, 0, 0),
+ PIN_GPIO_LPM("GMI_AD1", TEGRA_GPIO_PG1, 0, 0),
+ PIN_GPIO_LPM("GMI_AD2", TEGRA_GPIO_PG2, 0, 0),
+ PIN_GPIO_LPM("GMI_AD3", TEGRA_GPIO_PG3, 0, 0),
+ PIN_GPIO_LPM("GMI_AD4", TEGRA_GPIO_PG4, 0, 0),
+ PIN_GPIO_LPM("GMI_AD5", TEGRA_GPIO_PG5, 0, 0),
+ PIN_GPIO_LPM("GMI_AD6", TEGRA_GPIO_PG6, 0, 0),
+ PIN_GPIO_LPM("GMI_AD7", TEGRA_GPIO_PG7, 0, 0),
+ PIN_GPIO_LPM("GMI_AD11", TEGRA_GPIO_PH3, 0, 0),
+ PIN_GPIO_LPM("GMI_CS0_N", TEGRA_GPIO_PJ0, 0, 0),
+ PIN_GPIO_LPM("GMI_CS2_N", TEGRA_GPIO_PK3, 0, 0),
+ PIN_GPIO_LPM("GMI_CS3_N", TEGRA_GPIO_PK4, 0, 0),
+ PIN_GPIO_LPM("GMI_CS6_N", TEGRA_GPIO_PI3, 0, 0),
+ PIN_GPIO_LPM("GMI_CS7_N", TEGRA_GPIO_PI6, 0, 0),
+ PIN_GPIO_LPM("GMI_DQS", TEGRA_GPIO_PI2, 0, 0),
+ PIN_GPIO_LPM("GMI_RST_N", TEGRA_GPIO_PI4, 0, 0),
+ PIN_GPIO_LPM("GMI_WAIT", TEGRA_GPIO_PI7, 0, 0),
+ PIN_GPIO_LPM("GMI_WP_N", TEGRA_GPIO_PC7, 0, 0),
+ PIN_GPIO_LPM("KB_ROW6", TEGRA_GPIO_PR6, 0, 0),
+ PIN_GPIO_LPM("KB_ROW7", TEGRA_GPIO_PR7, 0, 0),
+ PIN_GPIO_LPM("KB_ROW9", TEGRA_GPIO_PS1, 0, 0),
+ PIN_GPIO_LPM("KB_ROW11", TEGRA_GPIO_PS3, 0, 0),
+ PIN_GPIO_LPM("KB_ROW13", TEGRA_GPIO_PS5, 0, 0),
+ PIN_GPIO_LPM("KB_ROW14", TEGRA_GPIO_PS6, 0, 0),
+ PIN_GPIO_LPM("KB_ROW15", TEGRA_GPIO_PS7, 0, 0),
+ PIN_GPIO_LPM("LCD_PCLK", TEGRA_GPIO_PB3, 0, 0),
+ PIN_GPIO_LPM("LCD_WR_N", TEGRA_GPIO_PZ3, 0, 0),
+ PIN_GPIO_LPM("LCD_HSYNC", TEGRA_GPIO_PJ3, 0, 0),
+ PIN_GPIO_LPM("LCD_VSYNC", TEGRA_GPIO_PJ4, 0, 0),
+ PIN_GPIO_LPM("LCD_D10", TEGRA_GPIO_PF2, 0, 0),
+ PIN_GPIO_LPM("LCD_PWR0", TEGRA_GPIO_PB2, 0, 0),
+ PIN_GPIO_LPM("LCD_SCK", TEGRA_GPIO_PZ4, 0, 0),
+ PIN_GPIO_LPM("LCD_SDOUT", TEGRA_GPIO_PN5, 0, 0),
+ PIN_GPIO_LPM("LCD_SDIN", TEGRA_GPIO_PZ2, 0, 0),
+ PIN_GPIO_LPM("CRT_HSYNC", TEGRA_GPIO_PV6, 0, 0),
+ PIN_GPIO_LPM("CRT_VSYNC", TEGRA_GPIO_PV7, 0, 0),
+ PIN_GPIO_LPM("SDMMC3_DAT4", TEGRA_GPIO_PD1, 0, 0),
+ PIN_GPIO_LPM("SDMMC3_DAT5", TEGRA_GPIO_PD0, 0, 0),
+ PIN_GPIO_LPM("SDMMC3_DAT6", TEGRA_GPIO_PD3, 0, 0),
+ PIN_GPIO_LPM("SDMMC3_DAT7", TEGRA_GPIO_PD4, 0, 0),
+ PIN_GPIO_LPM("SPDIF_OUT", TEGRA_GPIO_PK5, 0, 0),
+ PIN_GPIO_LPM("SPI1_CS0_N", TEGRA_GPIO_PX6, 0, 0),
+ PIN_GPIO_LPM("SPI2_SCK", TEGRA_GPIO_PX2, 0, 0),
+ PIN_GPIO_LPM("SPI2_CS0_N", TEGRA_GPIO_PX3, 0, 0),
+ PIN_GPIO_LPM("SPI2_MOSI", TEGRA_GPIO_PX0, 0, 0),
+ PIN_GPIO_LPM("SPI2_MISO", TEGRA_GPIO_PX1, 0, 0),
+};
+
+static void enterprise_set_unused_pin_gpio(struct pin_info_low_power_mode *lpm_pin_info,
+ int list_count)
+{
+ int i;
+ struct pin_info_low_power_mode *pin_info;
+ int ret;
+
+ for (i = 0; i < list_count; ++i) {
+ pin_info = (struct pin_info_low_power_mode *)(lpm_pin_info + i);
+ if (!pin_info->is_gpio)
+ continue;
+
+ ret = gpio_request(pin_info->gpio_nr, pin_info->name);
+ if (ret < 0) {
+ pr_err("%s() Error in gpio_request() for gpio %d\n",
+ __func__, pin_info->gpio_nr);
+ continue;
+ }
+ if (pin_info->is_input)
+ ret = gpio_direction_input(pin_info->gpio_nr);
+ else
+ ret = gpio_direction_output(pin_info->gpio_nr,
+ pin_info->value);
+ if (ret < 0) {
+ pr_err("%s() Error in setting gpio %d to in/out\n",
+ __func__, pin_info->gpio_nr);
+ gpio_free(pin_info->gpio_nr);
+ continue;
+ }
+ tegra_gpio_enable(pin_info->gpio_nr);
+ }
+}
+
+int __init enterprise_pinmux_init(void)
+{
+ tegra_pinmux_config_table(enterprise_pinmux, ARRAY_SIZE(enterprise_pinmux));
+ tegra_drive_pinmux_config_table(enterprise_drive_pinmux,
+ ARRAY_SIZE(enterprise_drive_pinmux));
+ tegra_pinmux_config_table(enterprise_unused_pinmux,
+ ARRAY_SIZE(enterprise_unused_pinmux));
+
+ tegra_gpio_config(gpio_table, ARRAY_SIZE(gpio_table));
+ enterprise_set_unused_pin_gpio(enterprise_unused_gpio_pins,
+ ARRAY_SIZE(enterprise_unused_gpio_pins));
+ return 0;
+}
diff --git a/arch/arm/mach-tegra/board-enterprise-power.c b/arch/arm/mach-tegra/board-enterprise-power.c
new file mode 100644
index 000000000000..8a7977309375
--- /dev/null
+++ b/arch/arm/mach-tegra/board-enterprise-power.c
@@ -0,0 +1,575 @@
+/*
+ * arch/arm/mach-tegra/board-enterprise-power.c
+ *
+ * Copyright (C) 2011 NVIDIA, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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/i2c.h>
+#include <linux/pda_power.h>
+#include <linux/platform_device.h>
+#include <linux/resource.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/gpio-switch-regulator.h>
+#include <linux/mfd/tps80031.h>
+#include <linux/regulator/tps80031-regulator.h>
+#include <linux/tps80031-charger.h>
+#include <linux/gpio.h>
+#include <linux/io.h>
+
+#include <mach/edp.h>
+#include <mach/iomap.h>
+#include <mach/irqs.h>
+#include <mach/pinmux.h>
+#include <mach/tsensor.h>
+
+#include "gpio-names.h"
+#include "board.h"
+#include "board-enterprise.h"
+#include "pm.h"
+#include "wakeups-t3.h"
+
+#define PMC_CTRL 0x0
+#define PMC_CTRL_INTR_LOW (1 << 17)
+
+/************************ TPS80031 based regulator ****************/
+static struct regulator_consumer_supply tps80031_vio_supply[] = {
+ REGULATOR_SUPPLY("vio_1v8", NULL),
+ REGULATOR_SUPPLY("avdd_osc", NULL),
+ REGULATOR_SUPPLY("vddio_sys", NULL),
+ REGULATOR_SUPPLY("vddio_uart", NULL),
+ REGULATOR_SUPPLY("pwrdet_uart", NULL),
+ REGULATOR_SUPPLY("vddio_lcd", NULL),
+ REGULATOR_SUPPLY("pwrdet_lcd", NULL),
+ REGULATOR_SUPPLY("vddio_audio", NULL),
+ REGULATOR_SUPPLY("pwrdet_audio", NULL),
+ REGULATOR_SUPPLY("vddio_bb", NULL),
+ REGULATOR_SUPPLY("pwrdet_bb", NULL),
+ REGULATOR_SUPPLY("vddio_gmi", NULL),
+ REGULATOR_SUPPLY("avdd_usb_pll", NULL),
+ REGULATOR_SUPPLY("vddio_cam", NULL),
+ REGULATOR_SUPPLY("pwrdet_cam", NULL),
+ REGULATOR_SUPPLY("vddio_sdmmc1", NULL),
+ REGULATOR_SUPPLY("pwrdet_sdmmc1", NULL),
+ REGULATOR_SUPPLY("vddio_sdmmc4", NULL),
+ REGULATOR_SUPPLY("pwrdet_sdmmc4", NULL),
+ REGULATOR_SUPPLY("avdd_hdmi_pll", NULL),
+ REGULATOR_SUPPLY("vddio_gps", NULL),
+ REGULATOR_SUPPLY("vdd_lcd_buffered", NULL),
+ REGULATOR_SUPPLY("vddio_nand", NULL),
+ REGULATOR_SUPPLY("pwrdet_nand", NULL),
+ REGULATOR_SUPPLY("vddio_sd", NULL),
+ REGULATOR_SUPPLY("vdd_bat", NULL),
+ REGULATOR_SUPPLY("vdd_io", NULL),
+ REGULATOR_SUPPLY("pwrdet_pex_ctl", NULL),
+};
+
+static struct regulator_consumer_supply tps80031_smps1_supply[] = {
+ REGULATOR_SUPPLY("vdd_cpu", NULL),
+};
+
+static struct regulator_consumer_supply tps80031_smps2_supply[] = {
+ REGULATOR_SUPPLY("vdd_core", NULL),
+};
+
+static struct regulator_consumer_supply tps80031_smps3_supply[] = {
+ REGULATOR_SUPPLY("en_vddio_ddr_1v2", NULL),
+ REGULATOR_SUPPLY("vddio_ddr", NULL),
+ REGULATOR_SUPPLY("vdd_lpddr", NULL),
+ REGULATOR_SUPPLY("ddr_comp_pu", NULL),
+};
+
+static struct regulator_consumer_supply tps80031_smps4_supply[] = {
+ REGULATOR_SUPPLY("vddio_sdmmc_2v85", NULL),
+ REGULATOR_SUPPLY("pwrdet_sdmmc3", NULL),
+};
+
+static struct regulator_consumer_supply tps80031_vana_supply[] = {
+ REGULATOR_SUPPLY("unused_vana", NULL),
+};
+
+static struct regulator_consumer_supply tps80031_ldo1_supply[] = {
+ REGULATOR_SUPPLY("avdd_dsi_csi", NULL),
+ REGULATOR_SUPPLY("pwrdet_mipi", NULL),
+};
+
+static struct regulator_consumer_supply tps80031_ldo2_supply[] = {
+ REGULATOR_SUPPLY("vdd_rtc", NULL),
+};
+
+static struct regulator_consumer_supply tps80031_ldo3_supply[] = {
+ REGULATOR_SUPPLY("vdd_vbrtr", NULL),
+};
+
+static struct regulator_consumer_supply tps80031_ldo4_supply[] = {
+ REGULATOR_SUPPLY("avdd_lcd", NULL),
+};
+
+static struct regulator_consumer_supply tps80031_ldo5_supply[] = {
+ REGULATOR_SUPPLY("vdd_sensor", NULL),
+ REGULATOR_SUPPLY("vdd_compass", NULL),
+ REGULATOR_SUPPLY("vdd_als", NULL),
+ REGULATOR_SUPPLY("vdd_gyro", NULL),
+ REGULATOR_SUPPLY("vdd_touch", NULL),
+ REGULATOR_SUPPLY("vdd_proxim_diode", NULL),
+};
+
+static struct regulator_consumer_supply tps80031_ldo6_supply[] = {
+ REGULATOR_SUPPLY("vdd_ddr_rx", NULL),
+ REGULATOR_SUPPLY("vddf_core_emmc", NULL),
+};
+
+static struct regulator_consumer_supply tps80031_ldo7_supply[] = {
+ REGULATOR_SUPPLY("vdd_plla_p_c_s", NULL),
+ REGULATOR_SUPPLY("vdd_pllm", NULL),
+ REGULATOR_SUPPLY("vdd_pllu_d", NULL),
+ REGULATOR_SUPPLY("vdd_pllx", NULL),
+};
+
+static struct regulator_consumer_supply tps80031_ldoln_supply[] = {
+ REGULATOR_SUPPLY("vdd_ddr_hs", NULL),
+};
+
+static struct regulator_consumer_supply tps80031_ldousb_supply[] = {
+ REGULATOR_SUPPLY("unused_ldousb", NULL),
+};
+
+static struct regulator_consumer_supply tps80031_vbus_supply[] = {
+ REGULATOR_SUPPLY("usb_vbus", NULL),
+};
+
+static struct regulator_consumer_supply tps80031_battery_charge_supply[] = {
+ REGULATOR_SUPPLY("usb_bat_chg", NULL),
+};
+
+#define TPS_PDATA_INIT(_id, _minmv, _maxmv, _supply_reg, _always_on, \
+ _boot_on, _apply_uv, _init_uV, _init_enable, _init_apply, \
+ _flags, _ectrl, _delay) \
+ static struct tps80031_regulator_platform_data pdata_##_id = { \
+ .regulator = { \
+ .constraints = { \
+ .min_uV = (_minmv)*1000, \
+ .max_uV = (_maxmv)*1000, \
+ .valid_modes_mask = (REGULATOR_MODE_NORMAL | \
+ REGULATOR_MODE_STANDBY), \
+ .valid_ops_mask = (REGULATOR_CHANGE_MODE | \
+ REGULATOR_CHANGE_STATUS | \
+ REGULATOR_CHANGE_VOLTAGE), \
+ .always_on = _always_on, \
+ .boot_on = _boot_on, \
+ .apply_uV = _apply_uv, \
+ }, \
+ .num_consumer_supplies = \
+ ARRAY_SIZE(tps80031_##_id##_supply), \
+ .consumer_supplies = tps80031_##_id##_supply, \
+ .supply_regulator = _supply_reg, \
+ }, \
+ .init_uV = _init_uV * 1000, \
+ .init_enable = _init_enable, \
+ .init_apply = _init_apply, \
+ .flags = _flags, \
+ .ext_ctrl_flag = _ectrl, \
+ .delay_us = _delay, \
+ }
+
+TPS_PDATA_INIT(vio, 600, 2100, 0, 1, 0, 0, -1, 0, 0, 0, 0, 0);
+TPS_PDATA_INIT(smps1, 600, 2100, 0, 0, 0, 0, -1, 0, 0, 0, PWR_REQ_INPUT_PREQ2 | PWR_OFF_ON_SLEEP, 0);
+TPS_PDATA_INIT(smps2, 600, 2100, 0, 0, 0, 0, -1, 0, 0, 0, PWR_REQ_INPUT_PREQ1, 0);
+TPS_PDATA_INIT(smps3, 600, 2100, 0, 1, 0, 0, -1, 0, 0, 0, 0, 0);
+TPS_PDATA_INIT(smps4, 600, 2100, 0, 0, 0, 0, -1, 0, 0, 0, PWR_REQ_INPUT_PREQ1, 0);
+TPS_PDATA_INIT(ldo1, 1000, 3300, tps80031_rails(VIO), 0, 0, 0, -1, 0, 0, 0, 0, 0);
+TPS_PDATA_INIT(ldo2, 1000, 3300, 0, 1, 1, 1, 1000, 1, 1, 0, 0, 0);
+TPS_PDATA_INIT(ldo3, 1000, 3300, tps80031_rails(VIO), 0, 0, 0, -1, 0, 0, 0, PWR_OFF_ON_SLEEP, 0);
+TPS_PDATA_INIT(ldo4, 1000, 3300, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0);
+TPS_PDATA_INIT(ldo5, 1000, 3300, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0);
+TPS_PDATA_INIT(ldo6, 1000, 3300, 0, 0, 0, 0, -1, 0, 0, 0, PWR_REQ_INPUT_PREQ1, 0);
+TPS_PDATA_INIT(ldo7, 1000, 3300, tps80031_rails(VIO), 0, 0, 0, -1, 0, 0, 0, PWR_REQ_INPUT_PREQ1, 0);
+TPS_PDATA_INIT(ldoln, 1000, 3300, tps80031_rails(SMPS3), 0, 0, 0, -1, 0, 0, 0, PWR_REQ_INPUT_PREQ1, 0);
+TPS_PDATA_INIT(ldousb, 1000, 3300, 0, 0, 0, 0, -1, 0, 0, USBLDO_INPUT_VSYS, PWR_OFF_ON_SLEEP, 0);
+TPS_PDATA_INIT(vana, 1000, 3300, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0);
+TPS_PDATA_INIT(vbus, 0, 5000, 0, 0, 0, 0, -1, 0, 0, (VBUS_SW_ONLY | VBUS_DISCHRG_EN_PDN), 0, 100000);
+
+static struct tps80031_rtc_platform_data rtc_data = {
+ .irq = ENT_TPS80031_IRQ_BASE + TPS80031_INT_RTC_ALARM,
+ .time = {
+ .tm_year = 2011,
+ .tm_mon = 0,
+ .tm_mday = 1,
+ .tm_hour = 1,
+ .tm_min = 2,
+ .tm_sec = 3,
+ },
+};
+
+int battery_charger_init(void *board_data)
+{
+ int ret;
+ ret = gpio_request(TEGRA_GPIO_PF6, "lcd_d14-bat_charge");
+ if (ret < 0) {
+ pr_err("%s() The gpio_request for battery"
+ " charger fails\n", __func__);
+ }
+ gpio_direction_output(TEGRA_GPIO_PF6, 1);
+ tegra_gpio_enable(TEGRA_GPIO_PF6);
+ return 0;
+}
+
+static struct tps80031_charger_platform_data bcharger_pdata = {
+ .max_charge_volt_mV = 4100,
+ .max_charge_current_mA = 1000,
+ .charging_term_current_mA = 100,
+ .watch_time_sec = 100,
+ .irq_base = ENT_TPS80031_IRQ_BASE,
+ .consumer_supplies = tps80031_battery_charge_supply,
+ .num_consumer_supplies = ARRAY_SIZE(tps80031_battery_charge_supply),
+ .board_init = battery_charger_init,
+ .board_data = NULL,
+};
+
+static struct tps80031_bg_platform_data battery_gauge_data = {
+ .irq_base = ENT_TPS80031_IRQ_BASE,
+ .battery_present = 1,
+};
+
+#define TPS_RTC() \
+ { \
+ .id = 0, \
+ .name = "rtc_tps80031", \
+ .platform_data = &rtc_data, \
+ }
+
+#define TPS_REG(_id, _data) \
+ { \
+ .id = TPS80031_ID_##_id, \
+ .name = "tps80031-regulator", \
+ .platform_data = &pdata_##_data, \
+ }
+#define TPS_BATTERY() \
+ { \
+ .name = "tps80031-charger", \
+ .platform_data = &bcharger_pdata, \
+ }
+#define TPS_BATTERY_GAUGE() \
+ { \
+ .name = "tps80031-battery-gauge", \
+ .platform_data = &battery_gauge_data, \
+ }
+#define TPS_GPADC() \
+ { \
+ .name = "tps80031-gpadc", \
+ }
+
+static struct tps80031_subdev_info tps80031_devs[] = {
+ TPS_REG(VIO, vio),
+ TPS_REG(SMPS1, smps1),
+ TPS_REG(SMPS2, smps2),
+ TPS_REG(SMPS3, smps3),
+ TPS_REG(SMPS4, smps4),
+ TPS_REG(LDO1, ldo1),
+ TPS_REG(LDO2, ldo2),
+ TPS_REG(LDO3, ldo3),
+ TPS_REG(LDO4, ldo4),
+ TPS_REG(LDO5, ldo5),
+ TPS_REG(LDO6, ldo6),
+ TPS_REG(LDO7, ldo7),
+ TPS_REG(LDOLN, ldoln),
+ TPS_REG(LDOUSB, ldousb),
+ TPS_REG(VANA, vana),
+ TPS_REG(VBUS, vbus),
+ TPS_RTC(),
+ TPS_BATTERY(),
+ TPS_BATTERY_GAUGE(),
+ TPS_GPADC(),
+};
+
+struct tps80031_clk32k_init_data clk32k_idata[] = {
+ {
+ .clk32k_nr = TPS80031_CLOCK32K_G,
+ .enable = true,
+ .ext_ctrl_flag = PWR_REQ_INPUT_PREQ1,
+ },
+ {
+ .clk32k_nr = TPS80031_CLOCK32K_AUDIO,
+ .enable = true,
+ .ext_ctrl_flag = PWR_REQ_INPUT_PREQ1,
+ },
+};
+
+static struct tps80031_platform_data tps_platform = {
+ .num_subdevs = ARRAY_SIZE(tps80031_devs),
+ .subdevs = tps80031_devs,
+ .irq_base = ENT_TPS80031_IRQ_BASE,
+ .gpio_base = ENT_TPS80031_GPIO_BASE,
+ .clk32k_init_data = clk32k_idata,
+ .clk32k_init_data_size = ARRAY_SIZE(clk32k_idata),
+};
+
+static struct i2c_board_info __initdata enterprise_regulators[] = {
+ {
+ I2C_BOARD_INFO("tps80031", 0x4A),
+ .irq = INT_EXTERNAL_PMU,
+ .platform_data = &tps_platform,
+ },
+};
+
+/************************ GPIO based switch regulator ****************/
+
+/* REGEN1 from PMU*/
+static struct regulator_consumer_supply gpio_switch_pmu_5v15_en_supply[] = {
+ REGULATOR_SUPPLY("vdd_5v15", NULL),
+};
+static int gpio_switch_pmu_5v15_en_voltages[] = {5000};
+
+/* REGEN2 from PMU*/
+static struct regulator_consumer_supply gpio_switch_pmu_3v3_en_supply[] = {
+ REGULATOR_SUPPLY("avdd_usb_hdmi_3v3", NULL),
+ REGULATOR_SUPPLY("avdd_usb", NULL),
+ REGULATOR_SUPPLY("avdd_hdmi", NULL),
+ REGULATOR_SUPPLY("vdd", "4-004c"),
+};
+static int gpio_switch_pmu_3v3_en_voltages[] = {3300};
+
+/* SYSEN from PMU*/
+static struct regulator_consumer_supply gpio_switch_pmu_hdmi_5v0_en_supply[] = {
+ REGULATOR_SUPPLY("hdmi_5v0", NULL),
+};
+static int gpio_switch_pmu_hdmi_5v0_en_voltages[] = {5000};
+
+/* LCD-D16 (GPIO M0) from T30*/
+static struct regulator_consumer_supply gpio_switch_vdd_fuse_en_supply[] = {
+ REGULATOR_SUPPLY("vdd_fuse", NULL),
+};
+static int gpio_switch_vdd_fuse_en_voltages[] = {3300};
+
+/* LCD-D17 (GPIO M1) from T30*/
+static struct regulator_consumer_supply gpio_switch_sdmmc3_vdd_sel_supply[] = {
+ REGULATOR_SUPPLY("vddio_sdmmc3_2v85_1v8", NULL),
+ REGULATOR_SUPPLY("sdmmc3_compu_pu", NULL),
+ REGULATOR_SUPPLY("vddio_sdmmc3", NULL),
+ REGULATOR_SUPPLY("vsys_3v7", NULL),
+};
+static int gpio_switch_sdmmc3_vdd_sel_voltages[] = {2850};
+
+/* LCD-D23 (GPIO M7) from T30*/
+/* 2-0036 is dev_name of ar0832 in Enterprise A01*/
+/* 2-0032 is alternative dev_name of ar0832 Enterprise A01*/
+/* 2-0010 is dev_name of ov9726 */
+/* 2-0070 is dev_name of PCA9546 in Enterprise A02*/
+/* 6-0036 is dev_name of ar0832 in Enterprise A02 */
+/* 7-0036 is dev_name of ar0832 in Enterprise A02 */
+static struct regulator_consumer_supply gpio_switch_cam_ldo_2v8_en_supply[] = {
+ REGULATOR_SUPPLY("vaa", "2-0036"),
+ REGULATOR_SUPPLY("vaa", "2-0032"),
+ REGULATOR_SUPPLY("avdd", "2-0010"),
+ REGULATOR_SUPPLY("vdd_2v8_cam", NULL),
+ REGULATOR_SUPPLY("vcc", "2-0070"),
+ REGULATOR_SUPPLY("vaa", "6-0036"),
+ REGULATOR_SUPPLY("vaa", "7-0036"),
+};
+static int gpio_switch_cam_ldo_2v8_en_voltages[] = {2800};
+
+/* LCD-D9 (GPIO F1) from T30*/
+/* 2-0036 is dev_name of ar0832 in Enterprise A01*/
+/* 2-0032 is alternative dev_name of ar0832 Enterprise A01*/
+/* 2-0010 is dev_name of ov9726 */
+/* 2-0033 is dev_name of tps61050 */
+/* 2-0070 is dev_name of PCA9546 in Enterprise A02*/
+/* 6-0036 is dev_name of ar0832 in Enterprise A02 */
+/* 7-0036 is dev_name of ar0832 in Enterprise A02 */
+static struct regulator_consumer_supply gpio_switch_cam_ldo_1v8_en_supply[] = {
+ REGULATOR_SUPPLY("vdd", "2-0036"),
+ REGULATOR_SUPPLY("vdd", "2-0032"),
+ REGULATOR_SUPPLY("dovdd", "2-0010"),
+ REGULATOR_SUPPLY("vdd_1v8_cam", NULL),
+ REGULATOR_SUPPLY("vdd_i2c", "2-0033"),
+ REGULATOR_SUPPLY("vcc_i2c", "2-0070"),
+ REGULATOR_SUPPLY("vdd", "6-0036"),
+ REGULATOR_SUPPLY("vdd", "7-0036"),
+};
+static int gpio_switch_cam_ldo_1v8_en_voltages[] = {1800};
+
+/* Macro for defining gpio switch regulator sub device data */
+#define GREG_INIT(_id, _name, _input_supply, _gpio_nr, _active_low, \
+ _init_state, _pg, _enable, _disable) \
+ static struct gpio_switch_regulator_subdev_data gpio_pdata_##_name = \
+ { \
+ .regulator_name = "gpio-switch-"#_name, \
+ .input_supply = _input_supply, \
+ .id = _id, \
+ .gpio_nr = _gpio_nr, \
+ .pin_group = _pg, \
+ .active_low = _active_low, \
+ .init_state = _init_state, \
+ .voltages = gpio_switch_##_name##_voltages, \
+ .n_voltages = ARRAY_SIZE(gpio_switch_##_name##_voltages), \
+ .num_consumer_supplies = \
+ ARRAY_SIZE(gpio_switch_##_name##_supply), \
+ .consumer_supplies = gpio_switch_##_name##_supply, \
+ .constraints = { \
+ .valid_modes_mask = (REGULATOR_MODE_NORMAL | \
+ REGULATOR_MODE_STANDBY), \
+ .valid_ops_mask = (REGULATOR_CHANGE_MODE | \
+ REGULATOR_CHANGE_STATUS | \
+ REGULATOR_CHANGE_VOLTAGE), \
+ }, \
+ .enable_rail = _enable, \
+ .disable_rail = _disable, \
+ }
+
+GREG_INIT(0, pmu_5v15_en, NULL, ENT_TPS80031_GPIO_REGEN1, false, 0, 0, 0, 0);
+GREG_INIT(1, pmu_3v3_en, "vdd_5v15", ENT_TPS80031_GPIO_REGEN2, false, 0, 0, 0, 0);
+GREG_INIT(2, pmu_hdmi_5v0_en, "vdd_5v15", ENT_TPS80031_GPIO_SYSEN, false, 0, 0, 0, 0);
+
+GREG_INIT(3, vdd_fuse_en, "avdd_usb_hdmi_3v3", TEGRA_GPIO_PM0, false, 0, 0, 0, 0);
+GREG_INIT(4, sdmmc3_vdd_sel, "vddio_sdmmc_2v85", TEGRA_GPIO_PM1, false, 0, 0, 0, 0);
+GREG_INIT(5, cam_ldo_2v8_en, NULL, TEGRA_GPIO_PM7, false, 0, 0, 0, 0);
+GREG_INIT(6, cam_ldo_1v8_en, NULL, TEGRA_GPIO_PF1, false, 0, 0, 0, 0);
+
+#define ADD_GPIO_REG(_name) (&gpio_pdata_##_name)
+static struct gpio_switch_regulator_subdev_data *gswitch_subdevs[] = {
+ ADD_GPIO_REG(pmu_5v15_en),
+ ADD_GPIO_REG(pmu_3v3_en),
+ ADD_GPIO_REG(pmu_hdmi_5v0_en),
+ ADD_GPIO_REG(vdd_fuse_en),
+ ADD_GPIO_REG(sdmmc3_vdd_sel),
+ ADD_GPIO_REG(cam_ldo_2v8_en),
+ ADD_GPIO_REG(cam_ldo_1v8_en),
+};
+
+static struct gpio_switch_regulator_platform_data gswitch_pdata = {
+ .num_subdevs = ARRAY_SIZE(gswitch_subdevs),
+ .subdevs = gswitch_subdevs,
+};
+
+static struct platform_device gswitch_regulator_pdata = {
+ .name = "gpio-switch-regulator",
+ .id = -1,
+ .dev = {
+ .platform_data = &gswitch_pdata,
+ },
+};
+
+static int __init enterprise_gpio_switch_regulator_init(void)
+{
+ int i;
+ for (i = 0; i < gswitch_pdata.num_subdevs; ++i) {
+ struct gpio_switch_regulator_subdev_data *gswitch_data =
+ gswitch_pdata.subdevs[i];
+ if (gswitch_data->gpio_nr <= TEGRA_NR_GPIOS)
+ tegra_gpio_enable(gswitch_data->gpio_nr);
+ }
+ return platform_device_register(&gswitch_regulator_pdata);
+}
+
+static void enterprise_power_off(void)
+{
+ int ret;
+ pr_info("enterprise: Powering off the device\n");
+ ret = tps80031_power_off();
+ if (ret)
+ pr_err("enterprise: failed to power off\n");
+ while(1);
+}
+
+void __init enterprise_tsensor_init(void)
+{
+ tegra3_tsensor_init(NULL);
+}
+
+int __init enterprise_regulator_init(void)
+{
+ void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE);
+ u32 pmc_ctrl;
+
+ /* configure the power management controller to trigger PMU
+ * interrupts when low */
+
+ pmc_ctrl = readl(pmc + PMC_CTRL);
+ writel(pmc_ctrl | PMC_CTRL_INTR_LOW, pmc + PMC_CTRL);
+
+ /* Disable battery charging if power adapter is connected. */
+ if (get_power_supply_type() == POWER_SUPPLY_TYPE_MAINS) {
+ bcharger_pdata.num_consumer_supplies = 0;
+ bcharger_pdata.consumer_supplies = NULL;
+ battery_gauge_data.battery_present = 0;
+ }
+
+ i2c_register_board_info(4, enterprise_regulators, 1);
+ enterprise_gpio_switch_regulator_init();
+ pm_power_off = enterprise_power_off;
+
+ return 0;
+}
+
+static void enterprise_board_suspend(int lp_state, enum suspend_stage stg)
+{
+ if ((lp_state == TEGRA_SUSPEND_LP1) && (stg == TEGRA_SUSPEND_BEFORE_CPU))
+ tegra_console_uart_suspend();
+}
+
+static void enterprise_board_resume(int lp_state, enum resume_stage stg)
+{
+ if ((lp_state == TEGRA_SUSPEND_LP1) && (stg == TEGRA_RESUME_AFTER_CPU))
+ tegra_console_uart_resume();
+}
+
+static struct tegra_suspend_platform_data enterprise_suspend_data = {
+ .cpu_timer = 2000,
+ .cpu_off_timer = 200,
+ .suspend_mode = TEGRA_SUSPEND_LP0,
+ .core_timer = 0x7e7e,
+ .core_off_timer = 0,
+ .corereq_high = true,
+ .sysclkreq_high = true,
+ .board_suspend = enterprise_board_suspend,
+ .board_resume = enterprise_board_resume,
+};
+
+static void enterprise_init_deep_sleep_mode(void)
+{
+ struct board_info bi;
+ tegra_get_board_info(&bi);
+
+ if (bi.board_id == BOARD_E1205 && bi.fab == BOARD_FAB_A01)
+ enterprise_suspend_data.suspend_mode = TEGRA_SUSPEND_LP1;
+
+ if ((bi.board_id == BOARD_E1205 && (bi.sku & BOARD_SKU_VF_BIT) == 0) ||
+ (bi.board_id == BOARD_E1197 && (bi.sku & BOARD_SKU_VF_BIT)))
+ enterprise_suspend_data.cpu_timer = 8000;
+}
+
+int __init enterprise_suspend_init(void)
+{
+ enterprise_init_deep_sleep_mode();
+ tegra_init_suspend(&enterprise_suspend_data);
+ return 0;
+}
+
+#ifdef CONFIG_TEGRA_EDP_LIMITS
+
+int __init enterprise_edp_init(void)
+{
+ unsigned int regulator_mA;
+
+ regulator_mA = get_maximum_cpu_current_supported();
+ if (!regulator_mA) {
+ regulator_mA = 2500; /* regular AP30 */
+ }
+ pr_info("%s: CPU regulator %d mA\n", __func__, regulator_mA);
+
+ tegra_init_cpu_edp_limits(regulator_mA);
+ return 0;
+}
+#endif
diff --git a/arch/arm/mach-tegra/board-enterprise-sdhci.c b/arch/arm/mach-tegra/board-enterprise-sdhci.c
new file mode 100644
index 000000000000..af1a9ea3dd59
--- /dev/null
+++ b/arch/arm/mach-tegra/board-enterprise-sdhci.c
@@ -0,0 +1,269 @@
+/*
+ * arch/arm/mach-tegra/board-enterprise-sdhci.c
+ *
+ * Copyright (C) 2011 NVIDIA Corporation.
+ *
+ * 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/resource.h>
+#include <linux/platform_device.h>
+#include <linux/wlan_plat.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/mmc/host.h>
+
+#include <asm/mach-types.h>
+#include <mach/irqs.h>
+#include <mach/iomap.h>
+#include <mach/sdhci.h>
+
+#include "gpio-names.h"
+#include "board.h"
+
+
+#define ENTERPRISE_WLAN_PWR TEGRA_GPIO_PV2
+#define ENTERPRISE_WLAN_RST TEGRA_GPIO_PV3
+#define ENTERPRISE_WLAN_WOW TEGRA_GPIO_PU6
+#define ENTERPRISE_SD_CD TEGRA_GPIO_PI5
+
+static void (*wifi_status_cb)(int card_present, void *dev_id);
+static void *wifi_status_cb_devid;
+static int enterprise_wifi_status_register(void (*callback)(int , void *), void *);
+
+static int enterprise_wifi_reset(int on);
+static int enterprise_wifi_power(int on);
+static int enterprise_wifi_set_carddetect(int val);
+
+static struct wifi_platform_data enterprise_wifi_control = {
+ .set_power = enterprise_wifi_power,
+ .set_reset = enterprise_wifi_reset,
+ .set_carddetect = enterprise_wifi_set_carddetect,
+};
+
+static struct resource wifi_resource[] = {
+ [0] = {
+ .name = "bcm4329_wlan_irq",
+ .start = TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PU6),
+ .end = TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PU6),
+ .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL | IORESOURCE_IRQ_SHAREABLE,
+ },
+};
+
+static struct platform_device enterprise_wifi_device = {
+ .name = "bcm4329_wlan",
+ .id = 1,
+ .num_resources = 1,
+ .resource = wifi_resource,
+ .dev = {
+ .platform_data = &enterprise_wifi_control,
+ },
+};
+
+static struct resource sdhci_resource0[] = {
+ [0] = {
+ .start = INT_SDMMC1,
+ .end = INT_SDMMC1,
+ .flags = IORESOURCE_IRQ,
+ },
+ [1] = {
+ .start = TEGRA_SDMMC1_BASE,
+ .end = TEGRA_SDMMC1_BASE + TEGRA_SDMMC1_SIZE-1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct resource sdhci_resource2[] = {
+ [0] = {
+ .start = INT_SDMMC3,
+ .end = INT_SDMMC3,
+ .flags = IORESOURCE_IRQ,
+ },
+ [1] = {
+ .start = TEGRA_SDMMC3_BASE,
+ .end = TEGRA_SDMMC3_BASE + TEGRA_SDMMC3_SIZE-1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct resource sdhci_resource3[] = {
+ [0] = {
+ .start = INT_SDMMC4,
+ .end = INT_SDMMC4,
+ .flags = IORESOURCE_IRQ,
+ },
+ [1] = {
+ .start = TEGRA_SDMMC4_BASE,
+ .end = TEGRA_SDMMC4_BASE + TEGRA_SDMMC4_SIZE-1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct embedded_sdio_data embedded_sdio_data0 = {
+ .cccr = {
+ .sdio_vsn = 2,
+ .multi_block = 1,
+ .low_speed = 0,
+ .wide_bus = 0,
+ .high_power = 1,
+ .high_speed = 1,
+ },
+ .cis = {
+ .vendor = 0x02d0,
+ .device = 0x4329,
+ },
+};
+
+static struct tegra_sdhci_platform_data tegra_sdhci_platform_data0 = {
+ .mmc_data = {
+ .register_status_notify = enterprise_wifi_status_register,
+ .embedded_sdio = &embedded_sdio_data0,
+ /* FIXME need to revert the built_in change
+ once we use get the signal strength fix of
+ bcmdhd driver from broadcom for bcm4329 chipset*/
+ .built_in = 0,
+ },
+ .cd_gpio = -1,
+ .wp_gpio = -1,
+ .power_gpio = -1,
+ .max_clk_limit = 45000000,
+};
+
+static struct tegra_sdhci_platform_data tegra_sdhci_platform_data2 = {
+ .cd_gpio = -1,
+ .wp_gpio = -1,
+ .power_gpio = -1,
+};
+
+static struct tegra_sdhci_platform_data tegra_sdhci_platform_data3 = {
+ .cd_gpio = -1,
+ .wp_gpio = -1,
+ .power_gpio = -1,
+ .is_8bit = 1,
+ .mmc_data = {
+ .built_in = 1,
+ }
+};
+
+static struct platform_device tegra_sdhci_device0 = {
+ .name = "sdhci-tegra",
+ .id = 0,
+ .resource = sdhci_resource0,
+ .num_resources = ARRAY_SIZE(sdhci_resource0),
+ .dev = {
+ .platform_data = &tegra_sdhci_platform_data0,
+ },
+};
+
+static struct platform_device tegra_sdhci_device2 = {
+ .name = "sdhci-tegra",
+ .id = 2,
+ .resource = sdhci_resource2,
+ .num_resources = ARRAY_SIZE(sdhci_resource2),
+ .dev = {
+ .platform_data = &tegra_sdhci_platform_data2,
+ },
+};
+
+static struct platform_device tegra_sdhci_device3 = {
+ .name = "sdhci-tegra",
+ .id = 3,
+ .resource = sdhci_resource3,
+ .num_resources = ARRAY_SIZE(sdhci_resource3),
+ .dev = {
+ .platform_data = &tegra_sdhci_platform_data3,
+ },
+};
+
+static int enterprise_wifi_status_register(
+ void (*callback)(int card_present, void *dev_id),
+ void *dev_id)
+{
+ if (wifi_status_cb)
+ return -EAGAIN;
+ wifi_status_cb = callback;
+ wifi_status_cb_devid = dev_id;
+ return 0;
+}
+
+static int enterprise_wifi_set_carddetect(int val)
+{
+ pr_debug("%s: %d\n", __func__, val);
+ if (wifi_status_cb)
+ wifi_status_cb(val, wifi_status_cb_devid);
+ else
+ pr_warning("%s: Nobody to notify\n", __func__);
+ return 0;
+}
+
+static int enterprise_wifi_power(int on)
+{
+ pr_debug("%s: %d\n", __func__, on);
+ gpio_set_value(ENTERPRISE_WLAN_PWR, on);
+ mdelay(100);
+ gpio_set_value(ENTERPRISE_WLAN_RST, on);
+ mdelay(200);
+
+ return 0;
+}
+
+static int enterprise_wifi_reset(int on)
+{
+ pr_debug("%s: do nothing\n", __func__);
+ return 0;
+}
+
+static int __init enterprise_wifi_init(void)
+{
+ int rc;
+
+ rc = gpio_request(ENTERPRISE_WLAN_PWR, "wlan_power");
+ if (rc)
+ pr_err("WLAN_PWR gpio request failed:%d\n", rc);
+ rc = gpio_request(ENTERPRISE_WLAN_RST, "wlan_rst");
+ if (rc)
+ pr_err("WLAN_RST gpio request failed:%d\n", rc);
+ rc = gpio_request(ENTERPRISE_WLAN_WOW, "bcmsdh_sdmmc");
+ if (rc)
+ pr_err("WLAN_WOW gpio request failed:%d\n", rc);
+
+ tegra_gpio_enable(ENTERPRISE_WLAN_PWR);
+ tegra_gpio_enable(ENTERPRISE_WLAN_RST);
+ tegra_gpio_enable(ENTERPRISE_WLAN_WOW);
+
+ rc = gpio_direction_output(ENTERPRISE_WLAN_PWR, 0);
+ if (rc)
+ pr_err("WLAN_PWR gpio direction configuration failed:%d\n", rc);
+ gpio_direction_output(ENTERPRISE_WLAN_RST, 0);
+ if (rc)
+ pr_err("WLAN_RST gpio direction configuration failed:%d\n", rc);
+ rc = gpio_direction_input(ENTERPRISE_WLAN_WOW);
+ if (rc)
+ pr_err("WLAN_WOW gpio direction configuration failed:%d\n", rc);
+
+ platform_device_register(&enterprise_wifi_device);
+ return 0;
+}
+
+int __init enterprise_sdhci_init(void)
+{
+ platform_device_register(&tegra_sdhci_device3);
+
+ tegra_gpio_enable(ENTERPRISE_SD_CD);
+ tegra_sdhci_platform_data2.cd_gpio = ENTERPRISE_SD_CD;
+ platform_device_register(&tegra_sdhci_device2);
+
+ platform_device_register(&tegra_sdhci_device0);
+ enterprise_wifi_init();
+ return 0;
+}
diff --git a/arch/arm/mach-tegra/board-enterprise-sensors.c b/arch/arm/mach-tegra/board-enterprise-sensors.c
new file mode 100644
index 000000000000..b7b10109286c
--- /dev/null
+++ b/arch/arm/mach-tegra/board-enterprise-sensors.c
@@ -0,0 +1,613 @@
+/*
+ * arch/arm/mach-tegra/board-enterprise-sensors.c
+ *
+ * Copyright (c) 2011, NVIDIA CORPORATION, All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Neither the name of NVIDIA CORPORATION nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/i2c/pca954x.h>
+#include <linux/nct1008.h>
+#include <linux/err.h>
+#include <linux/mpu.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <mach/gpio.h>
+#include <media/ar0832_main.h>
+#include <media/tps61050.h>
+#include <media/ov9726.h>
+#include <mach/edp.h>
+#include <mach/thermal.h>
+#include "cpu-tegra.h"
+#include "gpio-names.h"
+#include "board-enterprise.h"
+#include "board.h"
+
+#ifndef CONFIG_TEGRA_INTERNAL_TSENSOR_EDP_SUPPORT
+static int nct_get_temp(void *_data, long *temp)
+{
+ struct nct1008_data *data = _data;
+ return nct1008_thermal_get_temp(data, temp);
+}
+
+static int nct_set_limits(void *_data,
+ long lo_limit_milli,
+ long hi_limit_milli)
+{
+ struct nct1008_data *data = _data;
+ return nct1008_thermal_set_limits(data,
+ lo_limit_milli,
+ hi_limit_milli);
+}
+
+static int nct_set_alert(void *_data,
+ void (*alert_func)(void *),
+ void *alert_data)
+{
+ struct nct1008_data *data = _data;
+ return nct1008_thermal_set_alert(data, alert_func, alert_data);
+}
+
+static int nct_set_shutdown_temp(void *_data, long shutdown_temp)
+{
+ struct nct1008_data *data = _data;
+ return nct1008_thermal_set_shutdown_temp(data,
+ shutdown_temp);
+}
+
+static void nct1008_probe_callback(struct nct1008_data *data)
+{
+ struct tegra_thermal_device *thermal_device;
+
+ thermal_device = kzalloc(sizeof(struct tegra_thermal_device),
+ GFP_KERNEL);
+ if (!thermal_device) {
+ pr_err("unable to allocate thermal device\n");
+ return;
+ }
+
+ thermal_device->data = data;
+ thermal_device->offset = TDIODE_OFFSET;
+ thermal_device->get_temp = nct_get_temp;
+ thermal_device->set_limits = nct_set_limits;
+ thermal_device->set_alert = nct_set_alert;
+ thermal_device->set_shutdown_temp = nct_set_shutdown_temp;
+
+ tegra_thermal_set_device(thermal_device);
+}
+#endif
+
+static struct nct1008_platform_data enterprise_nct1008_pdata = {
+ .supported_hwrev = true,
+ .ext_range = true,
+ .conv_rate = 0x08,
+ .offset = 8, /* 4 * 2C. Bug 844025 - 1C for device accuracies */
+#ifndef CONFIG_TEGRA_INTERNAL_TSENSOR_EDP_SUPPORT
+ .probe_callback = nct1008_probe_callback,
+#endif
+};
+
+static struct i2c_board_info enterprise_i2c4_nct1008_board_info[] = {
+ {
+ I2C_BOARD_INFO("nct1008", 0x4C),
+ .irq = TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PH7),
+ .platform_data = &enterprise_nct1008_pdata,
+ }
+};
+
+static void enterprise_nct1008_init(void)
+{
+ int ret;
+
+ tegra_gpio_enable(TEGRA_GPIO_PH7);
+ ret = gpio_request(TEGRA_GPIO_PH7, "temp_alert");
+ if (ret < 0) {
+ pr_err("%s: gpio_request failed %d\n", __func__, ret);
+ return;
+ }
+
+ ret = gpio_direction_input(TEGRA_GPIO_PH7);
+ if (ret < 0) {
+ pr_err("%s: gpio_direction_input failed %d\n", __func__, ret);
+ gpio_free(TEGRA_GPIO_PH7);
+ return;
+ }
+
+ i2c_register_board_info(4, enterprise_i2c4_nct1008_board_info,
+ ARRAY_SIZE(enterprise_i2c4_nct1008_board_info));
+}
+
+static struct mpu_platform_data mpu3050_data = {
+ .int_config = 0x10,
+ .level_shifter = 0,
+ .orientation = MPU_GYRO_ORIENTATION, /* Located in board_[platformname].h */
+};
+
+static struct ext_slave_platform_data mpu3050_accel_data = {
+ .address = MPU_ACCEL_ADDR,
+ .irq = 0,
+ .adapt_num = MPU_ACCEL_BUS_NUM,
+ .bus = EXT_SLAVE_BUS_SECONDARY,
+ .orientation = MPU_ACCEL_ORIENTATION, /* Located in board_[platformname].h */
+};
+
+static struct ext_slave_platform_data mpu_compass_data = {
+ .address = MPU_COMPASS_ADDR,
+ .irq = 0,
+ .adapt_num = MPU_COMPASS_BUS_NUM,
+ .bus = EXT_SLAVE_BUS_PRIMARY,
+ .orientation = MPU_COMPASS_ORIENTATION, /* Located in board_[platformname].h */
+};
+
+static struct i2c_board_info __initdata inv_mpu_i2c2_board_info[] = {
+ {
+ I2C_BOARD_INFO(MPU_GYRO_NAME, MPU_GYRO_ADDR),
+ .irq = TEGRA_GPIO_TO_IRQ(MPU_GYRO_IRQ_GPIO),
+ .platform_data = &mpu3050_data,
+ },
+ {
+ I2C_BOARD_INFO(MPU_ACCEL_NAME, MPU_ACCEL_ADDR),
+#if MPU_ACCEL_IRQ_GPIO
+ .irq = TEGRA_GPIO_TO_IRQ(MPU_ACCEL_IRQ_GPIO),
+#endif
+ .platform_data = &mpu3050_accel_data,
+ },
+ {
+ I2C_BOARD_INFO(MPU_COMPASS_NAME, MPU_COMPASS_ADDR),
+#if MPU_COMPASS_IRQ_GPIO
+ .irq = TEGRA_GPIO_TO_IRQ(MPU_COMPASS_IRQ_GPIO),
+#endif
+ .platform_data = &mpu_compass_data,
+ },
+};
+
+static void mpuirq_init(void)
+{
+ int ret = 0;
+
+ pr_info("*** MPU START *** mpuirq_init...\n");
+
+#if MPU_ACCEL_IRQ_GPIO
+ /* ACCEL-IRQ assignment */
+ tegra_gpio_enable(MPU_ACCEL_IRQ_GPIO);
+ ret = gpio_request(MPU_ACCEL_IRQ_GPIO, MPU_ACCEL_NAME);
+ if (ret < 0) {
+ pr_err("%s: gpio_request failed %d\n", __func__, ret);
+ return;
+ }
+
+ ret = gpio_direction_input(MPU_ACCEL_IRQ_GPIO);
+ if (ret < 0) {
+ pr_err("%s: gpio_direction_input failed %d\n", __func__, ret);
+ gpio_free(MPU_ACCEL_IRQ_GPIO);
+ return;
+ }
+#endif
+
+ /* MPU-IRQ assignment */
+ tegra_gpio_enable(MPU_GYRO_IRQ_GPIO);
+ ret = gpio_request(MPU_GYRO_IRQ_GPIO, MPU_GYRO_NAME);
+ if (ret < 0) {
+ pr_err("%s: gpio_request failed %d\n", __func__, ret);
+ return;
+ }
+
+ ret = gpio_direction_input(MPU_GYRO_IRQ_GPIO);
+ if (ret < 0) {
+ pr_err("%s: gpio_direction_input failed %d\n", __func__, ret);
+ gpio_free(MPU_GYRO_IRQ_GPIO);
+ return;
+ }
+ pr_info("*** MPU END *** mpuirq_init...\n");
+
+ i2c_register_board_info(MPU_GYRO_BUS_NUM, inv_mpu_i2c2_board_info,
+ ARRAY_SIZE(inv_mpu_i2c2_board_info));
+}
+
+static inline void enterprise_msleep(u32 t)
+{
+ /*
+ If timer value is between ( 10us - 20ms),
+ usleep_range() is recommended.
+ Please read Documentation/timers/timers-howto.txt.
+ */
+ usleep_range(t*1000, t*1000 + 500);
+}
+
+static struct i2c_board_info enterprise_i2c0_isl_board_info[] = {
+ {
+ I2C_BOARD_INFO("isl29028", 0x44),
+ }
+};
+
+static void enterprise_isl_init(void)
+{
+ i2c_register_board_info(0, enterprise_i2c0_isl_board_info,
+ ARRAY_SIZE(enterprise_i2c0_isl_board_info));
+}
+
+enum CAMERA_INDEX {
+ CAM_REAR_LEFT,
+ CAM_REAR_RIGHT,
+ CAM_FRONT,
+ NUM_OF_CAM
+};
+
+struct enterprise_power_rail {
+ struct regulator *cam_reg;
+ struct regulator *csi_reg;
+};
+
+static struct enterprise_power_rail ent_vicsi_pwr[NUM_OF_CAM];
+
+static int enterprise_cam_pwr(enum CAMERA_INDEX cam, bool pwr_on)
+{
+ struct enterprise_power_rail *reg_cam = &ent_vicsi_pwr[cam];
+ int ret = 0;
+
+ /*
+ * SW must turn on 1.8V first then 2.8V
+ * SW must turn off 2.8V first then 1.8V
+ */
+ if (pwr_on) {
+ if (reg_cam->csi_reg == NULL) {
+ reg_cam->csi_reg = regulator_get(NULL,
+ "avdd_dsi_csi");
+ if (IS_ERR_OR_NULL(reg_cam->csi_reg)) {
+ pr_err("%s: csi pwr err\n", __func__);
+ ret = PTR_ERR(reg_cam->csi_reg);
+ goto enterprise_cam_pwr_fail;
+ }
+ }
+
+ ret = regulator_enable(reg_cam->csi_reg);
+ if (ret) {
+ pr_err("%s: enable csi pwr err\n", __func__);
+ goto enterprise_cam_pwr_fail;
+ }
+
+ if (reg_cam->cam_reg == NULL) {
+ reg_cam->cam_reg = regulator_get(NULL,
+ "vddio_cam");
+ if (IS_ERR_OR_NULL(reg_cam->cam_reg)) {
+ pr_err("%s: vddio pwr err\n", __func__);
+ ret = PTR_ERR(reg_cam->cam_reg);
+ regulator_disable(reg_cam->csi_reg);
+ goto enterprise_cam_pwr_fail;
+ }
+ }
+
+ ret = regulator_enable(reg_cam->cam_reg);
+ if (ret) {
+ pr_err("%s: enable vddio pwr err\n", __func__);
+ regulator_disable(reg_cam->csi_reg);
+ goto enterprise_cam_pwr_fail;
+ }
+ } else {
+ if (reg_cam->cam_reg)
+ regulator_disable(reg_cam->cam_reg);
+
+ if (reg_cam->csi_reg)
+ regulator_disable(reg_cam->csi_reg);
+ }
+ return 0;
+
+enterprise_cam_pwr_fail:
+ if (!IS_ERR_OR_NULL(reg_cam->cam_reg))
+ regulator_put(reg_cam->cam_reg);
+ reg_cam->cam_reg = NULL;
+
+ if (!IS_ERR_OR_NULL(reg_cam->csi_reg))
+ regulator_put(reg_cam->csi_reg);
+ reg_cam->csi_reg = NULL;
+
+ return ret;
+}
+
+static int enterprise_ar0832_ri_power_on(int is_stereo)
+{
+ int ret = 0;
+
+ pr_info("%s: ++\n", __func__);
+ ret = enterprise_cam_pwr(CAM_REAR_RIGHT, true);
+
+ /* Release Reset */
+ if (is_stereo) {
+ gpio_set_value(CAM1_RST_L_GPIO, 1);
+ gpio_set_value(CAM2_RST_L_GPIO, 1);
+ } else
+ gpio_set_value(CAM1_RST_L_GPIO, 1);
+ /*
+ It takes 2400 EXTCLK for ar0832 to be ready for I2c.
+ EXTCLK is 10 ~ 24MHz. 1 ms should be enough to cover
+ at least 2400 EXTCLK within frequency range.
+ */
+ enterprise_msleep(1);
+
+ return ret;
+}
+
+static int enterprise_ar0832_le_power_on(int is_stereo)
+{
+ int ret = 0;
+
+ pr_info("%s: ++\n", __func__);
+ ret = enterprise_cam_pwr(CAM_REAR_LEFT, true);
+
+ /* Release Reset */
+ gpio_set_value(CAM2_RST_L_GPIO, 1);
+
+ /*
+ It takes 2400 EXTCLK for ar0832 to be ready for I2c.
+ EXTCLK is 10 ~ 24MHz. 1 ms should be enough to cover
+ at least 2400 EXTCLK within frequency range.
+ */
+ enterprise_msleep(1);
+
+ /* CSI B is shared between Front camera and Rear Left camera */
+ gpio_set_value(CAM_CSI_MUX_SEL_GPIO, 1);
+
+ return ret;
+}
+
+static int enterprise_ar0832_ri_power_off(int is_stereo)
+{
+ int ret;
+
+ pr_info("%s: ++\n", __func__);
+ ret = enterprise_cam_pwr(CAM_REAR_RIGHT, false);
+
+ /* Assert Reset */
+ if (is_stereo) {
+ gpio_set_value(CAM1_RST_L_GPIO, 0);
+ gpio_set_value(CAM2_RST_L_GPIO, 0);
+ } else
+ gpio_set_value(CAM1_RST_L_GPIO, 0);
+
+ return ret;
+}
+
+static int enterprise_ar0832_le_power_off(int is_stereo)
+{
+ int ret;
+
+ pr_info("%s: ++\n", __func__);
+ ret = enterprise_cam_pwr(CAM_REAR_LEFT, false);
+
+ /* Assert Reset */
+ gpio_set_value(CAM2_RST_L_GPIO, 0);
+
+ return ret;
+}
+
+static int enterprise_ov9726_power_on(void)
+{
+ pr_info("ov9726 power on\n");
+
+ /* switch mipi mux to front camera */
+ gpio_set_value(CAM_CSI_MUX_SEL_GPIO, CAM_CSI_MUX_SEL_FRONT);
+ enterprise_cam_pwr(CAM_FRONT, true);
+
+ return 0;
+}
+
+static int enterprise_ov9726_power_off(void)
+{
+ pr_info("ov9726 power off\n");
+
+ enterprise_cam_pwr(CAM_FRONT, false);
+
+ return 0;
+}
+
+struct ov9726_platform_data enterprise_ov9726_data = {
+ .power_on = enterprise_ov9726_power_on,
+ .power_off = enterprise_ov9726_power_off,
+ .gpio_rst = CAM3_RST_L_GPIO,
+ .rst_low_active = true,
+ .gpio_pwdn = CAM3_PWDN_GPIO,
+ .pwdn_low_active = false,
+};
+
+static struct nvc_torch_pin_state enterprise_tps61050_pinstate = {
+ .mask = 0x0008, /*VGP3*/
+ .values = 0x0008,
+};
+
+static struct tps61050_platform_data enterprise_tps61050_pdata = {
+ .dev_name = "torch",
+ .pinstate = &enterprise_tps61050_pinstate,
+};
+
+
+struct enterprise_cam_gpio {
+ int gpio;
+ const char *label;
+ int value;
+};
+
+#define TEGRA_CAMERA_GPIO(_gpio, _label, _value) \
+ { \
+ .gpio = _gpio, \
+ .label = _label, \
+ .value = _value, \
+ }
+
+static struct enterprise_cam_gpio enterprise_cam_gpio_data[] = {
+ [0] = TEGRA_CAMERA_GPIO(CAM_CSI_MUX_SEL_GPIO, "cam_csi_sel", 1),
+ [1] = TEGRA_CAMERA_GPIO(CAM1_RST_L_GPIO, "cam1_rst_lo", 0),
+ [2] = TEGRA_CAMERA_GPIO(CAM2_RST_L_GPIO, "cam2_rst_lo", 0),
+ [3] = TEGRA_CAMERA_GPIO(CAM3_RST_L_GPIO, "cam3_rst_lo", 0),
+ [4] = TEGRA_CAMERA_GPIO(CAM3_PWDN_GPIO, "cam3_pwdn", 1),
+ [5] = TEGRA_CAMERA_GPIO(CAM_FLASH_EN_GPIO, "flash_en", 1),
+ [6] = TEGRA_CAMERA_GPIO(CAM_I2C_MUX_RST_EXP, "cam_i2c_mux_rst", 1),
+};
+
+static struct pca954x_platform_mode enterprise_pca954x_modes[] = {
+ { .adap_id = PCA954x_I2C_BUS0, .deselect_on_exit = true, },
+ { .adap_id = PCA954x_I2C_BUS1, .deselect_on_exit = true, },
+ { .adap_id = PCA954x_I2C_BUS2, .deselect_on_exit = true, },
+ { .adap_id = PCA954x_I2C_BUS3, .deselect_on_exit = true, },
+};
+
+static struct pca954x_platform_data enterprise_pca954x_data = {
+ .modes = enterprise_pca954x_modes,
+ .num_modes = ARRAY_SIZE(enterprise_pca954x_modes),
+};
+
+static struct ar0832_platform_data enterprise_ar0832_ri_data = {
+ .power_on = enterprise_ar0832_ri_power_on,
+ .power_off = enterprise_ar0832_ri_power_off,
+ .id = "right",
+};
+
+static struct ar0832_platform_data enterprise_ar0832_le_data = {
+ .power_on = enterprise_ar0832_le_power_on,
+ .power_off = enterprise_ar0832_le_power_off,
+ .id = "left",
+};
+
+static const struct i2c_board_info enterprise_i2c2_boardinfo[] = {
+ {
+ I2C_BOARD_INFO("pca9546", 0x70),
+ .platform_data = &enterprise_pca954x_data,
+ },
+ {
+ I2C_BOARD_INFO("tps61050", 0x33),
+ .platform_data = &enterprise_tps61050_pdata,
+ },
+ {
+ I2C_BOARD_INFO("ov9726", OV9726_I2C_ADDR >> 1),
+ .platform_data = &enterprise_ov9726_data,
+ },
+};
+
+/*
+ * Since ar0832 driver should support multiple devices, slave
+ * address should be changed after it is open. Default slave
+ * address of ar0832 is 0x36. It will be changed to alternate
+ * address defined below when device is open.
+ */
+static struct i2c_board_info ar0832_i2c2_boardinfo[] = {
+ {
+ /* 0x36: alternative slave address */
+ I2C_BOARD_INFO("ar0832", 0x36),
+ .platform_data = &enterprise_ar0832_ri_data,
+ },
+ {
+ /* 0x32: alternative slave address */
+ I2C_BOARD_INFO("ar0832", 0x32),
+ .platform_data = &enterprise_ar0832_le_data,
+ },
+ {
+ I2C_BOARD_INFO("tps61050", 0x33),
+ .platform_data = &enterprise_tps61050_pdata,
+ },
+ {
+ I2C_BOARD_INFO("ov9726", OV9726_I2C_ADDR >> 1),
+ .platform_data = &enterprise_ov9726_data,
+ },
+};
+
+static struct i2c_board_info enterprise_i2c6_boardinfo[] = {
+ {
+ I2C_BOARD_INFO("ar0832", 0x36),
+ .platform_data = &enterprise_ar0832_le_data,
+ },
+};
+
+static struct i2c_board_info enterprise_i2c7_boardinfo[] = {
+ {
+ I2C_BOARD_INFO("ar0832", 0x36),
+ .platform_data = &enterprise_ar0832_ri_data,
+ },
+};
+
+static int enterprise_cam_init(void)
+{
+ int ret;
+ int i;
+ struct board_info bi;
+
+ pr_info("%s:++\n", __func__);
+ memset(ent_vicsi_pwr, 0, sizeof(ent_vicsi_pwr));
+ for (i = 0; i < ARRAY_SIZE(enterprise_cam_gpio_data); i++) {
+ ret = gpio_request(enterprise_cam_gpio_data[i].gpio,
+ enterprise_cam_gpio_data[i].label);
+ if (ret < 0) {
+ pr_err("%s: gpio_request failed for gpio #%d\n",
+ __func__, i);
+ goto fail_free_gpio;
+ }
+ gpio_direction_output(enterprise_cam_gpio_data[i].gpio,
+ enterprise_cam_gpio_data[i].value);
+ gpio_export(enterprise_cam_gpio_data[i].gpio, false);
+ tegra_gpio_enable(enterprise_cam_gpio_data[i].gpio);
+ }
+
+ tegra_get_board_info(&bi);
+
+ if (bi.fab == BOARD_FAB_A00 || bi.fab == BOARD_FAB_A01)
+ i2c_register_board_info(2, ar0832_i2c2_boardinfo,
+ ARRAY_SIZE(ar0832_i2c2_boardinfo));
+ else if (bi.fab == BOARD_FAB_A02) {
+ i2c_register_board_info(2, enterprise_i2c2_boardinfo,
+ ARRAY_SIZE(enterprise_i2c2_boardinfo));
+ /*
+ * Right camera is on PCA954x's I2C BUS1,
+ * Left camera is on BUS0
+ */
+ i2c_register_board_info(PCA954x_I2C_BUS0, enterprise_i2c6_boardinfo,
+ ARRAY_SIZE(enterprise_i2c6_boardinfo));
+ i2c_register_board_info(PCA954x_I2C_BUS1, enterprise_i2c7_boardinfo,
+ ARRAY_SIZE(enterprise_i2c7_boardinfo));
+ }
+
+ return 0;
+
+fail_free_gpio:
+ pr_err("%s enterprise_cam_init failed!\n", __func__);
+ while (i--)
+ gpio_free(enterprise_cam_gpio_data[i].gpio);
+ return ret;
+}
+
+int __init enterprise_sensors_init(void)
+{
+ int ret;
+
+ enterprise_isl_init();
+ enterprise_nct1008_init();
+ mpuirq_init();
+ ret = enterprise_cam_init();
+
+ return ret;
+}
+
diff --git a/arch/arm/mach-tegra/board-enterprise.c b/arch/arm/mach-tegra/board-enterprise.c
new file mode 100644
index 000000000000..5ef53a83fdd3
--- /dev/null
+++ b/arch/arm/mach-tegra/board-enterprise.c
@@ -0,0 +1,891 @@
+/*
+ * arch/arm/mach-tegra/board-enterprise.c
+ *
+ * Copyright (c) 2011, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/serial_8250.h>
+#include <linux/i2c.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/i2c-tegra.h>
+#include <linux/gpio.h>
+#include <linux/input.h>
+#include <linux/platform_data/tegra_usb.h>
+#include <linux/spi/spi.h>
+#include <linux/tegra_uart.h>
+#include <linux/fsl_devices.h>
+#include <linux/i2c/atmel_mxt_ts.h>
+#include <linux/memblock.h>
+
+#include <linux/nfc/pn544.h>
+#include <sound/max98088.h>
+
+#include <mach/clk.h>
+#include <mach/iomap.h>
+#include <mach/irqs.h>
+#include <mach/pinmux.h>
+#include <mach/iomap.h>
+#include <mach/io.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <mach/usb_phy.h>
+#include <mach/i2s.h>
+#include <mach/tegra_max98088_pdata.h>
+#include <mach/thermal.h>
+
+#include "board.h"
+#include "clock.h"
+#include "board-enterprise.h"
+#include "baseband-xmm-power.h"
+#include "devices.h"
+#include "gpio-names.h"
+#include "fuse.h"
+#include "pm.h"
+
+/* All units are in millicelsius */
+static struct tegra_thermal_data thermal_data = {
+ .temp_throttle = 85000,
+ .temp_shutdown = 90000,
+ .temp_offset = TDIODE_OFFSET, /* temps based on tdiode */
+#ifdef CONFIG_TEGRA_EDP_LIMITS
+ .edp_offset = TDIODE_OFFSET, /* edp based on tdiode */
+ .hysteresis_edp = 3000,
+#endif
+#ifdef CONFIG_TEGRA_THERMAL_SYSFS
+ .tc1 = 0,
+ .tc2 = 1,
+ .passive_delay = 2000,
+#else
+ .hysteresis_throttle = 1000,
+#endif
+};
+
+/* !!!TODO: Change for enterprise (Taken from Cardhu) */
+static struct tegra_utmip_config utmi_phy_config[] = {
+ [0] = {
+ .hssync_start_delay = 0,
+ .idle_wait_delay = 17,
+ .elastic_limit = 16,
+ .term_range_adj = 6,
+ .xcvr_setup = 15,
+ .xcvr_setup_offset = 0,
+ .xcvr_use_fuses = 1,
+ .xcvr_lsfslew = 2,
+ .xcvr_lsrslew = 2,
+ },
+ [1] = {
+ .hssync_start_delay = 0,
+ .idle_wait_delay = 17,
+ .elastic_limit = 16,
+ .term_range_adj = 6,
+ .xcvr_setup = 15,
+ .xcvr_setup_offset = 0,
+ .xcvr_use_fuses = 1,
+ .xcvr_lsfslew = 2,
+ .xcvr_lsrslew = 2,
+ },
+ [2] = {
+ .hssync_start_delay = 0,
+ .idle_wait_delay = 17,
+ .elastic_limit = 16,
+ .term_range_adj = 6,
+ .xcvr_setup = 8,
+ .xcvr_setup_offset = 0,
+ .xcvr_use_fuses = 1,
+ .xcvr_lsfslew = 2,
+ .xcvr_lsrslew = 2,
+ },
+};
+
+static struct resource enterprise_bcm4329_rfkill_resources[] = {
+ {
+ .name = "bcm4329_nshutdown_gpio",
+ .start = TEGRA_GPIO_PE6,
+ .end = TEGRA_GPIO_PE6,
+ .flags = IORESOURCE_IO,
+ },
+};
+
+static struct platform_device enterprise_bcm4329_rfkill_device = {
+ .name = "bcm4329_rfkill",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(enterprise_bcm4329_rfkill_resources),
+ .resource = enterprise_bcm4329_rfkill_resources,
+};
+
+static struct resource enterprise_bluesleep_resources[] = {
+ [0] = {
+ .name = "gpio_host_wake",
+ .start = TEGRA_GPIO_PS2,
+ .end = TEGRA_GPIO_PS2,
+ .flags = IORESOURCE_IO,
+ },
+ [1] = {
+ .name = "gpio_ext_wake",
+ .start = TEGRA_GPIO_PE7,
+ .end = TEGRA_GPIO_PE7,
+ .flags = IORESOURCE_IO,
+ },
+ [2] = {
+ .name = "host_wake",
+ .start = TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PS2),
+ .end = TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PS2),
+ .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE,
+ },
+};
+
+static struct platform_device enterprise_bluesleep_device = {
+ .name = "bluesleep",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(enterprise_bluesleep_resources),
+ .resource = enterprise_bluesleep_resources,
+};
+
+static void __init enterprise_setup_bluesleep(void)
+{
+ platform_device_register(&enterprise_bluesleep_device);
+ tegra_gpio_enable(TEGRA_GPIO_PS2);
+ tegra_gpio_enable(TEGRA_GPIO_PE7);
+ return;
+}
+
+static __initdata struct tegra_clk_init_table enterprise_clk_init_table[] = {
+ /* name parent rate enabled */
+ { "pll_m", NULL, 0, false},
+ { "hda", "pll_p", 108000000, false},
+ { "hda2codec_2x","pll_p", 48000000, false},
+ { "pwm", "clk_32k", 32768, false},
+ { "blink", "clk_32k", 32768, true},
+ { "pll_a", NULL, 564480000, false},
+ { "pll_a_out0", NULL, 11289600, false},
+ { "i2s0", "pll_a_out0", 0, false},
+ { "i2s2", "pll_a_out0", 0, false},
+ { "i2s3", "pll_a_out0", 0, false},
+ { "spdif_out", "pll_a_out0", 0, false},
+ { "d_audio", "pll_a_out0", 0, false},
+ { "dam0", "pll_a_out0", 0, false},
+ { "dam1", "pll_a_out0", 0, false},
+ { "dam2", "pll_a_out0", 0, false},
+ { NULL, NULL, 0, 0},
+};
+
+static struct tegra_i2c_platform_data enterprise_i2c1_platform_data = {
+ .adapter_nr = 0,
+ .bus_count = 1,
+ .bus_clk_rate = { 100000, 0 },
+ .scl_gpio = {TEGRA_GPIO_PC4, 0},
+ .sda_gpio = {TEGRA_GPIO_PC5, 0},
+ .arb_recovery = arb_lost_recovery,
+};
+
+static struct tegra_i2c_platform_data enterprise_i2c2_platform_data = {
+ .adapter_nr = 1,
+ .bus_count = 1,
+ .bus_clk_rate = { 100000, 0 },
+ .is_clkon_always = true,
+ .scl_gpio = {TEGRA_GPIO_PT5, 0},
+ .sda_gpio = {TEGRA_GPIO_PT6, 0},
+ .arb_recovery = arb_lost_recovery,
+};
+
+static struct tegra_i2c_platform_data enterprise_i2c3_platform_data = {
+ .adapter_nr = 2,
+ .bus_count = 1,
+ .bus_clk_rate = { 100000, 0 },
+ .scl_gpio = {TEGRA_GPIO_PBB1, 0},
+ .sda_gpio = {TEGRA_GPIO_PBB2, 0},
+ .arb_recovery = arb_lost_recovery,
+};
+
+static struct tegra_i2c_platform_data enterprise_i2c4_platform_data = {
+ .adapter_nr = 3,
+ .bus_count = 1,
+ .bus_clk_rate = { 100000, 0 },
+ .scl_gpio = {TEGRA_GPIO_PV4, 0},
+ .sda_gpio = {TEGRA_GPIO_PV5, 0},
+ .arb_recovery = arb_lost_recovery,
+};
+
+static struct tegra_i2c_platform_data enterprise_i2c5_platform_data = {
+ .adapter_nr = 4,
+ .bus_count = 1,
+ .bus_clk_rate = { 100000, 0 },
+ .scl_gpio = {TEGRA_GPIO_PZ6, 0},
+ .sda_gpio = {TEGRA_GPIO_PZ7, 0},
+ .arb_recovery = arb_lost_recovery,
+};
+
+/* Equalizer filter coefs generated from the MAXIM MAX98088
+ * evkit software tool */
+static struct max98088_eq_cfg max98088_eq_cfg[] = {
+ {
+ .name = "FLAT",
+ .rate = 44100,
+ .band1 = {0x2000, 0xC002, 0x4000, 0x00E9, 0x0000},
+ .band2 = {0x2000, 0xC00F, 0x4000, 0x02BC, 0x0000},
+ .band3 = {0x2000, 0xC0A7, 0x4000, 0x0916, 0x0000},
+ .band4 = {0x2000, 0xC5C2, 0x4000, 0x1A87, 0x0000},
+ .band5 = {0x2000, 0xF6B0, 0x4000, 0x3F51, 0x0000},
+ },
+ {
+ .name = "LOWPASS1K",
+ .rate = 44100,
+ .band1 = {0x205D, 0xC001, 0x3FEF, 0x002E, 0x02E0},
+ .band2 = {0x5B9A, 0xC093, 0x3AB2, 0x088B, 0x1981},
+ .band3 = {0x0D22, 0xC170, 0x26EA, 0x0D79, 0x32CF},
+ .band4 = {0x0894, 0xC612, 0x01B3, 0x1B34, 0x3FFA},
+ .band5 = {0x0815, 0x3FFF, 0xCF78, 0x0000, 0x29B7},
+ },
+ { /* BASS=-12dB, TREBLE=+9dB, Fc=5KHz */
+ .name = "HIBOOST",
+ .rate = 44100,
+ .band1 = {0x0815, 0xC001, 0x3AA4, 0x0003, 0x19A2},
+ .band2 = {0x0815, 0xC103, 0x092F, 0x0B55, 0x3F56},
+ .band3 = {0x0E0A, 0xC306, 0x1E5C, 0x136E, 0x3856},
+ .band4 = {0x2459, 0xF665, 0x0CAA, 0x3F46, 0x3EBB},
+ .band5 = {0x5BBB, 0x3FFF, 0xCEB0, 0x0000, 0x28CA},
+ },
+ { /* BASS=12dB, TREBLE=+12dB */
+ .name = "LOUD12DB",
+ .rate = 44100,
+ .band1 = {0x7FC1, 0xC001, 0x3EE8, 0x0020, 0x0BC7},
+ .band2 = {0x51E9, 0xC016, 0x3C7C, 0x033F, 0x14E9},
+ .band3 = {0x1745, 0xC12C, 0x1680, 0x0C2F, 0x3BE9},
+ .band4 = {0x4536, 0xD7E2, 0x0ED4, 0x31DD, 0x3E42},
+ .band5 = {0x7FEF, 0x3FFF, 0x0BAB, 0x0000, 0x3EED},
+ },
+ {
+ .name = "FLAT",
+ .rate = 16000,
+ .band1 = {0x2000, 0xC004, 0x4000, 0x0141, 0x0000},
+ .band2 = {0x2000, 0xC033, 0x4000, 0x0505, 0x0000},
+ .band3 = {0x2000, 0xC268, 0x4000, 0x115F, 0x0000},
+ .band4 = {0x2000, 0xDA62, 0x4000, 0x33C6, 0x0000},
+ .band5 = {0x2000, 0x4000, 0x4000, 0x0000, 0x0000},
+ },
+ {
+ .name = "LOWPASS1K",
+ .rate = 16000,
+ .band1 = {0x2000, 0xC004, 0x4000, 0x0141, 0x0000},
+ .band2 = {0x5BE8, 0xC3E0, 0x3307, 0x15ED, 0x26A0},
+ .band3 = {0x0F71, 0xD15A, 0x08B3, 0x2BD0, 0x3F67},
+ .band4 = {0x0815, 0x3FFF, 0xCF78, 0x0000, 0x29B7},
+ .band5 = {0x0815, 0x3FFF, 0xCF78, 0x0000, 0x29B7},
+ },
+ { /* BASS=-12dB, TREBLE=+9dB, Fc=2KHz */
+ .name = "HIBOOST",
+ .rate = 16000,
+ .band1 = {0x0815, 0xC001, 0x3BD2, 0x0009, 0x16BF},
+ .band2 = {0x080E, 0xC17E, 0xF653, 0x0DBD, 0x3F43},
+ .band3 = {0x0F80, 0xDF45, 0xEE33, 0x36FE, 0x3D79},
+ .band4 = {0x590B, 0x3FF0, 0xE882, 0x02BD, 0x3B87},
+ .band5 = {0x4C87, 0xF3D0, 0x063F, 0x3ED4, 0x3FB1},
+ },
+ { /* BASS=12dB, TREBLE=+12dB */
+ .name = "LOUD12DB",
+ .rate = 16000,
+ .band1 = {0x7FC1, 0xC001, 0x3D07, 0x0058, 0x1344},
+ .band2 = {0x2DA6, 0xC013, 0x3CF1, 0x02FF, 0x138B},
+ .band3 = {0x18F1, 0xC08E, 0x244D, 0x0863, 0x34B5},
+ .band4 = {0x2BE0, 0xF385, 0x04FD, 0x3EC5, 0x3FCE},
+ .band5 = {0x7FEF, 0x4000, 0x0BAB, 0x0000, 0x3EED},
+ },
+};
+
+
+static struct max98088_pdata enterprise_max98088_pdata = {
+ /* equalizer configuration */
+ .eq_cfg = max98088_eq_cfg,
+ .eq_cfgcnt = ARRAY_SIZE(max98088_eq_cfg),
+
+ /* debounce time */
+ .debounce_time_ms = 200,
+
+ /* microphone configuration */
+ .digmic_left_mode = 1,
+ .digmic_right_mode = 1,
+
+ /* receiver output configuration */
+ .receiver_mode = 0, /* 0 = amplifier, 1 = line output */
+};
+
+static struct pn544_i2c_platform_data nfc_pdata = {
+ .irq_gpio = TEGRA_GPIO_PS4,
+ .ven_gpio = TEGRA_GPIO_PM6,
+ .firm_gpio = 0,
+};
+
+
+static struct i2c_board_info __initdata max98088_board_info = {
+ I2C_BOARD_INFO("max98088", 0x10),
+ .platform_data = &enterprise_max98088_pdata,
+ .irq = TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_HP_DET),
+};
+
+static struct i2c_board_info __initdata nfc_board_info = {
+ I2C_BOARD_INFO("pn544", 0x28),
+ .platform_data = &nfc_pdata,
+ .irq = TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PS4),
+};
+
+static void enterprise_i2c_init(void)
+{
+ tegra_i2c_device1.dev.platform_data = &enterprise_i2c1_platform_data;
+ tegra_i2c_device2.dev.platform_data = &enterprise_i2c2_platform_data;
+ tegra_i2c_device3.dev.platform_data = &enterprise_i2c3_platform_data;
+ tegra_i2c_device4.dev.platform_data = &enterprise_i2c4_platform_data;
+ tegra_i2c_device5.dev.platform_data = &enterprise_i2c5_platform_data;
+
+ platform_device_register(&tegra_i2c_device5);
+ platform_device_register(&tegra_i2c_device4);
+ platform_device_register(&tegra_i2c_device3);
+ platform_device_register(&tegra_i2c_device2);
+ platform_device_register(&tegra_i2c_device1);
+
+ i2c_register_board_info(0, &max98088_board_info, 1);
+ i2c_register_board_info(0, &nfc_board_info, 1);
+}
+
+static struct platform_device *enterprise_uart_devices[] __initdata = {
+ &tegra_uarta_device,
+ &tegra_uartb_device,
+ &tegra_uartc_device,
+ &tegra_uartd_device,
+ &tegra_uarte_device,
+};
+
+static struct uart_clk_parent uart_parent_clk[] = {
+ [0] = {.name = "clk_m"},
+ [1] = {.name = "pll_p"},
+#ifndef CONFIG_TEGRA_PLLM_RESTRICTED
+ [2] = {.name = "pll_m"},
+#endif
+};
+static struct tegra_uart_platform_data enterprise_uart_pdata;
+
+static void __init uart_debug_init(void)
+{
+ unsigned long rate;
+ struct clk *c;
+
+ /* UARTD is the debug port. */
+ pr_info("Selecting UARTD as the debug console\n");
+ enterprise_uart_devices[3] = &debug_uartd_device;
+ debug_uart_port_base = ((struct plat_serial8250_port *)(
+ debug_uartd_device.dev.platform_data))->mapbase;
+ debug_uart_clk = clk_get_sys("serial8250.0", "uartd");
+
+ /* Clock enable for the debug channel */
+ if (!IS_ERR_OR_NULL(debug_uart_clk)) {
+ rate = ((struct plat_serial8250_port *)(
+ debug_uartd_device.dev.platform_data))->uartclk;
+ pr_info("The debug console clock name is %s\n",
+ debug_uart_clk->name);
+ c = tegra_get_clock_by_name("pll_p");
+ if (IS_ERR_OR_NULL(c))
+ pr_err("Not getting the parent clock pll_p\n");
+ else
+ clk_set_parent(debug_uart_clk, c);
+
+ clk_enable(debug_uart_clk);
+ clk_set_rate(debug_uart_clk, rate);
+ } else {
+ pr_err("Not getting the clock %s for debug console\n",
+ debug_uart_clk->name);
+ }
+}
+
+static void __init enterprise_uart_init(void)
+{
+ int i;
+ struct clk *c;
+
+ for (i = 0; i < ARRAY_SIZE(uart_parent_clk); ++i) {
+ c = tegra_get_clock_by_name(uart_parent_clk[i].name);
+ if (IS_ERR_OR_NULL(c)) {
+ pr_err("Not able to get the clock for %s\n",
+ uart_parent_clk[i].name);
+ continue;
+ }
+ uart_parent_clk[i].parent_clk = c;
+ uart_parent_clk[i].fixed_clk_rate = clk_get_rate(c);
+ }
+ enterprise_uart_pdata.parent_clk_list = uart_parent_clk;
+ enterprise_uart_pdata.parent_clk_count = ARRAY_SIZE(uart_parent_clk);
+ tegra_uarta_device.dev.platform_data = &enterprise_uart_pdata;
+ tegra_uartb_device.dev.platform_data = &enterprise_uart_pdata;
+ tegra_uartc_device.dev.platform_data = &enterprise_uart_pdata;
+ tegra_uartd_device.dev.platform_data = &enterprise_uart_pdata;
+ tegra_uarte_device.dev.platform_data = &enterprise_uart_pdata;
+
+ /* Register low speed only if it is selected */
+ if (!is_tegra_debug_uartport_hs())
+ uart_debug_init();
+
+ platform_add_devices(enterprise_uart_devices,
+ ARRAY_SIZE(enterprise_uart_devices));
+}
+
+
+
+static struct resource tegra_rtc_resources[] = {
+ [0] = {
+ .start = TEGRA_RTC_BASE,
+ .end = TEGRA_RTC_BASE + TEGRA_RTC_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = INT_RTC,
+ .end = INT_RTC,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device tegra_rtc_device = {
+ .name = "tegra_rtc",
+ .id = -1,
+ .resource = tegra_rtc_resources,
+ .num_resources = ARRAY_SIZE(tegra_rtc_resources),
+};
+
+static struct platform_device tegra_camera = {
+ .name = "tegra_camera",
+ .id = -1,
+};
+
+static struct tegra_max98088_platform_data enterprise_audio_pdata = {
+ .gpio_spkr_en = -1,
+ .gpio_hp_det = TEGRA_GPIO_HP_DET,
+ .gpio_hp_mute = -1,
+ .gpio_int_mic_en = -1,
+ .gpio_ext_mic_en = -1,
+ .audio_port_id = {
+ [HIFI_CODEC] = 0,
+ [BASEBAND] = 2,
+ [BT_SCO] = 3,
+ },
+ .baseband_param = {
+ .rate = 8000,
+ .channels = 1,
+ },
+};
+
+static struct platform_device enterprise_audio_device = {
+ .name = "tegra-snd-max98088",
+ .id = 0,
+ .dev = {
+ .platform_data = &enterprise_audio_pdata,
+ },
+};
+
+static struct resource ram_console_resources[] = {
+ {
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct platform_device ram_console_device = {
+ .name = "ram_console",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(ram_console_resources),
+ .resource = ram_console_resources,
+};
+
+static struct platform_device *enterprise_devices[] __initdata = {
+ &tegra_pmu_device,
+ &tegra_rtc_device,
+ &tegra_udc_device,
+#if defined(CONFIG_TEGRA_IOVMM_SMMU)
+ &tegra_smmu_device,
+#endif
+ &tegra_wdt_device,
+#if defined(CONFIG_TEGRA_AVP)
+ &tegra_avp_device,
+#endif
+ &tegra_camera,
+ &tegra_ahub_device,
+ &tegra_dam_device0,
+ &tegra_dam_device1,
+ &tegra_dam_device2,
+ &tegra_i2s_device0,
+ &tegra_i2s_device2,
+ &tegra_i2s_device3,
+ &tegra_spdif_device,
+ &spdif_dit_device,
+ &bluetooth_dit_device,
+ &enterprise_bcm4329_rfkill_device,
+ &baseband_dit_device,
+ &tegra_pcm_device,
+ &enterprise_audio_device,
+ &tegra_spi_device4,
+ &tegra_hda_device,
+#if defined(CONFIG_CRYPTO_DEV_TEGRA_SE)
+ &tegra_se_device,
+#endif
+#if defined(CONFIG_CRYPTO_DEV_TEGRA_AES)
+ &tegra_aes_device,
+#endif
+ &ram_console_device,
+};
+
+#define MXT_CONFIG_CRC 0x62F903
+/*
+ * Config converted from memory-mapped cfg-file with
+ * following version information:
+ *
+ *
+ *
+ * FAMILY_ID=128
+ * VARIANT=1
+ * VERSION=32
+ * BUILD=170
+ * VENDOR_ID=255
+ * PRODUCT_ID=TBD
+ * CHECKSUM=0xC189B6
+ *
+ *
+ */
+
+static const u8 config[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xFF, 0xFF, 0x32, 0x0A, 0x00, 0x05, 0x01, 0x00,
+ 0x00, 0x1E, 0x0A, 0x8B, 0x00, 0x00, 0x13, 0x0B,
+ 0x00, 0x10, 0x32, 0x03, 0x03, 0x00, 0x03, 0x01,
+ 0x00, 0x0A, 0x0A, 0x0A, 0x0A, 0xBF, 0x03, 0x1B,
+ 0x02, 0x00, 0x00, 0x37, 0x37, 0x00, 0x00, 0x00,
+ 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xA9, 0x7F, 0x9A, 0x0E, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x03, 0x23, 0x00, 0x00, 0x00, 0x0A,
+ 0x0F, 0x14, 0x19, 0x03, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x01, 0x00, 0x03, 0x08, 0x10,
+ 0x00
+};
+
+static struct mxt_platform_data atmel_mxt_info = {
+ .x_line = 19,
+ .y_line = 11,
+ .x_size = 960,
+ .y_size = 540,
+ .blen = 0x10,
+ .threshold = 0x32,
+ .voltage = 3300000, /* 3.3V */
+ .orient = 3,
+ .config = config,
+ .config_length = 168,
+ .config_crc = MXT_CONFIG_CRC,
+ .irqflags = IRQF_TRIGGER_FALLING,
+/* .read_chg = &read_chg, */
+ .read_chg = NULL,
+};
+
+static struct i2c_board_info __initdata atmel_i2c_info[] = {
+ {
+ I2C_BOARD_INFO("atmel_mxt_ts", MXT224_I2C_ADDR1),
+ .irq = TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PH6),
+ .platform_data = &atmel_mxt_info,
+ }
+};
+
+static int __init enterprise_touch_init(void)
+{
+ tegra_gpio_enable(TEGRA_GPIO_PH6);
+ tegra_gpio_enable(TEGRA_GPIO_PF5);
+
+ gpio_request(TEGRA_GPIO_PH6, "atmel-irq");
+ gpio_direction_input(TEGRA_GPIO_PH6);
+
+ gpio_request(TEGRA_GPIO_PF5, "atmel-reset");
+ gpio_direction_output(TEGRA_GPIO_PF5, 0);
+ msleep(1);
+ gpio_set_value(TEGRA_GPIO_PF5, 1);
+ msleep(100);
+
+ i2c_register_board_info(1, atmel_i2c_info, 1);
+
+ return 0;
+}
+
+static struct usb_phy_plat_data tegra_usb_phy_pdata[] = {
+ [0] = {
+ .instance = 0,
+ .vbus_gpio = -1,
+ .vbus_reg_supply = "usb_vbus",
+ .vbus_irq = ENT_TPS80031_IRQ_BASE +
+ TPS80031_INT_VBUS_DET,
+ },
+ [1] = {
+ .instance = 1,
+ .vbus_gpio = -1,
+ },
+ [2] = {
+ .instance = 2,
+ .vbus_gpio = -1,
+ },
+};
+
+static struct tegra_uhsic_config uhsic_phy_config = {
+ .enable_gpio = -1,
+ .reset_gpio = -1,
+ .sync_start_delay = 9,
+ .idle_wait_delay = 17,
+ .term_range_adj = 0,
+ .elastic_underrun_limit = 16,
+ .elastic_overrun_limit = 16,
+};
+
+static struct tegra_ehci_platform_data tegra_ehci_uhsic_pdata = {
+ .phy_type = TEGRA_USB_PHY_TYPE_HSIC,
+ .phy_config = &uhsic_phy_config,
+ .operating_mode = TEGRA_USB_HOST,
+ .power_down_on_bus_suspend = 1,
+};
+
+static struct tegra_ehci_platform_data tegra_ehci_pdata[] = {
+ [0] = {
+ .phy_config = &utmi_phy_config[0],
+ .operating_mode = TEGRA_USB_HOST,
+ .power_down_on_bus_suspend = 1,
+ },
+ [1] = {
+ .phy_config = &utmi_phy_config[1],
+ .operating_mode = TEGRA_USB_HOST,
+ .power_down_on_bus_suspend = 1,
+ },
+ [2] = {
+ .phy_config = &utmi_phy_config[2],
+ .operating_mode = TEGRA_USB_HOST,
+ .power_down_on_bus_suspend = 1,
+ },
+};
+
+static struct tegra_otg_platform_data tegra_otg_pdata = {
+ .ehci_device = &tegra_ehci1_device,
+ .ehci_pdata = &tegra_ehci_pdata[0],
+};
+
+static int enterprise_usb_hsic_postsupend(void)
+{
+ pr_debug("%s\n", __func__);
+#ifdef CONFIG_TEGRA_BB_XMM_POWER
+ baseband_xmm_set_power_status(BBXMM_PS_L2);
+#endif
+ return 0;
+}
+
+static int enterprise_usb_hsic_preresume(void)
+{
+ pr_debug("%s\n", __func__);
+#ifdef CONFIG_TEGRA_BB_XMM_POWER
+ baseband_xmm_set_power_status(BBXMM_PS_L2TOL0);
+#endif
+ return 0;
+}
+
+static int enterprise_usb_hsic_phy_ready(void)
+{
+ pr_debug("%s\n", __func__);
+#ifdef CONFIG_TEGRA_BB_XMM_POWER
+ baseband_xmm_set_power_status(BBXMM_PS_L0);
+#endif
+ return 0;
+}
+
+static int enterprise_usb_hsic_phy_off(void)
+{
+ pr_debug("%s\n", __func__);
+#ifdef CONFIG_TEGRA_BB_XMM_POWER
+ baseband_xmm_set_power_status(BBXMM_PS_L3);
+#endif
+ return 0;
+}
+
+static void enterprise_usb_init(void)
+{
+ struct fsl_usb2_platform_data *udc_pdata;
+
+ tegra_usb_phy_init(tegra_usb_phy_pdata, ARRAY_SIZE(tegra_usb_phy_pdata));
+
+ tegra_otg_device.dev.platform_data = &tegra_otg_pdata;
+ platform_device_register(&tegra_otg_device);
+
+ udc_pdata = tegra_udc_device.dev.platform_data;
+}
+
+static void enterprise_gps_init(void)
+{
+ tegra_gpio_enable(TEGRA_GPIO_PE4);
+ tegra_gpio_enable(TEGRA_GPIO_PE5);
+}
+
+static struct baseband_power_platform_data tegra_baseband_power_data = {
+ .baseband_type = BASEBAND_XMM,
+ .modem = {
+ .xmm = {
+ .bb_rst = XMM_GPIO_BB_RST,
+ .bb_on = XMM_GPIO_BB_ON,
+ .ipc_bb_wake = XMM_GPIO_IPC_BB_WAKE,
+ .ipc_ap_wake = XMM_GPIO_IPC_AP_WAKE,
+ .ipc_hsic_active = XMM_GPIO_IPC_HSIC_ACTIVE,
+ .ipc_hsic_sus_req = XMM_GPIO_IPC_HSIC_SUS_REQ,
+ .hsic_device = &tegra_ehci2_device,
+ },
+ },
+};
+
+static struct platform_device tegra_baseband_power_device = {
+ .name = "baseband_xmm_power",
+ .id = -1,
+ .dev = {
+ .platform_data = &tegra_baseband_power_data,
+ },
+};
+
+static struct platform_device tegra_baseband_power2_device = {
+ .name = "baseband_xmm_power2",
+ .id = -1,
+ .dev = {
+ .platform_data = &tegra_baseband_power_data,
+ },
+};
+
+static void enterprise_baseband_init(void)
+{
+ int modem_id = tegra_get_modem_id();
+
+ switch (modem_id) {
+ case 1: /* PH450 ULPI */
+ enterprise_modem_init();
+ break;
+ case 2: /* XMM6260 HSIC */
+ /* xmm baseband - do not switch off phy during suspend */
+ tegra_ehci_uhsic_pdata.power_down_on_bus_suspend = 0;
+ uhsic_phy_config.postsuspend = enterprise_usb_hsic_postsupend;
+ uhsic_phy_config.preresume = enterprise_usb_hsic_preresume;
+ uhsic_phy_config.usb_phy_ready = enterprise_usb_hsic_phy_ready;
+ uhsic_phy_config.post_phy_off = enterprise_usb_hsic_phy_off;
+ /* baseband-power.ko will register ehci2 device */
+ tegra_ehci2_device.dev.platform_data
+ = &tegra_ehci_uhsic_pdata;
+ /* enable XMM6260 baseband gpio(s) */
+ tegra_gpio_enable(tegra_baseband_power_data.modem.generic
+ .mdm_reset);
+ tegra_gpio_enable(tegra_baseband_power_data.modem.generic
+ .mdm_on);
+ tegra_gpio_enable(tegra_baseband_power_data.modem.generic
+ .ap2mdm_ack);
+ tegra_gpio_enable(tegra_baseband_power_data.modem.generic
+ .mdm2ap_ack);
+ tegra_gpio_enable(tegra_baseband_power_data.modem.generic
+ .ap2mdm_ack2);
+ tegra_gpio_enable(tegra_baseband_power_data.modem.generic
+ .mdm2ap_ack2);
+ platform_device_register(&tegra_baseband_power_device);
+ platform_device_register(&tegra_baseband_power2_device);
+ break;
+ }
+}
+
+static void enterprise_nfc_init(void)
+{
+ tegra_gpio_enable(TEGRA_GPIO_PS4);
+ tegra_gpio_enable(TEGRA_GPIO_PM6);
+}
+
+static void __init tegra_enterprise_init(void)
+{
+ tegra_thermal_init(&thermal_data);
+ tegra_clk_init_from_table(enterprise_clk_init_table);
+ enterprise_pinmux_init();
+ enterprise_i2c_init();
+ enterprise_uart_init();
+ enterprise_usb_init();
+ enterprise_tsensor_init();
+ platform_add_devices(enterprise_devices, ARRAY_SIZE(enterprise_devices));
+ enterprise_regulator_init();
+ enterprise_sdhci_init();
+#ifdef CONFIG_TEGRA_EDP_LIMITS
+ enterprise_edp_init();
+#endif
+ enterprise_kbc_init();
+ enterprise_touch_init();
+ enterprise_gps_init();
+ enterprise_baseband_init();
+ enterprise_panel_init();
+ enterprise_setup_bluesleep();
+ enterprise_emc_init();
+ enterprise_sensors_init();
+ enterprise_suspend_init();
+ tegra_release_bootloader_fb();
+ enterprise_nfc_init();
+}
+
+static void __init tegra_enterprise_ramconsole_reserve(unsigned long size)
+{
+ struct resource *res;
+ long ret;
+
+ res = platform_get_resource(&ram_console_device, IORESOURCE_MEM, 0);
+ if (!res) {
+ pr_err("Failed to find memory resource for ram console\n");
+ return;
+ }
+ res->start = memblock_end_of_DRAM() - size;
+ res->end = res->start + size - 1;
+ ret = memblock_remove(res->start, size);
+ if (ret) {
+ ram_console_device.resource = NULL;
+ ram_console_device.num_resources = 0;
+ pr_err("Failed to reserve memory block for ram console\n");
+ }
+}
+
+static void __init tegra_enterprise_reserve(void)
+{
+#if defined(CONFIG_NVMAP_CONVERT_CARVEOUT_TO_IOVMM)
+ tegra_reserve(0, SZ_4M, SZ_8M);
+#else
+ tegra_reserve(SZ_128M, SZ_4M, SZ_8M);
+#endif
+ tegra_enterprise_ramconsole_reserve(SZ_1M);
+}
+
+MACHINE_START(TEGRA_ENTERPRISE, "tegra_enterprise")
+ .boot_params = 0x80000100,
+ .map_io = tegra_map_common_io,
+ .reserve = tegra_enterprise_reserve,
+ .init_early = tegra_init_early,
+ .init_irq = tegra_init_irq,
+ .timer = &tegra_timer,
+ .init_machine = tegra_enterprise_init,
+MACHINE_END
diff --git a/arch/arm/mach-tegra/board-enterprise.h b/arch/arm/mach-tegra/board-enterprise.h
new file mode 100644
index 000000000000..80fad492834b
--- /dev/null
+++ b/arch/arm/mach-tegra/board-enterprise.h
@@ -0,0 +1,131 @@
+/*
+ * arch/arm/mach-tegra/board-enterprise.h
+ *
+ * Copyright (c) 2011, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _MACH_TEGRA_BOARD_ENTERPRISE_H
+#define _MACH_TEGRA_BOARD_ENTERPRISE_H
+
+#include <mach/gpio.h>
+#include <mach/irqs.h>
+#include <linux/mfd/tps80031.h>
+
+/* Processor Board ID */
+#define BOARD_E1205 0x0C05
+#define BOARD_E1197 0x0B61
+#define SKU_BATTERY_SUPPORT 0x1
+
+/* Board Fab version */
+#define BOARD_FAB_A00 0x0
+#define BOARD_FAB_A01 0x1
+#define BOARD_FAB_A02 0x2
+
+/* vdd_cpu voltage follower */
+#define BOARD_SKU_VF_BIT 0x0400
+
+int enterprise_charge_init(void);
+int enterprise_sdhci_init(void);
+int enterprise_pinmux_init(void);
+int enterprise_panel_init(void);
+int enterprise_sensors_init(void);
+int touch_init(void);
+int enterprise_kbc_init(void);
+int enterprise_emc_init(void);
+int enterprise_regulator_init(void);
+int enterprise_modem_init(void);
+int enterprise_suspend_init(void);
+int enterprise_edp_init(void);
+void __init enterprise_tsensor_init(void);
+
+/* Invensense MPU Definitions */
+#define MPU_GYRO_NAME "mpu3050"
+#define MPU_GYRO_IRQ_GPIO TEGRA_GPIO_PH4
+#define MPU_GYRO_ADDR 0x68
+#define MPU_GYRO_BUS_NUM 0
+#define MPU_GYRO_ORIENTATION { -1, 0, 0, 0, -1, 0, 0, 0, 1 }
+#define MPU_ACCEL_NAME "kxtf9"
+#define MPU_ACCEL_IRQ_GPIO 0 /* DISABLE ACCELIRQ: TEGRA_GPIO_PJ2 */
+#define MPU_ACCEL_ADDR 0x0F
+#define MPU_ACCEL_BUS_NUM 0
+#define MPU_ACCEL_ORIENTATION { 0, 1, 0, -1, 0, 0, 0, 0, 1 }
+#define MPU_COMPASS_NAME "ak8975"
+#define MPU_COMPASS_IRQ_GPIO 0
+#define MPU_COMPASS_ADDR 0x0C
+#define MPU_COMPASS_BUS_NUM 0
+#define MPU_COMPASS_ORIENTATION { 0, 1, 0, -1, 0, 0, 0, 0, 1 }
+
+/* PCA954x I2C bus expander bus addresses */
+#define PCA954x_I2C_BUS_BASE 6
+#define PCA954x_I2C_BUS0 (PCA954x_I2C_BUS_BASE + 0)
+#define PCA954x_I2C_BUS1 (PCA954x_I2C_BUS_BASE + 1)
+#define PCA954x_I2C_BUS2 (PCA954x_I2C_BUS_BASE + 2)
+#define PCA954x_I2C_BUS3 (PCA954x_I2C_BUS_BASE + 3)
+
+/*****************External GPIO tables ******************/
+/* External peripheral gpio base. */
+#define ENT_TPS80031_GPIO_BASE TEGRA_NR_GPIOS
+#define ENT_TPS80031_GPIO_REGEN1 (ENT_TPS80031_GPIO_BASE + TPS80031_GPIO_REGEN1)
+#define ENT_TPS80031_GPIO_REGEN2 (ENT_TPS80031_GPIO_BASE + TPS80031_GPIO_REGEN2)
+#define ENT_TPS80031_GPIO_SYSEN (ENT_TPS80031_GPIO_BASE + TPS80031_GPIO_SYSEN)
+#define ENT_TPS80031_GPIO_END (ENT_TPS80031_GPIO_BASE + TPS80031_GPIO_NR)
+
+/*****************External Interrupt tables ******************/
+/* External peripheral irq base */
+#define ENT_TPS80031_IRQ_BASE TEGRA_NR_IRQS
+#define ENT_TPS80031_IRQ_END (ENT_TPS80031_IRQ_BASE + TPS80031_INT_NR)
+
+/*****************Camera GPIOs ******************/
+#define CAM_CSI_MUX_SEL_GPIO TEGRA_GPIO_PM3
+#define CAM_CSI_MUX_SEL_REAR 1
+#define CAM_CSI_MUX_SEL_FRONT 0
+
+#define CAM1_RST_L_GPIO TEGRA_GPIO_PM5 /*REAR RIGHT*/
+#define CAM2_RST_L_GPIO TEGRA_GPIO_PF4 /*REAR LEFT*/
+#define CAM3_RST_L_GPIO TEGRA_GPIO_PM2 /*FRONT*/
+#define CAM3_RST_L_TRUE 0
+#define CAM3_RST_L_FALSE 1
+#define CAM3_PWDN_GPIO TEGRA_GPIO_PN4 /*FRONT*/
+#define CAM3_PWDN_TRUE 1
+#define CAM3_PWDN_FALSE 0
+#define CAM_FLASH_EN_GPIO TEGRA_GPIO_PBB3
+#define CAM_FLASH_MAX_TORCH_AMP 7
+#define CAM_FLASH_MAX_FLASH_AMP 7
+#define CAM_I2C_MUX_RST_EXP TEGRA_GPIO_PF3 /*I2C Mux Reset*/
+
+/* Audio-related GPIOs */
+#define TEGRA_GPIO_HP_DET TEGRA_GPIO_PW3
+
+/* Baseband GPIO addresses */
+
+#define BB_GPIO_MDM_PWRON_AP2BB TEGRA_GPIO_PE0 /* LCD_D0 */
+#define BB_GPIO_RESET_AP2BB TEGRA_GPIO_PE1 /* LCD_D1 */
+#define BB_GPIO_LCD_PWR1 TEGRA_GPIO_PC1
+#define BB_GPIO_LCD_PWR2 TEGRA_GPIO_PC6
+#define BB_GPIO_HS1_AP2BB TEGRA_GPIO_PE3 /* LCD_D3 */
+#define BB_GPIO_HS1_BB2AP TEGRA_GPIO_PU5
+
+#define XMM_GPIO_BB_ON BB_GPIO_MDM_PWRON_AP2BB
+#define XMM_GPIO_BB_RST BB_GPIO_RESET_AP2BB
+#define XMM_GPIO_IPC_HSIC_ACTIVE BB_GPIO_LCD_PWR1
+#define XMM_GPIO_IPC_HSIC_SUS_REQ BB_GPIO_LCD_PWR2
+#define XMM_GPIO_IPC_BB_WAKE BB_GPIO_HS1_AP2BB
+#define XMM_GPIO_IPC_AP_WAKE BB_GPIO_HS1_BB2AP
+
+#define TDIODE_OFFSET (9000) /* in millicelsius */
+
+#endif
diff --git a/arch/arm/mach-tegra/board-harmony-kbc.c b/arch/arm/mach-tegra/board-harmony-kbc.c
new file mode 100644
index 000000000000..a780103d978d
--- /dev/null
+++ b/arch/arm/mach-tegra/board-harmony-kbc.c
@@ -0,0 +1,372 @@
+/*
+ * arch/arm/mach-tegra/board-harmony-kbc.c
+ * Keys configuration for Nvidia tegra2 harmony platform.
+ *
+ * Copyright (C) 2011 NVIDIA, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <mach/io.h>
+#include <mach/iomap.h>
+#include <mach/kbc.h>
+
+#include "board.h"
+#include "board-harmony.h"
+#include "devices.h"
+
+#define HARMONY_ROW_COUNT 16
+#define HARMONY_COL_COUNT 8
+
+static const u32 kbd_keymap[] = {
+ KEY(0, 0, KEY_RESERVED),
+ KEY(0, 1, KEY_RESERVED),
+ KEY(0, 2, KEY_W),
+ KEY(0, 3, KEY_S),
+ KEY(0, 4, KEY_A),
+ KEY(0, 5, KEY_Z),
+ KEY(0, 6, KEY_RESERVED),
+ KEY(0, 7, KEY_FN),
+
+ KEY(1, 0, KEY_RESERVED),
+ KEY(1, 1, KEY_RESERVED),
+ KEY(1, 2, KEY_RESERVED),
+ KEY(1, 3, KEY_RESERVED),
+ KEY(1, 4, KEY_RESERVED),
+ KEY(1, 5, KEY_RESERVED),
+ KEY(1, 6, KEY_RESERVED),
+ KEY(1, 7, KEY_MENU),
+
+ KEY(2, 0, KEY_RESERVED),
+ KEY(2, 1, KEY_RESERVED),
+ KEY(2, 2, KEY_RESERVED),
+ KEY(2, 3, KEY_RESERVED),
+ KEY(2, 4, KEY_RESERVED),
+ KEY(2, 5, KEY_RESERVED),
+ KEY(2, 6, KEY_LEFTALT),
+ KEY(2, 7, KEY_RIGHTALT),
+
+ KEY(3, 0, KEY_5),
+ KEY(3, 1, KEY_4),
+ KEY(3, 2, KEY_R),
+ KEY(3, 3, KEY_E),
+ KEY(3, 4, KEY_F),
+ KEY(3, 5, KEY_D),
+ KEY(3, 6, KEY_X),
+ KEY(3, 7, KEY_RESERVED),
+
+ KEY(4, 0, KEY_7),
+ KEY(4, 1, KEY_6),
+ KEY(4, 2, KEY_T),
+ KEY(4, 3, KEY_H),
+ KEY(4, 4, KEY_G),
+ KEY(4, 5, KEY_V),
+ KEY(4, 6, KEY_C),
+ KEY(4, 7, KEY_SPACE),
+
+ KEY(5, 0, KEY_9),
+ KEY(5, 1, KEY_8),
+ KEY(5, 2, KEY_U),
+ KEY(5, 3, KEY_Y),
+ KEY(5, 4, KEY_J),
+ KEY(5, 5, KEY_N),
+ KEY(5, 6, KEY_B),
+ KEY(5, 7, KEY_BACKSLASH),
+
+ KEY(6, 0, KEY_MINUS),
+ KEY(6, 1, KEY_0),
+ KEY(6, 2, KEY_O),
+ KEY(6, 3, KEY_I),
+ KEY(6, 4, KEY_L),
+ KEY(6, 5, KEY_K),
+ KEY(6, 6, KEY_COMMA),
+ KEY(6, 7, KEY_M),
+
+ KEY(7, 0, KEY_RESERVED),
+ KEY(7, 1, KEY_EQUAL),
+ KEY(7, 2, KEY_RIGHTBRACE),
+ KEY(7, 3, KEY_ENTER),
+ KEY(7, 4, KEY_RESERVED),
+ KEY(7, 5, KEY_RESERVED),
+ KEY(7, 6, KEY_RESERVED),
+ KEY(7, 7, KEY_MENU),
+
+ KEY(8, 0, KEY_RESERVED),
+ KEY(8, 1, KEY_RESERVED),
+ KEY(8, 2, KEY_RESERVED),
+ KEY(8, 3, KEY_RESERVED),
+ KEY(8, 4, KEY_LEFTSHIFT),
+ KEY(8, 5, KEY_RIGHTSHIFT),
+ KEY(8, 6, KEY_RESERVED),
+ KEY(8, 7, KEY_RESERVED),
+
+ KEY(9, 0, KEY_RESERVED),
+ KEY(9, 1, KEY_RESERVED),
+ KEY(9, 2, KEY_RESERVED),
+ KEY(9, 3, KEY_RESERVED),
+ KEY(9, 4, KEY_RESERVED),
+ KEY(9, 5, KEY_LEFTCTRL),
+ KEY(9, 6, KEY_RESERVED),
+ KEY(9, 7, KEY_RIGHTCTRL),
+
+ KEY(10, 0, KEY_RESERVED),
+ KEY(10, 1, KEY_RESERVED),
+ KEY(10, 2, KEY_RESERVED),
+ KEY(10, 3, KEY_RESERVED),
+ KEY(10, 4, KEY_RESERVED),
+ KEY(10, 5, KEY_RESERVED),
+ KEY(10, 6, KEY_RESERVED),
+ KEY(10, 7, KEY_RESERVED),
+
+ KEY(11, 0, KEY_LEFTBRACE),
+ KEY(11, 1, KEY_P),
+ KEY(11, 2, KEY_APOSTROPHE),
+ KEY(11, 3, KEY_SEMICOLON),
+ KEY(11, 4, KEY_SLASH),
+ KEY(11, 5, KEY_DOT),
+ KEY(11, 6, KEY_RESERVED),
+ KEY(11, 7, KEY_RESERVED),
+
+ KEY(12, 0, KEY_F10),
+ KEY(12, 1, KEY_F9),
+ KEY(12, 2, KEY_BACKSPACE),
+ KEY(12, 3, KEY_3),
+ KEY(12, 4, KEY_2),
+ KEY(12, 5, KEY_UP),
+ KEY(12, 6, KEY_PRINT),
+ KEY(12, 7, KEY_PAUSE),
+
+ KEY(13, 0, KEY_INSERT),
+ KEY(13, 1, KEY_DELETE),
+ KEY(13, 2, KEY_RESERVED),
+ KEY(13, 3, KEY_PAGEUP),
+ KEY(13, 4, KEY_PAGEDOWN),
+ KEY(13, 5, KEY_RIGHT),
+ KEY(13, 6, KEY_DOWN),
+ KEY(13, 7, KEY_LEFT),
+
+ KEY(14, 0, KEY_F11),
+ KEY(14, 1, KEY_F12),
+ KEY(14, 2, KEY_F8),
+ KEY(14, 3, KEY_Q),
+ KEY(14, 4, KEY_F4),
+ KEY(14, 5, KEY_F3),
+ KEY(14, 6, KEY_1),
+ KEY(14, 7, KEY_F7),
+
+ KEY(15, 0, KEY_ESC),
+ KEY(15, 1, KEY_GRAVE),
+ KEY(15, 2, KEY_F5),
+ KEY(15, 3, KEY_TAB),
+ KEY(15, 4, KEY_F1),
+ KEY(15, 5, KEY_F2),
+ KEY(15, 6, KEY_CAPSLOCK),
+ KEY(15, 7, KEY_F6),
+
+ KEY(16, 0, KEY_RESERVED),
+ KEY(16, 1, KEY_RESERVED),
+ KEY(16, 2, KEY_RESERVED),
+ KEY(16, 3, KEY_RESERVED),
+ KEY(16, 4, KEY_RESERVED),
+ KEY(16, 5, KEY_RESERVED),
+ KEY(16, 6, KEY_RESERVED),
+ KEY(16, 7, KEY_RESERVED),
+
+ KEY(17, 0, KEY_RESERVED),
+ KEY(17, 1, KEY_RESERVED),
+ KEY(17, 2, KEY_RESERVED),
+ KEY(17, 3, KEY_RESERVED),
+ KEY(17, 4, KEY_RESERVED),
+ KEY(17, 5, KEY_RESERVED),
+ KEY(17, 6, KEY_RESERVED),
+ KEY(17, 7, KEY_RESERVED),
+
+ KEY(18, 0, KEY_RESERVED),
+ KEY(18, 1, KEY_RESERVED),
+ KEY(18, 2, KEY_RESERVED),
+ KEY(18, 3, KEY_RESERVED),
+ KEY(18, 4, KEY_RESERVED),
+ KEY(18, 5, KEY_RESERVED),
+ KEY(18, 6, KEY_RESERVED),
+ KEY(18, 7, KEY_RESERVED),
+
+ KEY(19, 0, KEY_RESERVED),
+ KEY(19, 1, KEY_RESERVED),
+ KEY(19, 2, KEY_RESERVED),
+ KEY(19, 3, KEY_RESERVED),
+ KEY(19, 4, KEY_RESERVED),
+ KEY(19, 5, KEY_RESERVED),
+ KEY(19, 6, KEY_RESERVED),
+ KEY(19, 7, KEY_RESERVED),
+
+ KEY(20, 0, KEY_7),
+ KEY(20, 1, KEY_RESERVED),
+ KEY(20, 2, KEY_RESERVED),
+ KEY(20, 3, KEY_RESERVED),
+ KEY(20, 4, KEY_RESERVED),
+ KEY(20, 5, KEY_RESERVED),
+ KEY(20, 6, KEY_RESERVED),
+ KEY(20, 7, KEY_RESERVED),
+
+ KEY(21, 0, KEY_9),
+ KEY(21, 1, KEY_8),
+ KEY(21, 2, KEY_4),
+ KEY(21, 3, KEY_RESERVED),
+ KEY(21, 4, KEY_1),
+ KEY(21, 5, KEY_RESERVED),
+ KEY(21, 6, KEY_RESERVED),
+ KEY(21, 7, KEY_RESERVED),
+
+ KEY(22, 0, KEY_RESERVED),
+ KEY(22, 1, KEY_SLASH),
+ KEY(22, 2, KEY_6),
+ KEY(22, 3, KEY_5),
+ KEY(22, 4, KEY_3),
+ KEY(22, 5, KEY_2),
+ KEY(22, 6, KEY_RESERVED),
+ KEY(22, 7, KEY_0),
+
+ KEY(23, 0, KEY_RESERVED),
+ KEY(23, 1, KEY_RESERVED),
+ KEY(23, 2, KEY_RESERVED),
+ KEY(23, 3, KEY_RESERVED),
+ KEY(23, 4, KEY_RESERVED),
+ KEY(23, 5, KEY_RESERVED),
+ KEY(23, 6, KEY_RESERVED),
+ KEY(23, 7, KEY_RESERVED),
+
+ KEY(24, 0, KEY_RESERVED),
+ KEY(24, 1, KEY_RESERVED),
+ KEY(24, 2, KEY_RESERVED),
+ KEY(24, 3, KEY_RESERVED),
+ KEY(24, 4, KEY_RESERVED),
+ KEY(24, 5, KEY_RESERVED),
+ KEY(24, 6, KEY_RESERVED),
+ KEY(24, 7, KEY_RESERVED),
+
+ KEY(25, 0, KEY_RESERVED),
+ KEY(25, 1, KEY_RESERVED),
+ KEY(25, 2, KEY_RESERVED),
+ KEY(25, 3, KEY_RESERVED),
+ KEY(25, 4, KEY_RESERVED),
+ KEY(25, 5, KEY_RESERVED),
+ KEY(25, 6, KEY_RESERVED),
+ KEY(25, 7, KEY_RESERVED),
+
+ KEY(26, 0, KEY_RESERVED),
+ KEY(26, 1, KEY_RESERVED),
+ KEY(26, 2, KEY_RESERVED),
+ KEY(26, 3, KEY_RESERVED),
+ KEY(26, 4, KEY_RESERVED),
+ KEY(26, 5, KEY_RESERVED),
+ KEY(26, 6, KEY_RESERVED),
+ KEY(26, 7, KEY_RESERVED),
+
+ KEY(27, 0, KEY_RESERVED),
+ KEY(27, 1, KEY_KPASTERISK),
+ KEY(27, 2, KEY_RESERVED),
+ KEY(27, 3, KEY_KPMINUS),
+ KEY(27, 4, KEY_KPPLUS),
+ KEY(27, 5, KEY_DOT),
+ KEY(27, 6, KEY_RESERVED),
+ KEY(27, 7, KEY_RESERVED),
+
+ KEY(28, 0, KEY_RESERVED),
+ KEY(28, 1, KEY_RESERVED),
+ KEY(28, 2, KEY_RESERVED),
+ KEY(28, 3, KEY_RESERVED),
+ KEY(28, 4, KEY_RESERVED),
+ KEY(28, 5, KEY_VOLUMEUP),
+ KEY(28, 6, KEY_RESERVED),
+ KEY(28, 7, KEY_RESERVED),
+
+ KEY(29, 0, KEY_RESERVED),
+ KEY(29, 1, KEY_RESERVED),
+ KEY(29, 2, KEY_RESERVED),
+ KEY(29, 3, KEY_HOME),
+ KEY(29, 4, KEY_END),
+ KEY(29, 5, KEY_BRIGHTNESSUP),
+ KEY(29, 6, KEY_VOLUMEDOWN),
+ KEY(29, 7, KEY_BRIGHTNESSDOWN),
+
+ KEY(30, 0, KEY_NUMLOCK),
+ KEY(30, 1, KEY_SCROLLLOCK),
+ KEY(30, 2, KEY_MUTE),
+ KEY(30, 3, KEY_RESERVED),
+ KEY(30, 4, KEY_RESERVED),
+ KEY(30, 5, KEY_RESERVED),
+ KEY(30, 6, KEY_RESERVED),
+ KEY(30, 7, KEY_RESERVED),
+
+ KEY(31, 0, KEY_RESERVED),
+ KEY(31, 1, KEY_RESERVED),
+ KEY(31, 2, KEY_RESERVED),
+ KEY(31, 3, KEY_RESERVED),
+ KEY(31, 4, KEY_QUESTION),
+ KEY(31, 5, KEY_RESERVED),
+ KEY(31, 6, KEY_RESERVED),
+ KEY(31, 7, KEY_RESERVED),
+};
+
+static const struct matrix_keymap_data keymap_data = {
+ .keymap = kbd_keymap,
+ .keymap_size = ARRAY_SIZE(kbd_keymap),
+};
+
+static struct tegra_kbc_wake_key harmony_wake_cfg[] = {
+ [0] = {
+ .row = 1,
+ .col = 7,
+ },
+ [1] = {
+ .row = 15,
+ .col = 0,
+ },
+};
+
+static struct tegra_kbc_platform_data harmony_kbc_platform_data = {
+ .debounce_cnt = 2,
+ .repeat_cnt = 5 * 32,
+ .wakeup = true,
+ .keymap_data = &keymap_data,
+ .use_fn_map = true,
+ .wake_cnt = 2,
+ .wake_cfg = &harmony_wake_cfg[0],
+};
+
+int __init harmony_kbc_init(void)
+{
+ struct tegra_kbc_platform_data *data = &harmony_kbc_platform_data;
+ int i;
+ tegra_kbc_device.dev.platform_data = &harmony_kbc_platform_data;
+ pr_info("Registering tegra-kbc\n");
+
+ BUG_ON((KBC_MAX_ROW + KBC_MAX_COL) > KBC_MAX_GPIO);
+ for (i = 0; i < KBC_MAX_ROW; i++) {
+ data->pin_cfg[i].num = i;
+ data->pin_cfg[i].is_row = true;
+ }
+
+ for (i = 0; i < KBC_MAX_COL; i++)
+ data->pin_cfg[i + KBC_MAX_ROW].num = i;
+
+ platform_device_register(&tegra_kbc_device);
+ pr_info("Registering successful tegra-kbc\n");
+ return 0;
+}
+
diff --git a/arch/arm/mach-tegra/board-harmony-panel.c b/arch/arm/mach-tegra/board-harmony-panel.c
new file mode 100644
index 000000000000..2e24a4bc8046
--- /dev/null
+++ b/arch/arm/mach-tegra/board-harmony-panel.c
@@ -0,0 +1,205 @@
+/*
+ * arch/arm/mach-tegra/board-harmony-panel.c
+ *
+ * Copyright (c) 2010-2011, NVIDIA Corporation.
+ *
+ * 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/delay.h>
+#include <linux/resource.h>
+#include <linux/platform_device.h>
+#include <asm/mach-types.h>
+#include <linux/nvhost.h>
+#include <linux/gpio.h>
+
+#include <mach/dc.h>
+#include <mach/irqs.h>
+#include <mach/iomap.h>
+#include <mach/nvmap.h>
+#include <mach/tegra_fb.h>
+
+#include "devices.h"
+#include "gpio-names.h"
+#include "board.h"
+
+#define harmony_bl_enb TEGRA_GPIO_PB5
+#define harmony_lvds_shutdown TEGRA_GPIO_PB2
+#define harmony_en_vdd_pnl TEGRA_GPIO_PC6
+#define harmony_bl_vdd TEGRA_GPIO_PW0
+#define harmony_bl_pwm TEGRA_GPIO_PB4
+
+/* panel power on sequence timing */
+#define harmony_pnl_to_lvds_ms 0
+#define harmony_lvds_to_bl_ms 200
+
+
+static int harmony_panel_enable(void)
+{
+ gpio_set_value(harmony_en_vdd_pnl, 1);
+ mdelay(harmony_pnl_to_lvds_ms);
+ gpio_set_value(harmony_lvds_shutdown, 1);
+ mdelay(harmony_lvds_to_bl_ms);
+ return 0;
+}
+
+static int harmony_panel_disable(void)
+{
+ gpio_set_value(harmony_lvds_shutdown, 0);
+ gpio_set_value(harmony_en_vdd_pnl, 0);
+ return 0;
+}
+
+static struct resource harmony_disp1_resources[] = {
+ {
+ .name = "irq",
+ .start = INT_DISPLAY_GENERAL,
+ .end = INT_DISPLAY_GENERAL,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "regs",
+ .start = TEGRA_DISPLAY_BASE,
+ .end = TEGRA_DISPLAY_BASE + TEGRA_DISPLAY_SIZE-1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .name = "fbmem",
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct tegra_dc_mode harmony_panel_modes[] = {
+ {
+ .pclk = 42430000,
+ .h_ref_to_sync = 4,
+ .v_ref_to_sync = 2,
+ .h_sync_width = 136,
+ .v_sync_width = 4,
+ .h_back_porch = 138,
+ .v_back_porch = 21,
+ .h_active = 1024,
+ .v_active = 600,
+ .h_front_porch = 34,
+ .v_front_porch = 4,
+ },
+};
+
+static struct tegra_fb_data harmony_fb_data = {
+ .win = 0,
+ .xres = 1024,
+ .yres = 600,
+ .bits_per_pixel = 32,
+ .flags = TEGRA_FB_FLIP_ON_PROBE,
+};
+
+static struct tegra_dc_out harmony_disp1_out = {
+ .type = TEGRA_DC_OUT_RGB,
+
+ .align = TEGRA_DC_ALIGN_MSB,
+ .order = TEGRA_DC_ORDER_RED_BLUE,
+ .depth = 18,
+ .dither = TEGRA_DC_ORDERED_DITHER,
+
+ .modes = harmony_panel_modes,
+ .n_modes = ARRAY_SIZE(harmony_panel_modes),
+
+ .enable = harmony_panel_enable,
+ .disable = harmony_panel_disable,
+};
+
+static struct tegra_dc_platform_data harmony_disp1_pdata = {
+ .flags = TEGRA_DC_FLAG_ENABLED,
+ .default_out = &harmony_disp1_out,
+ .fb = &harmony_fb_data,
+};
+
+static struct nvhost_device harmony_disp1_device = {
+ .name = "tegradc",
+ .id = 0,
+ .resource = harmony_disp1_resources,
+ .num_resources = ARRAY_SIZE(harmony_disp1_resources),
+ .dev = {
+ .platform_data = &harmony_disp1_pdata,
+ },
+};
+
+static struct nvmap_platform_carveout harmony_carveouts[] = {
+ [0] = NVMAP_HEAP_CARVEOUT_IRAM_INIT,
+ [1] = {
+ .name = "generic-0",
+ .usage_mask = NVMAP_HEAP_CARVEOUT_GENERIC,
+ .buddy_size = SZ_32K,
+ },
+};
+
+static struct nvmap_platform_data harmony_nvmap_data = {
+ .carveouts = harmony_carveouts,
+ .nr_carveouts = ARRAY_SIZE(harmony_carveouts),
+};
+
+static struct platform_device harmony_nvmap_device = {
+ .name = "tegra-nvmap",
+ .id = -1,
+ .dev = {
+ .platform_data = &harmony_nvmap_data,
+ },
+};
+
+static struct platform_device *harmony_gfx_devices[] __initdata = {
+ &harmony_nvmap_device,
+ &tegra_grhost_device,
+};
+
+int __init harmony_panel_init(void) {
+ int err;
+ struct resource *res;
+
+ gpio_request(harmony_en_vdd_pnl, "en_vdd_pnl");
+ gpio_direction_output(harmony_en_vdd_pnl, 1);
+ tegra_gpio_enable(harmony_en_vdd_pnl);
+
+ gpio_request(harmony_bl_vdd, "bl_vdd");
+ gpio_direction_output(harmony_bl_vdd, 1);
+ tegra_gpio_enable(harmony_bl_vdd);
+
+ gpio_request(harmony_lvds_shutdown, "lvds_shdn");
+ gpio_direction_output(harmony_lvds_shutdown, 1);
+ tegra_gpio_enable(harmony_lvds_shutdown);
+
+ harmony_carveouts[1].base = tegra_carveout_start;
+ harmony_carveouts[1].size = tegra_carveout_size;
+
+ err = platform_add_devices(harmony_gfx_devices,
+ ARRAY_SIZE(harmony_gfx_devices));
+ if (err)
+ return err;
+
+ res = nvhost_get_resource_byname(&harmony_disp1_device,
+ IORESOURCE_MEM, "fbmem");
+ if (res) {
+ res->start = tegra_fb_start;
+ res->end = tegra_fb_start + tegra_fb_size - 1;
+
+ /* Copy the bootloader fb to the fb. */
+ if (tegra_bootloader_fb_start)
+ tegra_move_framebuffer(tegra_fb_start,
+ tegra_bootloader_fb_start,
+ min(tegra_fb_size, tegra_bootloader_fb_size));
+
+ err = nvhost_device_register(&harmony_disp1_device);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
diff --git a/arch/arm/mach-tegra/board-harmony-pinmux.c b/arch/arm/mach-tegra/board-harmony-pinmux.c
index 4d63e2e97a8d..2fa64cff58f3 100644
--- a/arch/arm/mach-tegra/board-harmony-pinmux.c
+++ b/arch/arm/mach-tegra/board-harmony-pinmux.c
@@ -16,12 +16,29 @@
#include <linux/kernel.h>
#include <linux/gpio.h>
+#include <linux/init.h>
#include <mach/pinmux.h>
#include "gpio-names.h"
#include "board-harmony.h"
-static struct tegra_pingroup_config harmony_pinmux[] = {
+#define DEFAULT_DRIVE(_name) \
+ { \
+ .pingroup = TEGRA_DRIVE_PINGROUP_##_name, \
+ .hsm = TEGRA_HSM_DISABLE, \
+ .schmitt = TEGRA_SCHMITT_ENABLE, \
+ .drive = TEGRA_DRIVE_DIV_1, \
+ .pull_down = TEGRA_PULL_31, \
+ .pull_up = TEGRA_PULL_31, \
+ .slew_rising = TEGRA_SLEW_SLOWEST, \
+ .slew_falling = TEGRA_SLEW_SLOWEST, \
+ }
+
+static __initdata struct tegra_drive_pingroup_config harmony_drive_pinmux[] = {
+ DEFAULT_DRIVE(SDIO1),
+};
+
+static __initdata struct tegra_pingroup_config harmony_pinmux[] = {
{TEGRA_PINGROUP_ATA, TEGRA_MUX_IDE, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
{TEGRA_PINGROUP_ATB, TEGRA_MUX_SDIO4, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
{TEGRA_PINGROUP_ATC, TEGRA_MUX_NAND, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
@@ -41,7 +58,7 @@ static struct tegra_pingroup_config harmony_pinmux[] = {
{TEGRA_PINGROUP_DTC, TEGRA_MUX_RSVD1, TEGRA_PUPD_NORMAL, TEGRA_TRI_TRISTATE},
{TEGRA_PINGROUP_DTD, TEGRA_MUX_SDIO2, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
{TEGRA_PINGROUP_DTE, TEGRA_MUX_RSVD1, TEGRA_PUPD_NORMAL, TEGRA_TRI_TRISTATE},
- {TEGRA_PINGROUP_DTF, TEGRA_MUX_I2C3, TEGRA_PUPD_NORMAL, TEGRA_TRI_TRISTATE},
+ {TEGRA_PINGROUP_DTF, TEGRA_MUX_I2C3, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
{TEGRA_PINGROUP_GMA, TEGRA_MUX_SDIO4, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
{TEGRA_PINGROUP_GMB, TEGRA_MUX_GMI, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
{TEGRA_PINGROUP_GMC, TEGRA_MUX_UARTD, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
@@ -102,18 +119,18 @@ static struct tegra_pingroup_config harmony_pinmux[] = {
{TEGRA_PINGROUP_LVS, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
{TEGRA_PINGROUP_OWC, TEGRA_MUX_RSVD2, TEGRA_PUPD_PULL_UP, TEGRA_TRI_TRISTATE},
{TEGRA_PINGROUP_PMC, TEGRA_MUX_PWR_ON, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
- {TEGRA_PINGROUP_PTA, TEGRA_MUX_HDMI, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_PTA, TEGRA_MUX_HDMI, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
{TEGRA_PINGROUP_RM, TEGRA_MUX_I2C, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
{TEGRA_PINGROUP_SDB, TEGRA_MUX_PWM, TEGRA_PUPD_NORMAL, TEGRA_TRI_TRISTATE},
{TEGRA_PINGROUP_SDC, TEGRA_MUX_PWM, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
{TEGRA_PINGROUP_SDD, TEGRA_MUX_PWM, TEGRA_PUPD_PULL_UP, TEGRA_TRI_TRISTATE},
- {TEGRA_PINGROUP_SDIO1, TEGRA_MUX_SDIO1, TEGRA_PUPD_NORMAL, TEGRA_TRI_TRISTATE},
+ {TEGRA_PINGROUP_SDIO1, TEGRA_MUX_SDIO1, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
{TEGRA_PINGROUP_SLXA, TEGRA_MUX_PCIE, TEGRA_PUPD_NORMAL, TEGRA_TRI_TRISTATE},
{TEGRA_PINGROUP_SLXC, TEGRA_MUX_SPDIF, TEGRA_PUPD_NORMAL, TEGRA_TRI_TRISTATE},
{TEGRA_PINGROUP_SLXD, TEGRA_MUX_SPDIF, TEGRA_PUPD_NORMAL, TEGRA_TRI_TRISTATE},
{TEGRA_PINGROUP_SLXK, TEGRA_MUX_PCIE, TEGRA_PUPD_NORMAL, TEGRA_TRI_TRISTATE},
- {TEGRA_PINGROUP_SPDI, TEGRA_MUX_RSVD2, TEGRA_PUPD_NORMAL, TEGRA_TRI_TRISTATE},
- {TEGRA_PINGROUP_SPDO, TEGRA_MUX_RSVD2, TEGRA_PUPD_NORMAL, TEGRA_TRI_TRISTATE},
+ {TEGRA_PINGROUP_SPDI, TEGRA_MUX_RSVD2, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_SPDO, TEGRA_MUX_RSVD2, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
{TEGRA_PINGROUP_SPIA, TEGRA_MUX_GMI, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
{TEGRA_PINGROUP_SPIB, TEGRA_MUX_GMI, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
{TEGRA_PINGROUP_SPIC, TEGRA_MUX_GMI, TEGRA_PUPD_PULL_UP, TEGRA_TRI_TRISTATE},
@@ -153,9 +170,11 @@ static struct tegra_gpio_table gpio_table[] = {
{ .gpio = TEGRA_GPIO_EXT_MIC_EN, .enable = true },
};
-void harmony_pinmux_init(void)
+void __init harmony_pinmux_init(void)
{
tegra_pinmux_config_table(harmony_pinmux, ARRAY_SIZE(harmony_pinmux));
+ tegra_drive_pinmux_config_table(harmony_drive_pinmux,
+ ARRAY_SIZE(harmony_drive_pinmux));
tegra_gpio_config(gpio_table, ARRAY_SIZE(gpio_table));
}
diff --git a/arch/arm/mach-tegra/board-harmony-power.c b/arch/arm/mach-tegra/board-harmony-power.c
index 5ad8b2f94f8d..9ebc04baee2f 100644
--- a/arch/arm/mach-tegra/board-harmony-power.c
+++ b/arch/arm/mach-tegra/board-harmony-power.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 NVIDIA, Inc.
+ * Copyright (C) 2010-2011 NVIDIA, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -20,8 +20,11 @@
#include <linux/gpio.h>
#include <linux/regulator/machine.h>
+#include <linux/regulator/fixed.h>
#include <linux/mfd/tps6586x.h>
+#include <linux/io.h>
+#include <mach/iomap.h>
#include <mach/irqs.h>
#include "board-harmony.h"
@@ -29,26 +32,163 @@
#define PMC_CTRL 0x0
#define PMC_CTRL_INTR_LOW (1 << 17)
+static struct regulator_consumer_supply tps658621_sm0_supply[] = {
+ REGULATOR_SUPPLY("vdd_core", NULL),
+};
+
+static struct regulator_consumer_supply tps658621_sm1_supply[] = {
+ REGULATOR_SUPPLY("vdd_cpu", NULL),
+};
+
+static struct regulator_consumer_supply tps658621_sm2_supply[] = {
+ REGULATOR_SUPPLY("vdd_sm2", NULL),
+};
+
static struct regulator_consumer_supply tps658621_ldo0_supply[] = {
- REGULATOR_SUPPLY("pex_clk", NULL),
+ REGULATOR_SUPPLY("p_cam_avdd", NULL),
+};
+
+static struct regulator_consumer_supply tps658621_ldo1_supply[] = {
+ REGULATOR_SUPPLY("avdd_pll", NULL),
+};
+
+static struct regulator_consumer_supply tps658621_ldo2_supply[] = {
+ REGULATOR_SUPPLY("vdd_rtc", NULL),
+};
+
+static struct regulator_consumer_supply tps658621_ldo3_supply[] = {
+ REGULATOR_SUPPLY("avdd_usb", NULL),
+ REGULATOR_SUPPLY("avdd_usb_pll", NULL),
+ REGULATOR_SUPPLY("avdd_lvds", NULL),
+};
+
+static struct regulator_consumer_supply tps658621_ldo4_supply[] = {
+ REGULATOR_SUPPLY("avdd_osc", NULL),
+ REGULATOR_SUPPLY("vddio_sys", "panjit_touch"),
+};
+
+static struct regulator_consumer_supply tps658621_ldo5_supply[] = {
+ REGULATOR_SUPPLY("vcore_mmc", "sdhci-tegra.1"),
+ REGULATOR_SUPPLY("vcore_mmc", "sdhci-tegra.3"),
};
-static struct regulator_init_data ldo0_data = {
+static struct regulator_consumer_supply tps658621_ldo6_supply[] = {
+ REGULATOR_SUPPLY("avdd_vdac", NULL),
+};
+
+static struct regulator_consumer_supply tps658621_ldo7_supply[] = {
+ REGULATOR_SUPPLY("avdd_hdmi", NULL),
+ REGULATOR_SUPPLY("vdd_fuse", NULL),
+};
+
+static struct regulator_consumer_supply tps658621_ldo8_supply[] = {
+ REGULATOR_SUPPLY("avdd_hdmi_pll", NULL),
+};
+
+static struct regulator_consumer_supply tps658621_ldo9_supply[] = {
+ REGULATOR_SUPPLY("avdd_2v85", NULL),
+ REGULATOR_SUPPLY("vdd_ddr_rx", NULL),
+ REGULATOR_SUPPLY("avdd_amp", NULL),
+};
+
+/* regulator supplies power to WWAN - by default disable */
+static struct regulator_consumer_supply vdd_1v5_consumer_supply[] = {
+ REGULATOR_SUPPLY("vdd_1v5", NULL),
+};
+
+static struct regulator_init_data vdd_1v5_initdata = {
+ .consumer_supplies = vdd_1v5_consumer_supply,
+ .num_consumer_supplies = 1,
.constraints = {
- .min_uV = 1250 * 1000,
- .max_uV = 3300 * 1000,
- .valid_modes_mask = (REGULATOR_MODE_NORMAL |
- REGULATOR_MODE_STANDBY),
- .valid_ops_mask = (REGULATOR_CHANGE_MODE |
- REGULATOR_CHANGE_STATUS |
- REGULATOR_CHANGE_VOLTAGE),
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+ .always_on = 0,
},
- .num_consumer_supplies = ARRAY_SIZE(tps658621_ldo0_supply),
- .consumer_supplies = tps658621_ldo0_supply,
};
-#define HARMONY_REGULATOR_INIT(_id, _minmv, _maxmv) \
- static struct regulator_init_data _id##_data = { \
+static struct fixed_voltage_config vdd_1v5 = {
+ .supply_name = "vdd_1v5",
+ .microvolts = 1500000, /* Enable 1.5V */
+ .gpio = TPS_GPIO_EN_1V5, /* GPIO BASE+0 */
+ .startup_delay = 0,
+ .enable_high = 0,
+ .enabled_at_boot = 0,
+ .init_data = &vdd_1v5_initdata,
+};
+
+/* regulator supplies power to WLAN - enable here, to satisfy SDIO probing */
+static struct regulator_consumer_supply vdd_1v2_consumer_supply[] = {
+ REGULATOR_SUPPLY("vdd_1v2", NULL),
+};
+
+static struct regulator_init_data vdd_1v2_initdata = {
+ .consumer_supplies = vdd_1v2_consumer_supply,
+ .num_consumer_supplies = 1,
+ .constraints = {
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+ .always_on = 1,
+ },
+};
+
+static struct fixed_voltage_config vdd_1v2 = {
+ .supply_name = "vdd_1v2",
+ .microvolts = 1200000, /* Enable 1.2V */
+ .gpio = TPS_GPIO_EN_1V2, /* GPIO BASE+1 */
+ .startup_delay = 0,
+ .enable_high = 1,
+ .enabled_at_boot = 1,
+ .init_data = &vdd_1v2_initdata,
+};
+
+/* regulator supplies power to PLL - enable here */
+static struct regulator_consumer_supply vdd_1v05_consumer_supply[] = {
+ REGULATOR_SUPPLY("vdd_1v05", NULL),
+};
+
+static struct regulator_init_data vdd_1v05_initdata = {
+ .consumer_supplies = vdd_1v05_consumer_supply,
+ .num_consumer_supplies = 1,
+ .constraints = {
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+ .always_on = 1,
+ },
+};
+
+static struct fixed_voltage_config vdd_1v05 = {
+ .supply_name = "vdd_1v05",
+ .microvolts = 1050000, /* Enable 1.05V */
+ .gpio = TPS_GPIO_EN_1V05, /* BASE+2 */
+ .startup_delay = 0,
+ .enable_high = 1,
+ .enabled_at_boot = 0,
+ .init_data = &vdd_1v05_initdata,
+};
+
+/* mode pin for 1.05V regulator - enable here */
+static struct regulator_consumer_supply vdd_1v05_mode_consumer_supply[] = {
+ REGULATOR_SUPPLY("vdd_1v05_mode", NULL),
+};
+
+static struct regulator_init_data vdd_1v05_mode_initdata = {
+ .consumer_supplies = vdd_1v05_mode_consumer_supply,
+ .num_consumer_supplies = 1,
+ .constraints = {
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+ .always_on = 1,
+ },
+};
+
+static struct fixed_voltage_config vdd_1v05_mode = {
+ .supply_name = "vdd_1v05_mode",
+ .microvolts = 1050000, /* Enable 1.05V */
+ .gpio = TPS_GPIO_MODE_1V05, /* BASE+3 */
+ .startup_delay = 0,
+ .enable_high = 1,
+ .enabled_at_boot = 0,
+ .init_data = &vdd_1v05_mode_initdata,
+};
+
+#define REGULATOR_INIT(_id, _minmv, _maxmv) \
+ { \
.constraints = { \
.min_uV = (_minmv)*1000, \
.max_uV = (_maxmv)*1000, \
@@ -58,20 +198,27 @@ static struct regulator_init_data ldo0_data = {
REGULATOR_CHANGE_STATUS | \
REGULATOR_CHANGE_VOLTAGE), \
}, \
+ .num_consumer_supplies = ARRAY_SIZE(tps658621_##_id##_supply),\
+ .consumer_supplies = tps658621_##_id##_supply, \
}
-HARMONY_REGULATOR_INIT(sm0, 725, 1500);
-HARMONY_REGULATOR_INIT(sm1, 725, 1500);
-HARMONY_REGULATOR_INIT(sm2, 3000, 4550);
-HARMONY_REGULATOR_INIT(ldo1, 725, 1500);
-HARMONY_REGULATOR_INIT(ldo2, 725, 1500);
-HARMONY_REGULATOR_INIT(ldo3, 1250, 3300);
-HARMONY_REGULATOR_INIT(ldo4, 1700, 2475);
-HARMONY_REGULATOR_INIT(ldo5, 1250, 3300);
-HARMONY_REGULATOR_INIT(ldo6, 1250, 3300);
-HARMONY_REGULATOR_INIT(ldo7, 1250, 3300);
-HARMONY_REGULATOR_INIT(ldo8, 1250, 3300);
-HARMONY_REGULATOR_INIT(ldo9, 1250, 3300);
+static struct regulator_init_data sm0_data = REGULATOR_INIT(sm0, 725, 1500);
+static struct regulator_init_data sm1_data = REGULATOR_INIT(sm1, 725, 1500);
+static struct regulator_init_data sm2_data = REGULATOR_INIT(sm2, 3000, 4550);
+static struct regulator_init_data ldo0_data = REGULATOR_INIT(ldo0, 1250, 3300);
+static struct regulator_init_data ldo1_data = REGULATOR_INIT(ldo1, 725, 1500);
+static struct regulator_init_data ldo2_data = REGULATOR_INIT(ldo2, 725, 1500);
+static struct regulator_init_data ldo3_data = REGULATOR_INIT(ldo3, 1250, 3300);
+static struct regulator_init_data ldo4_data = REGULATOR_INIT(ldo4, 1700, 2475);
+static struct regulator_init_data ldo5_data = REGULATOR_INIT(ldo5, 1250, 3300);
+static struct regulator_init_data ldo6_data = REGULATOR_INIT(ldo6, 1250, 3300);
+static struct regulator_init_data ldo7_data = REGULATOR_INIT(ldo7, 1250, 3300);
+static struct regulator_init_data ldo8_data = REGULATOR_INIT(ldo8, 1250, 3300);
+static struct regulator_init_data ldo9_data = REGULATOR_INIT(ldo9, 1250, 3300);
+
+static struct tps6586x_rtc_platform_data rtc_data = {
+ .irq = TEGRA_NR_IRQS + TPS6586X_INT_RTC_ALM1,
+};
#define TPS_REG(_id, _data) \
{ \
@@ -80,6 +227,13 @@ HARMONY_REGULATOR_INIT(ldo9, 1250, 3300);
.platform_data = _data, \
}
+#define TPS_GPIO_FIXED_REG(_id, _data) \
+ { \
+ .id = _id, \
+ .name = "reg-fixed-voltage", \
+ .platform_data = _data, \
+ }
+
static struct tps6586x_subdev_info tps_devs[] = {
TPS_REG(SM_0, &sm0_data),
TPS_REG(SM_1, &sm1_data),
@@ -94,6 +248,15 @@ static struct tps6586x_subdev_info tps_devs[] = {
TPS_REG(LDO_7, &ldo7_data),
TPS_REG(LDO_8, &ldo8_data),
TPS_REG(LDO_9, &ldo9_data),
+ TPS_GPIO_FIXED_REG(0, &vdd_1v5),
+ TPS_GPIO_FIXED_REG(1, &vdd_1v2),
+ TPS_GPIO_FIXED_REG(2, &vdd_1v05),
+ TPS_GPIO_FIXED_REG(3, &vdd_1v05_mode),
+ {
+ .id = 0,
+ .name = "tps6586x-rtc",
+ .platform_data = &rtc_data,
+ },
};
static struct tps6586x_platform_data tps_platform = {
@@ -113,7 +276,15 @@ static struct i2c_board_info __initdata harmony_regulators[] = {
int __init harmony_regulator_init(void)
{
- i2c_register_board_info(3, harmony_regulators, 1);
+ void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE);
+ u32 pmc_ctrl;
+
+ /* configure the power management controller to trigger PMU
+ * interrupts when low */
+ pmc_ctrl = readl(pmc + PMC_CTRL);
+ writel(pmc_ctrl | PMC_CTRL_INTR_LOW, pmc + PMC_CTRL);
+
+ i2c_register_board_info(4, harmony_regulators, 1);
return 0;
}
diff --git a/arch/arm/mach-tegra/board-harmony.c b/arch/arm/mach-tegra/board-harmony.c
index 846cd7d69e3e..9fb1c8fbce0c 100644
--- a/arch/arm/mach-tegra/board-harmony.c
+++ b/arch/arm/mach-tegra/board-harmony.c
@@ -20,11 +20,16 @@
#include <linux/platform_device.h>
#include <linux/serial_8250.h>
#include <linux/clk.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
#include <linux/dma-mapping.h>
#include <linux/pda_power.h>
#include <linux/io.h>
#include <linux/gpio.h>
#include <linux/i2c.h>
+#include <linux/i2c-tegra.h>
+#include <linux/memblock.h>
+#include <linux/delay.h>
#include <sound/wm8903.h>
@@ -37,13 +42,124 @@
#include <mach/iomap.h>
#include <mach/irqs.h>
#include <mach/sdhci.h>
+#include <mach/nand.h>
+#include <mach/clk.h>
+#include <mach/usb_phy.h>
+#include "clock.h"
#include "board.h"
#include "board-harmony.h"
#include "clock.h"
#include "devices.h"
#include "gpio-names.h"
+/* NVidia bootloader tags */
+#define ATAG_NVIDIA 0x41000801
+
+#define ATAG_NVIDIA_RM 0x1
+#define ATAG_NVIDIA_DISPLAY 0x2
+#define ATAG_NVIDIA_FRAMEBUFFER 0x3
+#define ATAG_NVIDIA_CHIPSHMOO 0x4
+#define ATAG_NVIDIA_CHIPSHMOOPHYS 0x5
+#define ATAG_NVIDIA_PRESERVED_MEM_0 0x10000
+#define ATAG_NVIDIA_PRESERVED_MEM_N 2
+#define ATAG_NVIDIA_FORCE_32 0x7fffffff
+
+struct tag_tegra {
+ __u32 bootarg_key;
+ __u32 bootarg_len;
+ char bootarg[1];
+};
+
+static int __init parse_tag_nvidia(const struct tag *tag)
+{
+
+ return 0;
+}
+__tagtable(ATAG_NVIDIA, parse_tag_nvidia);
+
+static struct tegra_utmip_config utmi_phy_config = {
+ .hssync_start_delay = 0,
+ .idle_wait_delay = 17,
+ .elastic_limit = 16,
+ .term_range_adj = 6,
+ .xcvr_setup = 9,
+ .xcvr_lsfslew = 2,
+ .xcvr_lsrslew = 2,
+};
+
+static struct tegra_ehci_platform_data tegra_ehci_pdata = {
+ .phy_config = &utmi_phy_config,
+ .operating_mode = TEGRA_USB_HOST,
+ .power_down_on_bus_suspend = 1,
+};
+
+static struct tegra_nand_chip_parms nand_chip_parms[] = {
+ /* Samsung K5E2G1GACM */
+ [0] = {
+ .vendor_id = 0xEC,
+ .device_id = 0xAA,
+ .read_id_fourth_byte = 0x15,
+ .capacity = 256,
+ .timing = {
+ .trp = 21,
+ .trh = 15,
+ .twp = 21,
+ .twh = 15,
+ .tcs = 31,
+ .twhr = 60,
+ .tcr_tar_trr = 20,
+ .twb = 100,
+ .trp_resp = 30,
+ .tadl = 100,
+ },
+ },
+ /* Hynix H5PS1GB3EFR */
+ [1] = {
+ .vendor_id = 0xAD,
+ .device_id = 0xDC,
+ .read_id_fourth_byte = 0x95,
+ .capacity = 512,
+ .timing = {
+ .trp = 12,
+ .trh = 10,
+ .twp = 12,
+ .twh = 10,
+ .tcs = 20,
+ .twhr = 80,
+ .tcr_tar_trr = 20,
+ .twb = 100,
+ .trp_resp = 20,
+ .tadl = 70,
+ },
+ },
+};
+
+struct tegra_nand_platform harmony_nand_data = {
+ .max_chips = 8,
+ .chip_parms = nand_chip_parms,
+ .nr_chip_parms = ARRAY_SIZE(nand_chip_parms),
+ .wp_gpio = TEGRA_GPIO_PC7,
+};
+
+static struct resource resources_nand[] = {
+ [0] = {
+ .start = INT_NANDFLASH,
+ .end = INT_NANDFLASH,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device tegra_nand_device = {
+ .name = "tegra_nand",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(resources_nand),
+ .resource = resources_nand,
+ .dev = {
+ .platform_data = &harmony_nand_data,
+ },
+};
+
static struct plat_serial8250_port debug_uart_platform_data[] = {
{
.membase = IO_ADDRESS(TEGRA_UARTD_BASE),
@@ -82,6 +198,43 @@ static struct platform_device harmony_audio_device = {
},
};
+static struct tegra_i2c_platform_data harmony_i2c1_platform_data = {
+ .adapter_nr = 0,
+ .bus_count = 1,
+ .bus_clk_rate = { 400000, 0 },
+};
+
+static const struct tegra_pingroup_config i2c2_ddc = {
+ .pingroup = TEGRA_PINGROUP_DDC,
+ .func = TEGRA_MUX_I2C2,
+};
+
+static const struct tegra_pingroup_config i2c2_gen2 = {
+ .pingroup = TEGRA_PINGROUP_PTA,
+ .func = TEGRA_MUX_I2C2,
+};
+
+static struct tegra_i2c_platform_data harmony_i2c2_platform_data = {
+ .adapter_nr = 1,
+ .bus_count = 2,
+ .bus_clk_rate = { 100000, 100000 },
+ .bus_mux = { &i2c2_ddc, &i2c2_gen2 },
+ .bus_mux_len = { 1, 1 },
+};
+
+static struct tegra_i2c_platform_data harmony_i2c3_platform_data = {
+ .adapter_nr = 3,
+ .bus_count = 1,
+ .bus_clk_rate = { 400000, 0 },
+};
+
+static struct tegra_i2c_platform_data harmony_dvc_platform_data = {
+ .adapter_nr = 4,
+ .bus_count = 1,
+ .bus_clk_rate = { 400000, 0 },
+ .is_dvc = true,
+};
+
static struct wm8903_platform_data harmony_wm8903_pdata = {
.irq_active_low = 0,
.micdet_cfg = 0,
@@ -104,6 +257,11 @@ static struct i2c_board_info __initdata wm8903_board_info = {
static void __init harmony_i2c_init(void)
{
+ tegra_i2c_device1.dev.platform_data = &harmony_i2c1_platform_data;
+ tegra_i2c_device2.dev.platform_data = &harmony_i2c2_platform_data;
+ tegra_i2c_device3.dev.platform_data = &harmony_i2c3_platform_data;
+ tegra_i2c_device4.dev.platform_data = &harmony_dvc_platform_data;
+
platform_device_register(&tegra_i2c_device1);
platform_device_register(&tegra_i2c_device2);
platform_device_register(&tegra_i2c_device3);
@@ -112,6 +270,52 @@ static void __init harmony_i2c_init(void)
i2c_register_board_info(0, &wm8903_board_info, 1);
}
+/* OTG gadget device */
+/*static u64 tegra_otg_dmamask = DMA_BIT_MASK(32);
+
+
+static struct resource tegra_otg_resources[] = {
+ [0] = {
+ .start = TEGRA_USB_BASE,
+ .end = TEGRA_USB_BASE + TEGRA_USB_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = INT_USB,
+ .end = INT_USB,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct fsl_usb2_platform_data tegra_otg_pdata = {
+ .operating_mode = FSL_USB2_DR_DEVICE,
+ .phy_mode = FSL_USB2_PHY_UTMI,
+};
+
+static struct platform_device tegra_otg = {
+ .name = "fsl-tegra-udc",
+ .id = -1,
+ .dev = {
+ .dma_mask = &tegra_otg_dmamask,
+ .coherent_dma_mask = 0xffffffff,
+ .platform_data = &tegra_otg_pdata,
+ },
+ .resource = tegra_otg_resources,
+ .num_resources = ARRAY_SIZE(tegra_otg_resources),
+};*/
+
+/* PDA power */
+static struct pda_power_pdata pda_power_pdata = {
+};
+
+static struct platform_device pda_power_device = {
+ .name = "pda_power",
+ .id = -1,
+ .dev = {
+ .platform_data = &pda_power_pdata,
+ },
+};
+
static struct platform_device *harmony_devices[] __initdata = {
&debug_uart,
&tegra_sdhci_device1,
@@ -121,6 +325,16 @@ static struct platform_device *harmony_devices[] __initdata = {
&tegra_das_device,
&tegra_pcm_device,
&harmony_audio_device,
+ &tegra_pmu_device,
+ &tegra_nand_device,
+ &tegra_udc_device,
+ &pda_power_device,
+ &tegra_ehci3_device,
+ &tegra_spi_device1,
+ &tegra_spi_device2,
+ &tegra_spi_device3,
+ &tegra_spi_device4,
+ &tegra_gart_device,
};
static void __init tegra_harmony_fixup(struct machine_desc *desc,
@@ -136,10 +350,11 @@ static void __init tegra_harmony_fixup(struct machine_desc *desc,
static __initdata struct tegra_clk_init_table harmony_clk_init_table[] = {
/* name parent rate enabled */
{ "uartd", "pll_p", 216000000, true },
- { "pll_a", "pll_p_out1", 56448000, true },
- { "pll_a_out0", "pll_a", 11289600, true },
- { "cdev1", NULL, 0, true },
- { "i2s1", "pll_a_out0", 11289600, false},
+ { "i2s1", "pll_a_out0", 0, false},
+ { "sdmmc1", "clk_m", 48000000, true },
+ { "sdmmc2", "clk_m", 48000000, true },
+ { "sdmmc4", "clk_m", 48000000, true },
+ { "ndflash", "pll_p", 108000000, true},
{ NULL, NULL, 0, 0},
};
@@ -163,6 +378,41 @@ static struct tegra_sdhci_platform_data sdhci_pdata4 = {
.is_8bit = 1,
};
+static int __init harmony_wifi_init(void)
+{
+ int gpio_pwr, gpio_rst;
+
+ if (!machine_is_harmony())
+ return 0;
+
+ /* WLAN - Power up (low) and Reset (low) */
+ gpio_pwr = gpio_request(TEGRA_GPIO_WLAN_PWR_LOW, "wlan_pwr");
+ gpio_rst = gpio_request(TEGRA_GPIO_WLAN_RST_LOW, "wlan_rst");
+ if (gpio_pwr < 0 || gpio_rst < 0)
+ pr_warning("Unable to get gpio for WLAN Power and Reset\n");
+ else {
+
+ tegra_gpio_enable(TEGRA_GPIO_WLAN_PWR_LOW);
+ tegra_gpio_enable(TEGRA_GPIO_WLAN_RST_LOW);
+ /* toggle in this order as per spec */
+ gpio_direction_output(TEGRA_GPIO_WLAN_PWR_LOW, 0);
+ gpio_direction_output(TEGRA_GPIO_WLAN_RST_LOW, 0);
+ udelay(5);
+ gpio_direction_output(TEGRA_GPIO_WLAN_PWR_LOW, 1);
+ gpio_direction_output(TEGRA_GPIO_WLAN_RST_LOW, 1);
+ }
+
+ return 0;
+}
+
+/*
+ * subsys_initcall_sync is good synch point to call harmony_wifi_init
+ * This makes sure that the required regulators (LDO3
+ * supply of external PMU and 1.2V regulator) are properly enabled,
+ * and mmc driver has not yet probed for a device on SDIO bus.
+ */
+subsys_initcall_sync(harmony_wifi_init);
+
static void __init tegra_harmony_init(void)
{
tegra_clk_init_from_table(harmony_clk_init_table);
@@ -173,15 +423,30 @@ static void __init tegra_harmony_init(void)
tegra_sdhci_device2.dev.platform_data = &sdhci_pdata2;
tegra_sdhci_device4.dev.platform_data = &sdhci_pdata4;
+ tegra_ehci3_device.dev.platform_data = &tegra_ehci_pdata;
+
platform_add_devices(harmony_devices, ARRAY_SIZE(harmony_devices));
harmony_i2c_init();
harmony_regulator_init();
+ harmony_panel_init();
+#ifdef CONFIG_KEYBOARD_TEGRA
+ harmony_kbc_init();
+#endif
+}
+
+void __init tegra_harmony_reserve(void)
+{
+ if (memblock_reserve(0x0, 4096) < 0)
+ pr_warn("Cannot reserve first 4K of memory for safety\n");
+
+ tegra_reserve(SZ_128M, SZ_8M, 0);
}
MACHINE_START(HARMONY, "harmony")
.boot_params = 0x00000100,
.fixup = tegra_harmony_fixup,
.map_io = tegra_map_common_io,
+ .reserve = tegra_harmony_reserve,
.init_early = tegra_init_early,
.init_irq = tegra_init_irq,
.timer = &tegra_timer,
diff --git a/arch/arm/mach-tegra/board-harmony.h b/arch/arm/mach-tegra/board-harmony.h
index d85142edaf6b..c7a081dfe3b9 100644
--- a/arch/arm/mach-tegra/board-harmony.h
+++ b/arch/arm/mach-tegra/board-harmony.h
@@ -31,8 +31,19 @@
#define TEGRA_GPIO_HP_DET TEGRA_GPIO_PW2
#define TEGRA_GPIO_INT_MIC_EN TEGRA_GPIO_PX0
#define TEGRA_GPIO_EXT_MIC_EN TEGRA_GPIO_PX1
+/* fixed voltage regulator enable/mode gpios */
+#define TPS_GPIO_EN_1V5 (HARMONY_GPIO_TPS6586X(0))
+#define TPS_GPIO_EN_1V2 (HARMONY_GPIO_TPS6586X(1))
+#define TPS_GPIO_EN_1V05 (HARMONY_GPIO_TPS6586X(2))
+#define TPS_GPIO_MODE_1V05 (HARMONY_GPIO_TPS6586X(3))
+
+/* WLAN pwr and reset gpio */
+#define TEGRA_GPIO_WLAN_PWR_LOW TEGRA_GPIO_PK5
+#define TEGRA_GPIO_WLAN_RST_LOW TEGRA_GPIO_PK6
void harmony_pinmux_init(void);
int harmony_regulator_init(void);
+int harmony_panel_init(void);
+int harmony_kbc_init(void);
#endif
diff --git a/arch/arm/mach-tegra/board-seaboard-pinmux.c b/arch/arm/mach-tegra/board-seaboard-pinmux.c
index 0bda495e9742..56dc2c279289 100644
--- a/arch/arm/mach-tegra/board-seaboard-pinmux.c
+++ b/arch/arm/mach-tegra/board-seaboard-pinmux.c
@@ -17,7 +17,6 @@
#include <linux/gpio.h>
#include <mach/pinmux.h>
-#include <mach/pinmux-t2.h>
#include "gpio-names.h"
#include "board-seaboard.h"
diff --git a/arch/arm/mach-tegra/board-ventana-memory.c b/arch/arm/mach-tegra/board-ventana-memory.c
new file mode 100644
index 000000000000..9ef7c7797341
--- /dev/null
+++ b/arch/arm/mach-tegra/board-ventana-memory.c
@@ -0,0 +1,592 @@
+/*
+ * Copyright (C) 2010-2011 NVIDIA, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+
+#include "board-ventana.h"
+#include "tegra2_emc.h"
+#include "board.h"
+
+static const struct tegra_emc_table ventana_emc_tables_elpida_300Mhz[] = {
+ {
+ .rate = 25000, /* SDRAM frquency */
+ .regs = {
+ 0x00000002, /* RC */
+ 0x00000006, /* RFC */
+ 0x00000003, /* RAS */
+ 0x00000003, /* RP */
+ 0x00000006, /* R2W */
+ 0x00000004, /* W2R */
+ 0x00000002, /* R2P */
+ 0x00000009, /* W2P */
+ 0x00000003, /* RD_RCD */
+ 0x00000003, /* WR_RCD */
+ 0x00000002, /* RRD */
+ 0x00000002, /* REXT */
+ 0x00000002, /* WDV */
+ 0x00000004, /* QUSE */
+ 0x00000003, /* QRST */
+ 0x00000008, /* QSAFE */
+ 0x0000000b, /* RDV */
+ 0x0000004d, /* REFRESH */
+ 0x00000000, /* BURST_REFRESH_NUM */
+ 0x00000003, /* PDEX2WR */
+ 0x00000003, /* PDEX2RD */
+ 0x00000003, /* PCHG2PDEN */
+ 0x00000008, /* ACT2PDEN */
+ 0x00000001, /* AR2PDEN */
+ 0x0000000a, /* RW2PDEN */
+ 0x00000004, /* TXSR */
+ 0x00000003, /* TCKE */
+ 0x00000008, /* TFAW */
+ 0x00000004, /* TRPAB */
+ 0x00000006, /* TCLKSTABLE */
+ 0x00000002, /* TCLKSTOP */
+ 0x00000068, /* TREFBW */
+ 0x00000003, /* QUSE_EXTRA */
+ 0x00000003, /* FBIO_CFG6 */
+ 0x00000000, /* ODT_WRITE */
+ 0x00000000, /* ODT_READ */
+ 0x00000082, /* FBIO_CFG5 */
+ 0xa06a04ae, /* CFG_DIG_DLL */
+ 0x0001f000, /* DLL_XFORM_DQS */
+ 0x00000000, /* DLL_XFORM_QUSE */
+ 0x00000000, /* ZCAL_REF_CNT */
+ 0x00000003, /* ZCAL_WAIT_CNT */
+ 0x00000000, /* AUTO_CAL_INTERVAL */
+ 0x00000000, /* CFG_CLKTRIM_0 */
+ 0x00000000, /* CFG_CLKTRIM_1 */
+ 0x00000000, /* CFG_CLKTRIM_2 */
+ }
+ },
+ {
+ .rate = 50000, /* SDRAM frequency */
+ .regs = {
+ 0x00000003, /* RC */
+ 0x00000007, /* RFC */
+ 0x00000003, /* RAS */
+ 0x00000003, /* RP */
+ 0x00000006, /* R2W */
+ 0x00000004, /* W2R */
+ 0x00000002, /* R2P */
+ 0x00000009, /* W2P */
+ 0x00000003, /* RD_RCD */
+ 0x00000003, /* WR_RCD */
+ 0x00000002, /* RRD */
+ 0x00000002, /* REXT */
+ 0x00000002, /* WDV */
+ 0x00000005, /* QUSE */
+ 0x00000003, /* QRST */
+ 0x00000008, /* QSAFE */
+ 0x0000000b, /* RDV */
+ 0x0000009f, /* REFRESH */
+ 0x00000000, /* BURST_REFRESH_NUM */
+ 0x00000003, /* PDEX2WR */
+ 0x00000003, /* PDEX2RD */
+ 0x00000003, /* PCHG2PDEN */
+ 0x00000008, /* ACT2PDEN */
+ 0x00000001, /* AR2PDEN */
+ 0x0000000a, /* RW2PDEN */
+ 0x00000007, /* TXSR */
+ 0x00000003, /* TCKE */
+ 0x00000008, /* TFAW */
+ 0x00000004, /* TRPAB */
+ 0x00000006, /* TCLKSTABLE */
+ 0x00000002, /* TCLKSTOP */
+ 0x000000d0, /* TREFBW */
+ 0x00000004, /* QUSE_EXTRA */
+ 0x00000000, /* FBIO_CFG6 */
+ 0x00000000, /* ODT_WRITE */
+ 0x00000000, /* ODT_READ */
+ 0x00000082, /* FBIO_CFG5 */
+ 0xa06a04ae, /* CFG_DIG_DLL */
+ 0x0001f000, /* DLL_XFORM_DQS */
+ 0x00000000, /* DLL_XFORM_QUSE */
+ 0x00000000, /* ZCAL_REF_CNT */
+ 0x00000005, /* ZCAL_WAIT_CNT */
+ 0x00000000, /* AUTO_CAL_INTERVAL */
+ 0x00000000, /* CFG_CLKTRIM_0 */
+ 0x00000000, /* CFG_CLKTRIM_1 */
+ 0x00000000, /* CFG_CLKTRIM_2 */
+ }
+ },
+ {
+ .rate = 75000, /* SDRAM frequency */
+ .regs = {
+ 0x00000005, /* RC */
+ 0x0000000a, /* RFC */
+ 0x00000004, /* RAS */
+ 0x00000003, /* RP */
+ 0x00000006, /* R2W */
+ 0x00000004, /* W2R */
+ 0x00000002, /* R2P */
+ 0x00000009, /* W2P */
+ 0x00000003, /* RD_RCD */
+ 0x00000003, /* WR_RCD */
+ 0x00000002, /* RRD */
+ 0x00000002, /* REXT */
+ 0x00000002, /* WDV */
+ 0x00000005, /* QUSE */
+ 0x00000003, /* QRST */
+ 0x00000008, /* QSAFE */
+ 0x0000000b, /* RDV */
+ 0x000000ff, /* REFRESH */
+ 0x00000000, /* BURST_REFRESH_NUM */
+ 0x00000003, /* PDEX2WR */
+ 0x00000003, /* PDEX2RD */
+ 0x00000003, /* PCHG2PDEN */
+ 0x00000008, /* ACT2PDEN */
+ 0x00000001, /* AR2PDEN */
+ 0x0000000a, /* RW2PDEN */
+ 0x0000000b, /* TXSR */
+ 0x00000003, /* TCKE */
+ 0x00000008, /* TFAW */
+ 0x00000004, /* TRPAB */
+ 0x00000006, /* TCLKSTABLE */
+ 0x00000002, /* TCLKSTOP */
+ 0x00000138, /* TREFBW */
+ 0x00000004, /* QUSE_EXTRA */
+ 0x00000000, /* FBIO_CFG6 */
+ 0x00000000, /* ODT_WRITE */
+ 0x00000000, /* ODT_READ */
+ 0x00000082, /* FBIO_CFG5 */
+ 0xa06a04ae, /* CFG_DIG_DLL */
+ 0x0001f000, /* DLL_XFORM_DQS */
+ 0x00000000, /* DLL_XFORM_QUSE */
+ 0x00000000, /* ZCAL_REF_CNT */
+ 0x00000007, /* ZCAL_WAIT_CNT */
+ 0x00000000, /* AUTO_CAL_INTERVAL */
+ 0x00000000, /* CFG_CLKTRIM_0 */
+ 0x00000000, /* CFG_CLKTRIM_1 */
+ 0x00000000, /* CFG_CLKTRIM_2 */
+ }
+ },
+ {
+ .rate = 150000, /* SDRAM frequency */
+ .regs = {
+ 0x00000009, /* RC */
+ 0x00000014, /* RFC */
+ 0x00000007, /* RAS */
+ 0x00000004, /* RP */
+ 0x00000006, /* R2W */
+ 0x00000004, /* W2R */
+ 0x00000002, /* R2P */
+ 0x00000009, /* W2P */
+ 0x00000003, /* RD_RCD */
+ 0x00000003, /* WR_RCD */
+ 0x00000002, /* RRD */
+ 0x00000002, /* REXT */
+ 0x00000002, /* WDV */
+ 0x00000005, /* QUSE */
+ 0x00000003, /* QRST */
+ 0x00000008, /* QSAFE */
+ 0x0000000b, /* RDV */
+ 0x0000021f, /* REFRESH */
+ 0x00000000, /* BURST_REFRESH_NUM */
+ 0x00000003, /* PDEX2WR */
+ 0x00000003, /* PDEX2RD */
+ 0x00000004, /* PCHG2PDEN */
+ 0x00000008, /* ACT2PDEN */
+ 0x00000001, /* AR2PDEN */
+ 0x0000000a, /* RW2PDEN */
+ 0x00000015, /* TXSR */
+ 0x00000003, /* TCKE */
+ 0x00000008, /* TFAW */
+ 0x00000004, /* TRPAB */
+ 0x00000006, /* TCLKSTABLE */
+ 0x00000002, /* TCLKSTOP */
+ 0x00000270, /* TREFBW */
+ 0x00000000, /* QUSE_EXTRA */
+ 0x00000001, /* FBIO_CFG6 */
+ 0x00000000, /* ODT_WRITE */
+ 0x00000000, /* ODT_READ */
+ 0x00000082, /* FBIO_CFG5 */
+ 0xA04C04AE, /* CFG_DIG_DLL */
+ 0x007FC010, /* DLL_XFORM_DQS */
+ 0x00000000, /* DLL_XFORM_QUSE */
+ 0x00000000, /* ZCAL_REF_CNT */
+ 0x0000000e, /* ZCAL_WAIT_CNT */
+ 0x00000000, /* AUTO_CAL_INTERVAL */
+ 0x00000000, /* CFG_CLKTRIM_0 */
+ 0x00000000, /* CFG_CLKTRIM_1 */
+ 0x00000000, /* CFG_CLKTRIM_2 */
+ }
+ },
+ {
+ .rate = 300000, /* SDRAM frequency */
+ .regs = {
+ 0x00000012, /* RC */
+ 0x00000027, /* RFC */
+ 0x0000000D, /* RAS */
+ 0x00000007, /* RP */
+ 0x00000007, /* R2W */
+ 0x00000005, /* W2R */
+ 0x00000003, /* R2P */
+ 0x00000009, /* W2P */
+ 0x00000006, /* RD_RCD */
+ 0x00000006, /* WR_RCD */
+ 0x00000003, /* RRD */
+ 0x00000003, /* REXT */
+ 0x00000002, /* WDV */
+ 0x00000006, /* QUSE */
+ 0x00000003, /* QRST */
+ 0x00000009, /* QSAFE */
+ 0x0000000c, /* RDV */
+ 0x0000045f, /* REFRESH */
+ 0x00000000, /* BURST_REFRESH_NUM */
+ 0x00000004, /* PDEX2WR */
+ 0x00000004, /* PDEX2RD */
+ 0x00000007, /* PCHG2PDEN */
+ 0x00000008, /* ACT2PDEN */
+ 0x00000001, /* AR2PDEN */
+ 0x0000000e, /* RW2PDEN */
+ 0x0000002A, /* TXSR */
+ 0x00000003, /* TCKE */
+ 0x0000000F, /* TFAW */
+ 0x00000008, /* TRPAB */
+ 0x00000005, /* TCLKSTABLE */
+ 0x00000002, /* TCLKSTOP */
+ 0x000004E1, /* TREFBW */
+ 0x00000005, /* QUSE_EXTRA */
+ 0x00000002, /* FBIO_CFG6 */
+ 0x00000000, /* ODT_WRITE */
+ 0x00000000, /* ODT_READ */
+ 0x00000282, /* FBIO_CFG5 */
+ 0xE03C048B, /* CFG_DIG_DLL */
+ 0x007FC010, /* DLL_XFORM_DQS */
+ 0x00000000, /* DLL_XFORM_QUSE */
+ 0x00000000, /* ZCAL_REF_CNT */
+ 0x0000001B, /* ZCAL_WAIT_CNT */
+ 0x00000000, /* AUTO_CAL_INTERVAL */
+ 0x00000000, /* CFG_CLKTRIM_0 */
+ 0x00000000, /* CFG_CLKTRIM_1 */
+ 0x00000000, /* CFG_CLKTRIM_2 */
+ }
+ }
+};
+
+static const struct tegra_emc_table ventana_emc_tables_elpida_400Mhz[] = {
+ {
+ .rate = 23750, /* SDRAM frquency */
+ .regs = {
+ 0x00000002, /* RC */
+ 0x00000006, /* RFC */
+ 0x00000003, /* RAS */
+ 0x00000003, /* RP */
+ 0x00000006, /* R2W */
+ 0x00000004, /* W2R */
+ 0x00000002, /* R2P */
+ 0x0000000b, /* W2P */
+ 0x00000003, /* RD_RCD */
+ 0x00000003, /* WR_RCD */
+ 0x00000002, /* RRD */
+ 0x00000002, /* REXT */
+ 0x00000003, /* WDV */
+ 0x00000005, /* QUSE */
+ 0x00000004, /* QRST */
+ 0x00000008, /* QSAFE */
+ 0x0000000c, /* RDV */
+ 0x00000047, /* REFRESH */
+ 0x00000000, /* BURST_REFRESH_NUM */
+ 0x00000003, /* PDEX2WR */
+ 0x00000003, /* PDEX2RD */
+ 0x00000003, /* PCHG2PDEN */
+ 0x00000008, /* ACT2PDEN */
+ 0x00000001, /* AR2PDEN */
+ 0x0000000b, /* RW2PDEN */
+ 0x00000004, /* TXSR */
+ 0x00000003, /* TCKE */
+ 0x00000008, /* TFAW */
+ 0x00000004, /* TRPAB */
+ 0x00000008, /* TCLKSTABLE */
+ 0x00000002, /* TCLKSTOP */
+ 0x00000060, /* TREFBW */
+ 0x00000004, /* QUSE_EXTRA */
+ 0x00000003, /* FBIO_CFG6 */
+ 0x00000000, /* ODT_WRITE */
+ 0x00000000, /* ODT_READ */
+ 0x00000082, /* FBIO_CFG5 */
+ 0xa0ae04ae, /* CFG_DIG_DLL */
+ 0x0001f800, /* DLL_XFORM_DQS */
+ 0x00000000, /* DLL_XFORM_QUSE */
+ 0x00000000, /* ZCAL_REF_CNT */
+ 0x00000003, /* ZCAL_WAIT_CNT */
+ 0x00000000, /* AUTO_CAL_INTERVAL */
+ 0x00000000, /* CFG_CLKTRIM_0 */
+ 0x00000000, /* CFG_CLKTRIM_1 */
+ 0x00000000, /* CFG_CLKTRIM_2 */
+ }
+ },
+ {
+ .rate = 63333, /* SDRAM frquency */
+ .regs = {
+ 0x00000004, /* RC */
+ 0x00000009, /* RFC */
+ 0x00000003, /* RAS */
+ 0x00000003, /* RP */
+ 0x00000006, /* R2W */
+ 0x00000004, /* W2R */
+ 0x00000002, /* R2P */
+ 0x0000000b, /* W2P */
+ 0x00000003, /* RD_RCD */
+ 0x00000003, /* WR_RCD */
+ 0x00000002, /* RRD */
+ 0x00000002, /* REXT */
+ 0x00000003, /* WDV */
+ 0x00000006, /* QUSE */
+ 0x00000004, /* QRST */
+ 0x00000008, /* QSAFE */
+ 0x0000000c, /* RDV */
+ 0x000000c4, /* REFRESH */
+ 0x00000000, /* BURST_REFRESH_NUM */
+ 0x00000003, /* PDEX2WR */
+ 0x00000003, /* PDEX2RD */
+ 0x00000003, /* PCHG2PDEN */
+ 0x00000008, /* ACT2PDEN */
+ 0x00000001, /* AR2PDEN */
+ 0x0000000b, /* RW2PDEN */
+ 0x00000009, /* TXSR */
+ 0x00000003, /* TCKE */
+ 0x00000008, /* TFAW */
+ 0x00000004, /* TRPAB */
+ 0x00000008, /* TCLKSTABLE */
+ 0x00000002, /* TCLKSTOP */
+ 0x00000107, /* TREFBW */
+ 0x00000005, /* QUSE_EXTRA */
+ 0x00000000, /* FBIO_CFG6 */
+ 0x00000000, /* ODT_WRITE */
+ 0x00000000, /* ODT_READ */
+ 0x00000082, /* FBIO_CFG5 */
+ 0xa0ae04ae, /* CFG_DIG_DLL */
+ 0x0001f800, /* DLL_XFORM_DQS */
+ 0x00000000, /* DLL_XFORM_QUSE */
+ 0x00000000, /* ZCAL_REF_CNT */
+ 0x00000006, /* ZCAL_WAIT_CNT */
+ 0x00000000, /* AUTO_CAL_INTERVAL */
+ 0x00000000, /* CFG_CLKTRIM_0 */
+ 0x00000000, /* CFG_CLKTRIM_1 */
+ 0x00000000, /* CFG_CLKTRIM_2 */
+ }
+ },
+ {
+ .rate = 95000, /* SDRAM frquency */
+ .regs = {
+ 0x00000006, /* RC */
+ 0x0000000d, /* RFC */
+ 0x00000004, /* RAS */
+ 0x00000003, /* RP */
+ 0x00000006, /* R2W */
+ 0x00000004, /* W2R */
+ 0x00000002, /* R2P */
+ 0x0000000b, /* W2P */
+ 0x00000003, /* RD_RCD */
+ 0x00000003, /* WR_RCD */
+ 0x00000002, /* RRD */
+ 0x00000002, /* REXT */
+ 0x00000003, /* WDV */
+ 0x00000006, /* QUSE */
+ 0x00000004, /* QRST */
+ 0x00000008, /* QSAFE */
+ 0x0000000c, /* RDV */
+ 0x0000013f, /* REFRESH */
+ 0x00000000, /* BURST_REFRESH_NUM */
+ 0x00000003, /* PDEX2WR */
+ 0x00000003, /* PDEX2RD */
+ 0x00000003, /* PCHG2PDEN */
+ 0x00000008, /* ACT2PDEN */
+ 0x00000001, /* AR2PDEN */
+ 0x0000000b, /* RW2PDEN */
+ 0x0000000e, /* TXSR */
+ 0x00000003, /* TCKE */
+ 0x00000008, /* TFAW */
+ 0x00000004, /* TRPAB */
+ 0x00000008, /* TCLKSTABLE */
+ 0x00000002, /* TCLKSTOP */
+ 0x0000018c, /* TREFBW */
+ 0x00000005, /* QUSE_EXTRA */
+ 0x00000001, /* FBIO_CFG6 */
+ 0x00000000, /* ODT_WRITE */
+ 0x00000000, /* ODT_READ */
+ 0x00000082, /* FBIO_CFG5 */
+ 0xa0ae04ae, /* CFG_DIG_DLL */
+ 0x0001f000, /* DLL_XFORM_DQS */
+ 0x00000000, /* DLL_XFORM_QUSE */
+ 0x00000000, /* ZCAL_REF_CNT */
+ 0x00000009, /* ZCAL_WAIT_CNT */
+ 0x00000000, /* AUTO_CAL_INTERVAL */
+ 0x00000000, /* CFG_CLKTRIM_0 */
+ 0x00000000, /* CFG_CLKTRIM_1 */
+ 0x00000000, /* CFG_CLKTRIM_2 */
+ }
+ },
+ {
+ .rate = 190000, /* SDRAM frquency */
+ .regs = {
+ 0x0000000c, /* RC */
+ 0x00000019, /* RFC */
+ 0x00000008, /* RAS */
+ 0x00000004, /* RP */
+ 0x00000007, /* R2W */
+ 0x00000004, /* W2R */
+ 0x00000002, /* R2P */
+ 0x0000000b, /* W2P */
+ 0x00000004, /* RD_RCD */
+ 0x00000004, /* WR_RCD */
+ 0x00000002, /* RRD */
+ 0x00000003, /* REXT */
+ 0x00000003, /* WDV */
+ 0x00000006, /* QUSE */
+ 0x00000004, /* QRST */
+ 0x00000009, /* QSAFE */
+ 0x0000000d, /* RDV */
+ 0x000002bf, /* REFRESH */
+ 0x00000000, /* BURST_REFRESH_NUM */
+ 0x00000003, /* PDEX2WR */
+ 0x00000003, /* PDEX2RD */
+ 0x00000004, /* PCHG2PDEN */
+ 0x00000008, /* ACT2PDEN */
+ 0x00000001, /* AR2PDEN */
+ 0x0000000c, /* RW2PDEN */
+ 0x0000001b, /* TXSR */
+ 0x00000003, /* TCKE */
+ 0x0000000a, /* TFAW */
+ 0x00000004, /* TRPAB */
+ 0x00000008, /* TCLKSTABLE */
+ 0x00000002, /* TCLKSTOP */
+ 0x00000317, /* TREFBW */
+ 0x00000005, /* QUSE_EXTRA */
+ 0x00000002, /* FBIO_CFG6 */
+ 0x00000000, /* ODT_WRITE */
+ 0x00000000, /* ODT_READ */
+ 0x00000082, /* FBIO_CFG5 */
+ 0xa06204ae, /* CFG_DIG_DLL */
+ 0x007f7010, /* DLL_XFORM_DQS */
+ 0x00000000, /* DLL_XFORM_QUSE */
+ 0x00000000, /* ZCAL_REF_CNT */
+ 0x00000012, /* ZCAL_WAIT_CNT */
+ 0x00000000, /* AUTO_CAL_INTERVAL */
+ 0x00000000, /* CFG_CLKTRIM_0 */
+ 0x00000000, /* CFG_CLKTRIM_1 */
+ 0x00000000, /* CFG_CLKTRIM_2 */
+ }
+ },
+ {
+ .rate = 380000, /* SDRAM frquency */
+ .regs = {
+ 0x00000017, /* RC */
+ 0x00000032, /* RFC */
+ 0x00000010, /* RAS */
+ 0x00000007, /* RP */
+ 0x00000008, /* R2W */
+ 0x00000005, /* W2R */
+ 0x00000003, /* R2P */
+ 0x0000000b, /* W2P */
+ 0x00000007, /* RD_RCD */
+ 0x00000007, /* WR_RCD */
+ 0x00000004, /* RRD */
+ 0x00000003, /* REXT */
+ 0x00000003, /* WDV */
+ 0x00000007, /* QUSE */
+ 0x00000004, /* QRST */
+ 0x0000000a, /* QSAFE */
+ 0x0000000e, /* RDV */
+ 0x0000059f, /* REFRESH */
+ 0x00000000, /* BURST_REFRESH_NUM */
+ 0x00000004, /* PDEX2WR */
+ 0x00000004, /* PDEX2RD */
+ 0x00000007, /* PCHG2PDEN */
+ 0x00000008, /* ACT2PDEN */
+ 0x00000001, /* AR2PDEN */
+ 0x00000011, /* RW2PDEN */
+ 0x00000036, /* TXSR */
+ 0x00000003, /* TCKE */
+ 0x00000013, /* TFAW */
+ 0x00000008, /* TRPAB */
+ 0x00000007, /* TCLKSTABLE */
+ 0x00000002, /* TCLKSTOP */
+ 0x0000062d, /* TREFBW */
+ 0x00000006, /* QUSE_EXTRA */
+ 0x00000003, /* FBIO_CFG6 */
+ 0x00000000, /* ODT_WRITE */
+ 0x00000000, /* ODT_READ */
+ 0x00000282, /* FBIO_CFG5 */
+ 0xe044048b, /* CFG_DIG_DLL */
+ 0x007fb010, /* DLL_XFORM_DQS */
+ 0x00000000, /* DLL_XFORM_QUSE */
+ 0x00000000, /* ZCAL_REF_CNT */
+ 0x00000023, /* ZCAL_WAIT_CNT */
+ 0x00000000, /* AUTO_CAL_INTERVAL */
+ 0x00000000, /* CFG_CLKTRIM_0 */
+ 0x00000000, /* CFG_CLKTRIM_1 */
+ 0x00000000, /* CFG_CLKTRIM_2 */
+ }
+ }
+};
+
+static const struct tegra_emc_chip ventana_emc_chips[] = {
+ {
+ .description = "Elpida 300MHz",
+ .mem_manufacturer_id = 0x0303,
+ .mem_revision_id1 = -1,
+ .mem_revision_id2 = -1,
+ .mem_pid = -1,
+ .table = ventana_emc_tables_elpida_300Mhz,
+ .table_size = ARRAY_SIZE(ventana_emc_tables_elpida_300Mhz)
+ },
+};
+
+static const struct tegra_emc_chip ventana_t25_emc_chips[] = {
+ {
+ .description = "Elpida 400MHz",
+ .mem_manufacturer_id = 0x0303,
+ .mem_revision_id1 = -1,
+ .mem_revision_id2 = -1,
+ .mem_pid = -1,
+ .table = ventana_emc_tables_elpida_400Mhz,
+ .table_size = ARRAY_SIZE(ventana_emc_tables_elpida_400Mhz)
+ },
+};
+
+static const struct tegra_emc_chip ventana_siblings_emc_chips[] = {
+};
+
+#define TEGRA25_SKU 0x0B00
+#define board_is_ventana(bi) (bi.board_id == 0x24b || bi.board_id == 0x252)
+
+int ventana_emc_init(void)
+{
+ struct board_info BoardInfo;
+
+ tegra_get_board_info(&BoardInfo);
+
+ if (board_is_ventana(BoardInfo)) {
+ if (BoardInfo.sku == TEGRA25_SKU)
+ tegra_init_emc(ventana_t25_emc_chips,
+ ARRAY_SIZE(ventana_t25_emc_chips));
+ else
+ tegra_init_emc(ventana_emc_chips,
+ ARRAY_SIZE(ventana_emc_chips));
+ } else {
+ pr_info("ventana_emc_init: using ventana_siblings_emc_chips\n");
+ tegra_init_emc(ventana_siblings_emc_chips,
+ ARRAY_SIZE(ventana_siblings_emc_chips));
+ }
+
+ return 0;
+}
diff --git a/arch/arm/mach-tegra/board-ventana-panel.c b/arch/arm/mach-tegra/board-ventana-panel.c
new file mode 100644
index 000000000000..749240687735
--- /dev/null
+++ b/arch/arm/mach-tegra/board-ventana-panel.c
@@ -0,0 +1,438 @@
+/*
+ * arch/arm/mach-tegra/board-ventana-panel.c
+ *
+ * Copyright (c) 2010-2011, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/resource.h>
+#include <asm/mach-types.h>
+#include <linux/platform_device.h>
+#include <linux/earlysuspend.h>
+#include <linux/pwm_backlight.h>
+#include <linux/nvhost.h>
+#include <mach/nvmap.h>
+#include <mach/irqs.h>
+#include <mach/iomap.h>
+#include <mach/dc.h>
+#include <mach/fb.h>
+
+#include "devices.h"
+#include "gpio-names.h"
+#include "board.h"
+
+#define ventana_pnl_pwr_enb TEGRA_GPIO_PC6
+#define ventana_bl_enb TEGRA_GPIO_PD4
+#define ventana_lvds_shutdown TEGRA_GPIO_PB2
+#define ventana_hdmi_hpd TEGRA_GPIO_PN7
+#define ventana_hdmi_enb TEGRA_GPIO_PV5
+
+/*panel power on sequence timing*/
+#define ventana_pnl_to_lvds_ms 0
+#define ventana_lvds_to_bl_ms 200
+
+#ifdef CONFIG_TEGRA_DC
+static struct regulator *ventana_hdmi_reg = NULL;
+static struct regulator *ventana_hdmi_pll = NULL;
+#endif
+
+static int ventana_backlight_init(struct device *dev) {
+ int ret;
+
+ ret = gpio_request(ventana_bl_enb, "backlight_enb");
+ if (ret < 0)
+ return ret;
+
+ ret = gpio_direction_output(ventana_bl_enb, 1);
+ if (ret < 0)
+ gpio_free(ventana_bl_enb);
+ else
+ tegra_gpio_enable(ventana_bl_enb);
+
+ return ret;
+};
+
+static void ventana_backlight_exit(struct device *dev) {
+ gpio_set_value(ventana_bl_enb, 0);
+ gpio_free(ventana_bl_enb);
+ tegra_gpio_disable(ventana_bl_enb);
+}
+
+static int ventana_backlight_notify(struct device *unused, int brightness)
+{
+ gpio_set_value(ventana_bl_enb, !!brightness);
+ return brightness;
+}
+
+static int ventana_disp1_check_fb(struct device *dev, struct fb_info *info);
+
+static struct platform_pwm_backlight_data ventana_backlight_data = {
+ .pwm_id = 2,
+ .max_brightness = 255,
+ .dft_brightness = 224,
+ .pwm_period_ns = 5000000,
+ .init = ventana_backlight_init,
+ .exit = ventana_backlight_exit,
+ .notify = ventana_backlight_notify,
+ /* Only toggle backlight on fb blank notifications for disp1 */
+ .check_fb = ventana_disp1_check_fb,
+};
+
+static struct platform_device ventana_backlight_device = {
+ .name = "pwm-backlight",
+ .id = -1,
+ .dev = {
+ .platform_data = &ventana_backlight_data,
+ },
+};
+
+#ifdef CONFIG_TEGRA_DC
+static int ventana_panel_enable(void)
+{
+ struct regulator *reg = regulator_get(NULL, "vdd_ldo4");
+
+ if (!reg) {
+ regulator_enable(reg);
+ regulator_put(reg);
+ }
+
+ gpio_set_value(ventana_pnl_pwr_enb, 1);
+ mdelay(ventana_pnl_to_lvds_ms);
+ gpio_set_value(ventana_lvds_shutdown, 1);
+ mdelay(ventana_lvds_to_bl_ms);
+ return 0;
+}
+
+static int ventana_panel_disable(void)
+{
+ gpio_set_value(ventana_lvds_shutdown, 0);
+ gpio_set_value(ventana_pnl_pwr_enb, 0);
+ return 0;
+}
+
+static int ventana_hdmi_enable(void)
+{
+ if (!ventana_hdmi_reg) {
+ ventana_hdmi_reg = regulator_get(NULL, "avdd_hdmi"); /* LD07 */
+ if (IS_ERR_OR_NULL(ventana_hdmi_reg)) {
+ pr_err("hdmi: couldn't get regulator avdd_hdmi\n");
+ ventana_hdmi_reg = NULL;
+ return PTR_ERR(ventana_hdmi_reg);
+ }
+ }
+ regulator_enable(ventana_hdmi_reg);
+
+ if (!ventana_hdmi_pll) {
+ ventana_hdmi_pll = regulator_get(NULL, "avdd_hdmi_pll"); /* LD08 */
+ if (IS_ERR_OR_NULL(ventana_hdmi_pll)) {
+ pr_err("hdmi: couldn't get regulator avdd_hdmi_pll\n");
+ ventana_hdmi_pll = NULL;
+ regulator_disable(ventana_hdmi_reg);
+ ventana_hdmi_reg = NULL;
+ return PTR_ERR(ventana_hdmi_pll);
+ }
+ }
+ regulator_enable(ventana_hdmi_pll);
+ return 0;
+}
+
+static int ventana_hdmi_disable(void)
+{
+ regulator_disable(ventana_hdmi_reg);
+ regulator_disable(ventana_hdmi_pll);
+ return 0;
+}
+
+static struct resource ventana_disp1_resources[] = {
+ {
+ .name = "irq",
+ .start = INT_DISPLAY_GENERAL,
+ .end = INT_DISPLAY_GENERAL,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "regs",
+ .start = TEGRA_DISPLAY_BASE,
+ .end = TEGRA_DISPLAY_BASE + TEGRA_DISPLAY_SIZE-1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .name = "fbmem",
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct resource ventana_disp2_resources[] = {
+ {
+ .name = "irq",
+ .start = INT_DISPLAY_B_GENERAL,
+ .end = INT_DISPLAY_B_GENERAL,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "regs",
+ .start = TEGRA_DISPLAY2_BASE,
+ .end = TEGRA_DISPLAY2_BASE + TEGRA_DISPLAY2_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .name = "fbmem",
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .name = "hdmi_regs",
+ .start = TEGRA_HDMI_BASE,
+ .end = TEGRA_HDMI_BASE + TEGRA_HDMI_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct tegra_dc_mode ventana_panel_modes[] = {
+ {
+ .pclk = 72072000,
+ .h_ref_to_sync = 11,
+ .v_ref_to_sync = 1,
+ .h_sync_width = 58,
+ .v_sync_width = 4,
+ .h_back_porch = 58,
+ .v_back_porch = 4,
+ .h_active = 1366,
+ .v_active = 768,
+ .h_front_porch = 58,
+ .v_front_porch = 4,
+ },
+};
+
+static struct tegra_fb_data ventana_fb_data = {
+ .win = 0,
+ .xres = 1366,
+ .yres = 768,
+ .bits_per_pixel = 32,
+ .flags = TEGRA_FB_FLIP_ON_PROBE,
+};
+
+static struct tegra_fb_data ventana_hdmi_fb_data = {
+ .win = 0,
+ .xres = 1366,
+ .yres = 768,
+ .bits_per_pixel = 32,
+ .flags = TEGRA_FB_FLIP_ON_PROBE,
+};
+
+static struct tegra_dc_out ventana_disp1_out = {
+ .type = TEGRA_DC_OUT_RGB,
+
+ .align = TEGRA_DC_ALIGN_MSB,
+ .order = TEGRA_DC_ORDER_RED_BLUE,
+ .depth = 18,
+ .dither = TEGRA_DC_ORDERED_DITHER,
+
+ .modes = ventana_panel_modes,
+ .n_modes = ARRAY_SIZE(ventana_panel_modes),
+
+ .enable = ventana_panel_enable,
+ .disable = ventana_panel_disable,
+};
+
+static struct tegra_dc_out ventana_disp2_out = {
+ .type = TEGRA_DC_OUT_HDMI,
+ .flags = TEGRA_DC_OUT_HOTPLUG_HIGH,
+
+ .dcc_bus = 1,
+ .hotplug_gpio = ventana_hdmi_hpd,
+
+ .max_pixclock = KHZ2PICOS(148500),
+
+ .align = TEGRA_DC_ALIGN_MSB,
+ .order = TEGRA_DC_ORDER_RED_BLUE,
+
+ .enable = ventana_hdmi_enable,
+ .disable = ventana_hdmi_disable,
+};
+
+static struct tegra_dc_platform_data ventana_disp1_pdata = {
+ .flags = TEGRA_DC_FLAG_ENABLED,
+ .default_out = &ventana_disp1_out,
+ .fb = &ventana_fb_data,
+};
+
+static struct tegra_dc_platform_data ventana_disp2_pdata = {
+ .flags = 0,
+ .default_out = &ventana_disp2_out,
+ .fb = &ventana_hdmi_fb_data,
+};
+
+static struct nvhost_device ventana_disp1_device = {
+ .name = "tegradc",
+ .id = 0,
+ .resource = ventana_disp1_resources,
+ .num_resources = ARRAY_SIZE(ventana_disp1_resources),
+ .dev = {
+ .platform_data = &ventana_disp1_pdata,
+ },
+};
+
+static int ventana_disp1_check_fb(struct device *dev, struct fb_info *info)
+{
+ return info->device == &ventana_disp1_device.dev;
+}
+
+static struct nvhost_device ventana_disp2_device = {
+ .name = "tegradc",
+ .id = 1,
+ .resource = ventana_disp2_resources,
+ .num_resources = ARRAY_SIZE(ventana_disp2_resources),
+ .dev = {
+ .platform_data = &ventana_disp2_pdata,
+ },
+};
+#else
+static int ventana_disp1_check_fb(struct device *dev, struct fb_info *info)
+{
+ return 0;
+}
+#endif
+
+static struct nvmap_platform_carveout ventana_carveouts[] = {
+ [0] = NVMAP_HEAP_CARVEOUT_IRAM_INIT,
+ [1] = {
+ .name = "generic-0",
+ .usage_mask = NVMAP_HEAP_CARVEOUT_GENERIC,
+ .buddy_size = SZ_32K,
+ },
+};
+
+static struct nvmap_platform_data ventana_nvmap_data = {
+ .carveouts = ventana_carveouts,
+ .nr_carveouts = ARRAY_SIZE(ventana_carveouts),
+};
+
+static struct platform_device ventana_nvmap_device = {
+ .name = "tegra-nvmap",
+ .id = -1,
+ .dev = {
+ .platform_data = &ventana_nvmap_data,
+ },
+};
+
+static struct platform_device *ventana_gfx_devices[] __initdata = {
+ &ventana_nvmap_device,
+#ifdef CONFIG_TEGRA_GRHOST
+ &tegra_grhost_device,
+#endif
+ &tegra_pwfm2_device,
+ &ventana_backlight_device,
+};
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+/* put early_suspend/late_resume handlers here for the display in order
+ * to keep the code out of the display driver, keeping it closer to upstream
+ */
+struct early_suspend ventana_panel_early_suspender;
+
+static void ventana_panel_early_suspend(struct early_suspend *h)
+{
+ unsigned i;
+
+ /* power down LCD, add use a black screen for HDMI */
+ if (num_registered_fb > 0)
+ fb_blank(registered_fb[0], FB_BLANK_POWERDOWN);
+ if (num_registered_fb > 1)
+ fb_blank(registered_fb[1], FB_BLANK_NORMAL);
+#ifdef CONFIG_TEGRA_CONVSERVATIVE_GOV_ON_EARLYSUPSEND
+ cpufreq_save_default_governor();
+ cpufreq_set_conservative_governor();
+ cpufreq_set_conservative_governor_param(
+ SET_CONSERVATIVE_GOVERNOR_UP_THRESHOLD,
+ SET_CONSERVATIVE_GOVERNOR_DOWN_THRESHOLD);
+#endif
+}
+
+static void ventana_panel_late_resume(struct early_suspend *h)
+{
+ unsigned i;
+#ifdef CONFIG_TEGRA_CONVSERVATIVE_GOV_ON_EARLYSUPSEND
+ cpufreq_restore_default_governor();
+#endif
+ for (i = 0; i < num_registered_fb; i++)
+ fb_blank(registered_fb[i], FB_BLANK_UNBLANK);
+}
+#endif
+
+int __init ventana_panel_init(void)
+{
+ int err;
+ struct resource __maybe_unused *res;
+
+ gpio_request(ventana_pnl_pwr_enb, "pnl_pwr_enb");
+ gpio_direction_output(ventana_pnl_pwr_enb, 1);
+ tegra_gpio_enable(ventana_pnl_pwr_enb);
+
+ gpio_request(ventana_lvds_shutdown, "lvds_shdn");
+ gpio_direction_output(ventana_lvds_shutdown, 1);
+ tegra_gpio_enable(ventana_lvds_shutdown);
+
+ tegra_gpio_enable(ventana_hdmi_enb);
+ gpio_request(ventana_hdmi_enb, "hdmi_5v_en");
+ gpio_direction_output(ventana_hdmi_enb, 1);
+
+ tegra_gpio_enable(ventana_hdmi_hpd);
+ gpio_request(ventana_hdmi_hpd, "hdmi_hpd");
+ gpio_direction_input(ventana_hdmi_hpd);
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ ventana_panel_early_suspender.suspend = ventana_panel_early_suspend;
+ ventana_panel_early_suspender.resume = ventana_panel_late_resume;
+ ventana_panel_early_suspender.level = EARLY_SUSPEND_LEVEL_DISABLE_FB;
+ register_early_suspend(&ventana_panel_early_suspender);
+#endif
+
+ ventana_carveouts[1].base = tegra_carveout_start;
+ ventana_carveouts[1].size = tegra_carveout_size;
+
+ err = platform_add_devices(ventana_gfx_devices,
+ ARRAY_SIZE(ventana_gfx_devices));
+
+#if defined(CONFIG_TEGRA_GRHOST) && defined(CONFIG_TEGRA_DC)
+ res = nvhost_get_resource_byname(&ventana_disp1_device,
+ IORESOURCE_MEM, "fbmem");
+ res->start = tegra_fb_start;
+ res->end = tegra_fb_start + tegra_fb_size - 1;
+
+ res = nvhost_get_resource_byname(&ventana_disp2_device,
+ IORESOURCE_MEM, "fbmem");
+ res->start = tegra_fb2_start;
+ res->end = tegra_fb2_start + tegra_fb2_size - 1;
+#endif
+
+ /* Copy the bootloader fb to the fb. */
+ tegra_move_framebuffer(tegra_fb_start, tegra_bootloader_fb_start,
+ min(tegra_fb_size, tegra_bootloader_fb_size));
+
+#if defined(CONFIG_TEGRA_GRHOST) && defined(CONFIG_TEGRA_DC)
+ if (!err)
+ err = nvhost_device_register(&ventana_disp1_device);
+
+ if (!err)
+ err = nvhost_device_register(&ventana_disp2_device);
+#endif
+
+ return err;
+}
+
diff --git a/arch/arm/mach-tegra/board-ventana-pinmux.c b/arch/arm/mach-tegra/board-ventana-pinmux.c
new file mode 100644
index 000000000000..9f3447264599
--- /dev/null
+++ b/arch/arm/mach-tegra/board-ventana-pinmux.c
@@ -0,0 +1,194 @@
+/*
+ * arch/arm/mach-tegra/board-ventana-pinmux.c
+ *
+ * Copyright (C) 2010-2011 NVIDIA Corporation
+ *
+ * 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/init.h>
+#include <linux/gpio.h>
+#include <mach/pinmux.h>
+
+#include "board-ventana.h"
+#include "gpio-names.h"
+
+#define DEFAULT_DRIVE(_name) \
+ { \
+ .pingroup = TEGRA_DRIVE_PINGROUP_##_name, \
+ .hsm = TEGRA_HSM_DISABLE, \
+ .schmitt = TEGRA_SCHMITT_ENABLE, \
+ .drive = TEGRA_DRIVE_DIV_1, \
+ .pull_down = TEGRA_PULL_31, \
+ .pull_up = TEGRA_PULL_31, \
+ .slew_rising = TEGRA_SLEW_SLOWEST, \
+ .slew_falling = TEGRA_SLEW_SLOWEST, \
+ }
+
+#define SET_DRIVE(_name, _hsm, _schmitt, _drive, _pulldn_drive, _pullup_drive, _pulldn_slew, _pullup_slew) \
+ { \
+ .pingroup = TEGRA_DRIVE_PINGROUP_##_name, \
+ .hsm = TEGRA_HSM_##_hsm, \
+ .schmitt = TEGRA_SCHMITT_##_schmitt, \
+ .drive = TEGRA_DRIVE_##_drive, \
+ .pull_down = TEGRA_PULL_##_pulldn_drive, \
+ .pull_up = TEGRA_PULL_##_pullup_drive, \
+ .slew_rising = TEGRA_SLEW_##_pulldn_slew, \
+ .slew_falling = TEGRA_SLEW_##_pullup_slew, \
+ }
+
+static __initdata struct tegra_drive_pingroup_config ventana_drive_pinmux[] = {
+ DEFAULT_DRIVE(DDC),
+ DEFAULT_DRIVE(VI1),
+ DEFAULT_DRIVE(SDIO1),
+
+ SET_DRIVE(DBG, DISABLE, ENABLE, DIV_1, 31, 31, FASTEST, FASTEST),
+ SET_DRIVE(VI2, DISABLE, ENABLE, DIV_1, 31, 31, FASTEST, FASTEST),
+ SET_DRIVE(AT1, DISABLE, ENABLE, DIV_1, 31, 31, FASTEST, FASTEST),
+ SET_DRIVE(AO1, DISABLE, ENABLE, DIV_1, 31, 31, FASTEST, FASTEST),
+};
+
+static __initdata struct tegra_pingroup_config ventana_pinmux[] = {
+ {TEGRA_PINGROUP_ATA, TEGRA_MUX_IDE, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_ATB, TEGRA_MUX_SDIO4, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_ATC, TEGRA_MUX_NAND, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_ATD, TEGRA_MUX_GMI, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_ATE, TEGRA_MUX_GMI, TEGRA_PUPD_NORMAL, TEGRA_TRI_TRISTATE},
+ {TEGRA_PINGROUP_CDEV1, TEGRA_MUX_PLLA_OUT, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_CDEV2, TEGRA_MUX_PLLP_OUT4, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_CRTP, TEGRA_MUX_CRT, TEGRA_PUPD_PULL_UP, TEGRA_TRI_TRISTATE},
+ {TEGRA_PINGROUP_CSUS, TEGRA_MUX_VI_SENSOR_CLK, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_DAP1, TEGRA_MUX_DAP1, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_DAP2, TEGRA_MUX_DAP2, TEGRA_PUPD_NORMAL, TEGRA_TRI_TRISTATE},
+ {TEGRA_PINGROUP_DAP3, TEGRA_MUX_DAP3, TEGRA_PUPD_NORMAL, TEGRA_TRI_TRISTATE},
+ {TEGRA_PINGROUP_DAP4, TEGRA_MUX_DAP4, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_DDC, TEGRA_MUX_RSVD, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_DTA, TEGRA_MUX_VI, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_DTB, TEGRA_MUX_VI, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_DTC, TEGRA_MUX_VI, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_DTD, TEGRA_MUX_VI, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_DTE, TEGRA_MUX_VI, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_DTF, TEGRA_MUX_I2C3, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_GMA, TEGRA_MUX_SDIO4, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_GMB, TEGRA_MUX_GMI, TEGRA_PUPD_PULL_UP, TEGRA_TRI_TRISTATE},
+ {TEGRA_PINGROUP_GMC, TEGRA_MUX_UARTD, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_GMD, TEGRA_MUX_SFLASH, TEGRA_PUPD_NORMAL, TEGRA_TRI_TRISTATE},
+ {TEGRA_PINGROUP_GME, TEGRA_MUX_SDIO4, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_GPU, TEGRA_MUX_PWM, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_GPU7, TEGRA_MUX_RTCK, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_GPV, TEGRA_MUX_PCIE, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_HDINT, TEGRA_MUX_HDMI, TEGRA_PUPD_NORMAL, TEGRA_TRI_TRISTATE},
+ {TEGRA_PINGROUP_I2CP, TEGRA_MUX_I2C, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_IRRX, TEGRA_MUX_UARTB, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_IRTX, TEGRA_MUX_UARTB, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_KBCA, TEGRA_MUX_KBC, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_KBCB, TEGRA_MUX_KBC, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_KBCC, TEGRA_MUX_KBC, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_KBCD, TEGRA_MUX_KBC, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_KBCE, TEGRA_MUX_KBC, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_KBCF, TEGRA_MUX_KBC, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LCSN, TEGRA_MUX_RSVD, TEGRA_PUPD_NORMAL, TEGRA_TRI_TRISTATE},
+ {TEGRA_PINGROUP_LD0, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LD1, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LD10, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LD11, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LD12, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LD13, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LD14, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LD15, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LD16, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LD17, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LD2, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LD3, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LD4, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LD5, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LD6, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LD7, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LD8, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LD9, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LDC, TEGRA_MUX_RSVD, TEGRA_PUPD_NORMAL, TEGRA_TRI_TRISTATE},
+ {TEGRA_PINGROUP_LDI, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LHP0, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LHP1, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LHP2, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LHS, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LM0, TEGRA_MUX_RSVD, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LM1, TEGRA_MUX_RSVD, TEGRA_PUPD_NORMAL, TEGRA_TRI_TRISTATE},
+ {TEGRA_PINGROUP_LPP, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LPW0, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LPW1, TEGRA_MUX_RSVD, TEGRA_PUPD_NORMAL, TEGRA_TRI_TRISTATE},
+ {TEGRA_PINGROUP_LPW2, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LSC0, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LSC1, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LSCK, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_NORMAL, TEGRA_TRI_TRISTATE},
+ {TEGRA_PINGROUP_LSDA, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_NORMAL, TEGRA_TRI_TRISTATE},
+ {TEGRA_PINGROUP_LSDI, TEGRA_MUX_RSVD, TEGRA_PUPD_NORMAL, TEGRA_TRI_TRISTATE},
+ {TEGRA_PINGROUP_LSPI, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LVP0, TEGRA_MUX_RSVD, TEGRA_PUPD_NORMAL, TEGRA_TRI_TRISTATE},
+ {TEGRA_PINGROUP_LVP1, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LVS, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_OWC, TEGRA_MUX_RSVD, TEGRA_PUPD_NORMAL, TEGRA_TRI_TRISTATE},
+ {TEGRA_PINGROUP_PMC, TEGRA_MUX_PWR_ON, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_PTA, TEGRA_MUX_RSVD, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_RM, TEGRA_MUX_I2C, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_SDB, TEGRA_MUX_SDIO3, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_SDC, TEGRA_MUX_SDIO3, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_SDD, TEGRA_MUX_SDIO3, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_SDIO1, TEGRA_MUX_SDIO1, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_SLXA, TEGRA_MUX_PCIE, TEGRA_PUPD_PULL_UP, TEGRA_TRI_TRISTATE},
+ {TEGRA_PINGROUP_SLXC, TEGRA_MUX_SDIO3, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_SLXD, TEGRA_MUX_SPDIF, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_SLXK, TEGRA_MUX_SDIO3, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_SPDI, TEGRA_MUX_RSVD, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_SPDO, TEGRA_MUX_RSVD, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_SPIA, TEGRA_MUX_GMI, TEGRA_PUPD_PULL_UP, TEGRA_TRI_TRISTATE},
+ {TEGRA_PINGROUP_SPIB, TEGRA_MUX_GMI, TEGRA_PUPD_NORMAL, TEGRA_TRI_TRISTATE},
+ {TEGRA_PINGROUP_SPIC, TEGRA_MUX_GMI, TEGRA_PUPD_NORMAL, TEGRA_TRI_TRISTATE},
+ {TEGRA_PINGROUP_SPID, TEGRA_MUX_SPI1, TEGRA_PUPD_NORMAL, TEGRA_TRI_TRISTATE},
+ {TEGRA_PINGROUP_SPIE, TEGRA_MUX_SPI1, TEGRA_PUPD_NORMAL, TEGRA_TRI_TRISTATE},
+ {TEGRA_PINGROUP_SPIF, TEGRA_MUX_SPI1, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_TRISTATE},
+ {TEGRA_PINGROUP_SPIG, TEGRA_MUX_SPI2_ALT, TEGRA_PUPD_PULL_UP, TEGRA_TRI_TRISTATE},
+ {TEGRA_PINGROUP_SPIH, TEGRA_MUX_SPI2_ALT, TEGRA_PUPD_PULL_UP, TEGRA_TRI_TRISTATE},
+ {TEGRA_PINGROUP_UAA, TEGRA_MUX_ULPI, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_UAB, TEGRA_MUX_ULPI, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_UAC, TEGRA_MUX_RSVD, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_UAD, TEGRA_MUX_IRDA, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_UCA, TEGRA_MUX_UARTC, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_UCB, TEGRA_MUX_UARTC, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_UDA, TEGRA_MUX_ULPI, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_CK32, TEGRA_MUX_NONE, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_DDRC, TEGRA_MUX_NONE, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_PMCA, TEGRA_MUX_NONE, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_PMCB, TEGRA_MUX_NONE, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_PMCC, TEGRA_MUX_NONE, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_PMCD, TEGRA_MUX_NONE, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_PMCE, TEGRA_MUX_NONE, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_XM2C, TEGRA_MUX_NONE, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_XM2D, TEGRA_MUX_NONE, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+};
+
+static struct tegra_gpio_table gpio_table[] = {
+ { .gpio = TEGRA_GPIO_CDC_IRQ, .enable = true },
+ { .gpio = TEGRA_GPIO_HP_DET, .enable = true },
+ { .gpio = TEGRA_GPIO_INT_MIC_EN, .enable = true },
+ { .gpio = TEGRA_GPIO_EXT_MIC_EN, .enable = true },
+};
+
+int __init ventana_pinmux_init(void)
+{
+ tegra_pinmux_config_table(ventana_pinmux, ARRAY_SIZE(ventana_pinmux));
+ tegra_drive_pinmux_config_table(ventana_drive_pinmux,
+ ARRAY_SIZE(ventana_drive_pinmux));
+
+ tegra_gpio_config(gpio_table, ARRAY_SIZE(gpio_table));
+ return 0;
+}
diff --git a/arch/arm/mach-tegra/board-ventana-power.c b/arch/arm/mach-tegra/board-ventana-power.c
new file mode 100644
index 000000000000..6d8ea8db3894
--- /dev/null
+++ b/arch/arm/mach-tegra/board-ventana-power.c
@@ -0,0 +1,320 @@
+/*
+ * Copyright (C) 2010-2011 NVIDIA, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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/i2c.h>
+#include <linux/pda_power.h>
+#include <linux/platform_device.h>
+#include <linux/resource.h>
+#include <linux/regulator/machine.h>
+#include <linux/mfd/tps6586x.h>
+#include <linux/gpio.h>
+#include <linux/io.h>
+#include <linux/power/gpio-charger.h>
+
+#include <mach/iomap.h>
+#include <mach/irqs.h>
+
+#include <generated/mach-types.h>
+
+#include "gpio-names.h"
+#include "fuse.h"
+#include "pm.h"
+#include "wakeups-t2.h"
+#include "board.h"
+#include "board-ventana.h"
+
+#define PMC_CTRL 0x0
+#define PMC_CTRL_INTR_LOW (1 << 17)
+
+#define CHARGING_DISABLE TEGRA_GPIO_PR6
+
+int __init ventana_charge_init(void)
+{
+ gpio_request(CHARGING_DISABLE, "chg_disable");
+ gpio_direction_output(CHARGING_DISABLE, 0);
+ tegra_gpio_enable(CHARGING_DISABLE);
+ return 0;
+}
+
+static struct regulator_consumer_supply tps658621_sm0_supply[] = {
+ REGULATOR_SUPPLY("vdd_core", NULL),
+};
+static struct regulator_consumer_supply tps658621_sm1_supply[] = {
+ REGULATOR_SUPPLY("vdd_cpu", NULL),
+};
+static struct regulator_consumer_supply tps658621_sm2_supply[] = {
+ REGULATOR_SUPPLY("vdd_sm2", NULL),
+};
+static struct regulator_consumer_supply tps658621_ldo0_supply[] = {
+ REGULATOR_SUPPLY("vdd_ldo0", NULL),
+ REGULATOR_SUPPLY("p_cam_avdd", NULL),
+};
+static struct regulator_consumer_supply tps658621_ldo1_supply[] = {
+ REGULATOR_SUPPLY("vdd_ldo1", NULL),
+ REGULATOR_SUPPLY("avdd_pll", NULL),
+};
+static struct regulator_consumer_supply tps658621_ldo2_supply[] = {
+ REGULATOR_SUPPLY("vdd_ldo2", NULL),
+ REGULATOR_SUPPLY("vdd_rtc", NULL),
+ REGULATOR_SUPPLY("vdd_aon", NULL),
+};
+static struct regulator_consumer_supply tps658621_ldo3_supply[] = {
+ REGULATOR_SUPPLY("vdd_ldo3", NULL),
+ REGULATOR_SUPPLY("avdd_usb", NULL),
+ REGULATOR_SUPPLY("avdd_usb_pll", NULL),
+};
+static struct regulator_consumer_supply tps658621_ldo4_supply[] = {
+ REGULATOR_SUPPLY("vdd_ldo4", NULL),
+ REGULATOR_SUPPLY("avdd_osc", NULL),
+ REGULATOR_SUPPLY("vddio_sys", "panjit_touch"),
+};
+static struct regulator_consumer_supply tps658621_ldo5_supply[] = {
+ REGULATOR_SUPPLY("vdd_ldo5", NULL),
+ REGULATOR_SUPPLY("vmmc", "sdhci-tegra.3"),
+};
+static struct regulator_consumer_supply tps658621_ldo6_supply[] = {
+ REGULATOR_SUPPLY("vdd_ldo6", NULL),
+ REGULATOR_SUPPLY("vcsi", "tegra_camera"),
+ REGULATOR_SUPPLY("vdd_dmic", "tegra-snd-wm8903"),
+ REGULATOR_SUPPLY("vdd_i2c", "3-0030"),
+ REGULATOR_SUPPLY("vdd_i2c", "6-0072"),
+ REGULATOR_SUPPLY("vdd_i2c", "7-0072"),
+};
+static struct regulator_consumer_supply tps658621_ldo7_supply[] = {
+ REGULATOR_SUPPLY("vdd_ldo7", NULL),
+ REGULATOR_SUPPLY("avdd_hdmi", NULL),
+ REGULATOR_SUPPLY("vdd_fuse", NULL),
+};
+static struct regulator_consumer_supply tps658621_ldo8_supply[] = {
+ REGULATOR_SUPPLY("vdd_ldo8", NULL),
+ REGULATOR_SUPPLY("avdd_hdmi_pll", NULL),
+};
+static struct regulator_consumer_supply tps658621_ldo9_supply[] = {
+ REGULATOR_SUPPLY("vdd_ldo9", NULL),
+ REGULATOR_SUPPLY("avdd_2v85", NULL),
+ REGULATOR_SUPPLY("vdd_ddr_rx", NULL),
+ REGULATOR_SUPPLY("vdd_spk_amp", "tegra-snd-wm8903"),
+};
+
+static struct tps6586x_settings sm0_config = {
+ .sm_pwm_mode = PWM_DEFAULT_VALUE,
+ .slew_rate = SLEW_RATE_3520UV_PER_SEC,
+};
+
+static struct tps6586x_settings sm1_config = {
+ /*
+ * Current TPS6586x is known for having a voltage glitch if current load
+ * changes from low to high in auto PWM/PFM mode for CPU's Vdd line.
+ */
+ .sm_pwm_mode = PWM_ONLY,
+ .slew_rate = SLEW_RATE_3520UV_PER_SEC,
+};
+
+#define REGULATOR_INIT(_id, _minmv, _maxmv, on, config) \
+ { \
+ .constraints = { \
+ .min_uV = (_minmv)*1000, \
+ .max_uV = (_maxmv)*1000, \
+ .valid_modes_mask = (REGULATOR_MODE_NORMAL | \
+ REGULATOR_MODE_STANDBY), \
+ .valid_ops_mask = (REGULATOR_CHANGE_MODE | \
+ REGULATOR_CHANGE_STATUS | \
+ REGULATOR_CHANGE_VOLTAGE), \
+ .always_on = on, \
+ .apply_uV = 1, \
+ }, \
+ .num_consumer_supplies = ARRAY_SIZE(tps658621_##_id##_supply),\
+ .consumer_supplies = tps658621_##_id##_supply, \
+ .driver_data = config, \
+ }
+
+#define ON 1
+#define OFF 0
+
+static struct regulator_init_data sm0_data = REGULATOR_INIT(sm0, 725, 1500, ON, &sm0_config);
+static struct regulator_init_data sm1_data = REGULATOR_INIT(sm1, 725, 1500, ON, &sm1_config);
+static struct regulator_init_data sm2_data = REGULATOR_INIT(sm2, 3000, 4550, ON, NULL);
+static struct regulator_init_data ldo0_data = REGULATOR_INIT(ldo0, 1250, 3300, OFF, NULL);
+static struct regulator_init_data ldo1_data = REGULATOR_INIT(ldo1, 725, 1500, ON, NULL);
+static struct regulator_init_data ldo2_data = REGULATOR_INIT(ldo2, 725, 1500, OFF, NULL);
+static struct regulator_init_data ldo3_data = REGULATOR_INIT(ldo3, 1250, 3300, OFF, NULL);
+static struct regulator_init_data ldo4_data = REGULATOR_INIT(ldo4, 1700, 2475, ON, NULL);
+static struct regulator_init_data ldo5_data = REGULATOR_INIT(ldo5, 1250, 3300, ON, NULL);
+static struct regulator_init_data ldo6_data = REGULATOR_INIT(ldo6, 1800, 1800, OFF, NULL);
+static struct regulator_init_data ldo7_data = REGULATOR_INIT(ldo7, 1250, 3300, OFF, NULL);
+static struct regulator_init_data ldo8_data = REGULATOR_INIT(ldo8, 1250, 3300, OFF, NULL);
+static struct regulator_init_data ldo9_data = REGULATOR_INIT(ldo9, 1250, 3300, OFF, NULL);
+
+static struct tps6586x_rtc_platform_data rtc_data = {
+ .irq = TEGRA_NR_IRQS + TPS6586X_INT_RTC_ALM1,
+ .start = {
+ .year = 2009,
+ .month = 1,
+ .day = 1,
+ },
+ .cl_sel = TPS6586X_RTC_CL_SEL_1_5PF /* use lowest (external 20pF cap) */
+};
+
+#define TPS_REG(_id, _data) \
+ { \
+ .id = TPS6586X_ID_##_id, \
+ .name = "tps6586x-regulator", \
+ .platform_data = _data, \
+ }
+
+static struct tps6586x_subdev_info tps_devs[] = {
+ TPS_REG(SM_0, &sm0_data),
+ TPS_REG(SM_1, &sm1_data),
+ TPS_REG(SM_2, &sm2_data),
+ TPS_REG(LDO_0, &ldo0_data),
+ TPS_REG(LDO_1, &ldo1_data),
+ TPS_REG(LDO_2, &ldo2_data),
+ TPS_REG(LDO_3, &ldo3_data),
+ TPS_REG(LDO_4, &ldo4_data),
+ TPS_REG(LDO_5, &ldo5_data),
+ TPS_REG(LDO_6, &ldo6_data),
+ TPS_REG(LDO_7, &ldo7_data),
+ TPS_REG(LDO_8, &ldo8_data),
+ TPS_REG(LDO_9, &ldo9_data),
+ {
+ .id = 0,
+ .name = "tps6586x-rtc",
+ .platform_data = &rtc_data,
+ },
+};
+
+static struct tps6586x_platform_data tps_platform = {
+ .irq_base = TPS6586X_INT_BASE,
+ .num_subdevs = ARRAY_SIZE(tps_devs),
+ .subdevs = tps_devs,
+ .gpio_base = TPS6586X_GPIO_BASE,
+};
+
+static struct i2c_board_info __initdata ventana_regulators[] = {
+ {
+ I2C_BOARD_INFO("tps6586x", 0x34),
+ .irq = INT_EXTERNAL_PMU,
+ .platform_data = &tps_platform,
+ },
+};
+
+static void ventana_board_suspend(int lp_state, enum suspend_stage stg)
+{
+ if ((lp_state == TEGRA_SUSPEND_LP1) && (stg == TEGRA_SUSPEND_BEFORE_CPU))
+ tegra_console_uart_suspend();
+}
+
+static void ventana_board_resume(int lp_state, enum resume_stage stg)
+{
+ if ((lp_state == TEGRA_SUSPEND_LP1) && (stg == TEGRA_RESUME_AFTER_CPU))
+ tegra_console_uart_resume();
+}
+
+static struct tegra_suspend_platform_data ventana_suspend_data = {
+ /*
+ * Check power on time and crystal oscillator start time
+ * for appropriate settings.
+ */
+ .cpu_timer = 2000,
+ .cpu_off_timer = 100,
+ .suspend_mode = TEGRA_SUSPEND_LP0,
+ .core_timer = 0x7e7e,
+ .core_off_timer = 0xf,
+ .corereq_high = false,
+ .sysclkreq_high = true,
+ .board_suspend = ventana_board_suspend,
+ .board_resume = ventana_board_resume,
+};
+
+int __init ventana_regulator_init(void)
+{
+ void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE);
+ void __iomem *chip_id = IO_ADDRESS(TEGRA_APB_MISC_BASE) + 0x804;
+ u32 pmc_ctrl;
+ u32 minor;
+
+ minor = (readl(chip_id) >> 16) & 0xf;
+ /* A03 (but not A03p) chips do not support LP0 */
+ if (minor == 3 && !(tegra_spare_fuse(18) || tegra_spare_fuse(19)))
+ ventana_suspend_data.suspend_mode = TEGRA_SUSPEND_LP1;
+
+ /* configure the power management controller to trigger PMU
+ * interrupts when low */
+ pmc_ctrl = readl(pmc + PMC_CTRL);
+ writel(pmc_ctrl | PMC_CTRL_INTR_LOW, pmc + PMC_CTRL);
+
+ i2c_register_board_info(4, ventana_regulators, 1);
+
+// regulator_has_full_constraints();
+
+ tegra_init_suspend(&ventana_suspend_data);
+
+ return 0;
+}
+
+static char *ventana_battery[] = {
+ "battery",
+};
+
+static struct gpio_charger_platform_data ventana_charger_pdata = {
+ .name = "ac",
+ .type = POWER_SUPPLY_TYPE_MAINS,
+ .gpio = AC_PRESENT_GPIO,
+ .gpio_active_low = 1,
+ .supplied_to = ventana_battery,
+ .num_supplicants = ARRAY_SIZE(ventana_battery),
+};
+
+static struct platform_device ventana_charger_device = {
+ .name = "gpio-charger",
+ .dev = {
+ .platform_data = &ventana_charger_pdata,
+ },
+};
+
+int __init ventana_charger_init(void)
+{
+ tegra_gpio_enable(AC_PRESENT_GPIO);
+ platform_device_register(&ventana_charger_device);
+ return 0;
+}
+
+static int __init ventana_pcie_init(void)
+{
+ int ret;
+
+ if (!machine_is_ventana())
+ return 0;
+
+ ret = gpio_request(TPS6586X_GPIO_BASE, "pcie_vdd");
+ if (ret < 0)
+ goto fail;
+
+ ret = gpio_direction_output(TPS6586X_GPIO_BASE, 1);
+ if (ret < 0)
+ goto fail;
+
+ gpio_export(TPS6586X_GPIO_BASE, false);
+ return 0;
+
+fail:
+ pr_err("%s: gpio_request failed #%d\n", __func__, TPS6586X_GPIO_BASE);
+ gpio_free(TPS6586X_GPIO_BASE);
+ return ret;
+}
+
+late_initcall(ventana_pcie_init);
diff --git a/arch/arm/mach-tegra/board-ventana-sdhci.c b/arch/arm/mach-tegra/board-ventana-sdhci.c
new file mode 100644
index 000000000000..d6ec6edb0b0f
--- /dev/null
+++ b/arch/arm/mach-tegra/board-ventana-sdhci.c
@@ -0,0 +1,266 @@
+/*
+ * arch/arm/mach-tegra/board-harmony-sdhci.c
+ *
+ * Copyright (C) 2010 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/resource.h>
+#include <linux/platform_device.h>
+#include <linux/wlan_plat.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/mmc/host.h>
+
+#include <asm/mach-types.h>
+#include <mach/irqs.h>
+#include <mach/iomap.h>
+#include <mach/sdhci.h>
+
+#include "gpio-names.h"
+#include "board.h"
+
+#define VENTANA_WLAN_PWR TEGRA_GPIO_PK5
+#define VENTANA_WLAN_RST TEGRA_GPIO_PK6
+#define VENTANA_WLAN_WOW TEGRA_GPIO_PS0
+
+static void (*wifi_status_cb)(int card_present, void *dev_id);
+static void *wifi_status_cb_devid;
+static int ventana_wifi_status_register(void (*callback)(int , void *), void *);
+static struct clk *wifi_32k_clk;
+
+static int ventana_wifi_reset(int on);
+static int ventana_wifi_power(int on);
+static int ventana_wifi_set_carddetect(int val);
+
+static struct wifi_platform_data ventana_wifi_control = {
+ .set_power = ventana_wifi_power,
+ .set_reset = ventana_wifi_reset,
+ .set_carddetect = ventana_wifi_set_carddetect,
+};
+
+static struct resource wifi_resource[] = {
+ [0] = {
+ .name = "bcm4329_wlan_irq",
+ .start = TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PS0),
+ .end = TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PS0),
+ .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL | IORESOURCE_IRQ_SHAREABLE,
+ },
+};
+
+static struct platform_device ventana_wifi_device = {
+ .name = "bcm4329_wlan",
+ .id = 1,
+ .num_resources = 1,
+ .resource = wifi_resource,
+ .dev = {
+ .platform_data = &ventana_wifi_control,
+ },
+};
+
+static struct resource sdhci_resource0[] = {
+ [0] = {
+ .start = INT_SDMMC1,
+ .end = INT_SDMMC1,
+ .flags = IORESOURCE_IRQ,
+ },
+ [1] = {
+ .start = TEGRA_SDMMC1_BASE,
+ .end = TEGRA_SDMMC1_BASE + TEGRA_SDMMC1_SIZE-1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct resource sdhci_resource2[] = {
+ [0] = {
+ .start = INT_SDMMC3,
+ .end = INT_SDMMC3,
+ .flags = IORESOURCE_IRQ,
+ },
+ [1] = {
+ .start = TEGRA_SDMMC3_BASE,
+ .end = TEGRA_SDMMC3_BASE + TEGRA_SDMMC3_SIZE-1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct resource sdhci_resource3[] = {
+ [0] = {
+ .start = INT_SDMMC4,
+ .end = INT_SDMMC4,
+ .flags = IORESOURCE_IRQ,
+ },
+ [1] = {
+ .start = TEGRA_SDMMC4_BASE,
+ .end = TEGRA_SDMMC4_BASE + TEGRA_SDMMC4_SIZE-1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct embedded_sdio_data embedded_sdio_data0 = {
+ .cccr = {
+ .sdio_vsn = 2,
+ .multi_block = 1,
+ .low_speed = 0,
+ .wide_bus = 0,
+ .high_power = 1,
+ .high_speed = 1,
+ },
+ .cis = {
+ .vendor = 0x02d0,
+ .device = 0x4329,
+ },
+};
+
+static struct tegra_sdhci_platform_data tegra_sdhci_platform_data0 = {
+ .mmc_data = {
+ .register_status_notify = ventana_wifi_status_register,
+ .embedded_sdio = &embedded_sdio_data0,
+ .built_in = 1,
+ },
+ .cd_gpio = -1,
+ .wp_gpio = -1,
+ .power_gpio = -1,
+};
+
+static struct tegra_sdhci_platform_data tegra_sdhci_platform_data2 = {
+ .cd_gpio = TEGRA_GPIO_PI5,
+ .wp_gpio = TEGRA_GPIO_PH1,
+ .power_gpio = TEGRA_GPIO_PT3,
+};
+
+static struct tegra_sdhci_platform_data tegra_sdhci_platform_data3 = {
+ .cd_gpio = -1,
+ .wp_gpio = -1,
+ .power_gpio = TEGRA_GPIO_PI6,
+ .mmc_data = {
+ .built_in = 1,
+ }
+};
+
+static struct platform_device tegra_sdhci_device0 = {
+ .name = "sdhci-tegra",
+ .id = 0,
+ .resource = sdhci_resource0,
+ .num_resources = ARRAY_SIZE(sdhci_resource0),
+ .dev = {
+ .platform_data = &tegra_sdhci_platform_data0,
+ },
+};
+
+static struct platform_device tegra_sdhci_device2 = {
+ .name = "sdhci-tegra",
+ .id = 2,
+ .resource = sdhci_resource2,
+ .num_resources = ARRAY_SIZE(sdhci_resource2),
+ .dev = {
+ .platform_data = &tegra_sdhci_platform_data2,
+ },
+};
+
+static struct platform_device tegra_sdhci_device3 = {
+ .name = "sdhci-tegra",
+ .id = 3,
+ .resource = sdhci_resource3,
+ .num_resources = ARRAY_SIZE(sdhci_resource3),
+ .dev = {
+ .platform_data = &tegra_sdhci_platform_data3,
+ },
+};
+
+static int ventana_wifi_status_register(
+ void (*callback)(int card_present, void *dev_id),
+ void *dev_id)
+{
+ if (wifi_status_cb)
+ return -EAGAIN;
+ wifi_status_cb = callback;
+ wifi_status_cb_devid = dev_id;
+ return 0;
+}
+
+static int ventana_wifi_set_carddetect(int val)
+{
+ pr_debug("%s: %d\n", __func__, val);
+ if (wifi_status_cb)
+ wifi_status_cb(val, wifi_status_cb_devid);
+ else
+ pr_warning("%s: Nobody to notify\n", __func__);
+ return 0;
+}
+
+static int ventana_wifi_power(int on)
+{
+ pr_debug("%s: %d\n", __func__, on);
+
+ gpio_set_value(VENTANA_WLAN_PWR, on);
+ mdelay(100);
+ gpio_set_value(VENTANA_WLAN_RST, on);
+ mdelay(200);
+
+ if (on)
+ clk_enable(wifi_32k_clk);
+ else
+ clk_disable(wifi_32k_clk);
+
+ return 0;
+}
+
+static int ventana_wifi_reset(int on)
+{
+ pr_debug("%s: do nothing\n", __func__);
+ return 0;
+}
+
+static int __init ventana_wifi_init(void)
+{
+ wifi_32k_clk = clk_get_sys(NULL, "blink");
+ if (IS_ERR(wifi_32k_clk)) {
+ pr_err("%s: unable to get blink clock\n", __func__);
+ return PTR_ERR(wifi_32k_clk);
+ }
+
+ gpio_request(VENTANA_WLAN_PWR, "wlan_power");
+ gpio_request(VENTANA_WLAN_RST, "wlan_rst");
+ gpio_request(VENTANA_WLAN_WOW, "bcmsdh_sdmmc");
+
+ tegra_gpio_enable(VENTANA_WLAN_PWR);
+ tegra_gpio_enable(VENTANA_WLAN_RST);
+ tegra_gpio_enable(VENTANA_WLAN_WOW);
+
+ gpio_direction_output(VENTANA_WLAN_PWR, 0);
+ gpio_direction_output(VENTANA_WLAN_RST, 0);
+ gpio_direction_input(VENTANA_WLAN_WOW);
+
+ platform_device_register(&ventana_wifi_device);
+
+ device_init_wakeup(&ventana_wifi_device.dev, 1);
+ device_set_wakeup_enable(&ventana_wifi_device.dev, 0);
+
+ return 0;
+}
+int __init ventana_sdhci_init(void)
+{
+ tegra_gpio_enable(tegra_sdhci_platform_data2.power_gpio);
+ tegra_gpio_enable(tegra_sdhci_platform_data2.cd_gpio);
+ tegra_gpio_enable(tegra_sdhci_platform_data2.wp_gpio);
+ tegra_gpio_enable(tegra_sdhci_platform_data3.power_gpio);
+
+ platform_device_register(&tegra_sdhci_device3);
+ platform_device_register(&tegra_sdhci_device2);
+ platform_device_register(&tegra_sdhci_device0);
+
+ ventana_wifi_init();
+ return 0;
+}
diff --git a/arch/arm/mach-tegra/board-ventana-sensors.c b/arch/arm/mach-tegra/board-ventana-sensors.c
new file mode 100644
index 000000000000..c9c2f5441612
--- /dev/null
+++ b/arch/arm/mach-tegra/board-ventana-sensors.c
@@ -0,0 +1,573 @@
+/*
+ * arch/arm/mach-tegra/board-ventana-sensors.c
+ *
+ * Copyright (c) 2011, NVIDIA CORPORATION, All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Neither the name of NVIDIA CORPORATION nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/mpu.h>
+#include <linux/i2c/pca954x.h>
+#include <linux/i2c/pca953x.h>
+#include <linux/nct1008.h>
+#include <linux/err.h>
+#include <linux/regulator/consumer.h>
+
+#include <mach/gpio.h>
+
+#include <media/ov5650.h>
+#include <media/ov2710.h>
+#include <media/sh532u.h>
+#include <media/ssl3250a.h>
+#include <generated/mach-types.h>
+
+#include "gpio-names.h"
+#include "board.h"
+#include "board-ventana.h"
+#include "cpu-tegra.h"
+
+#define ISL29018_IRQ_GPIO TEGRA_GPIO_PZ2
+#define AKM8975_IRQ_GPIO TEGRA_GPIO_PN5
+#define CAMERA_POWER_GPIO TEGRA_GPIO_PV4
+#define CAMERA_CSI_MUX_SEL_GPIO TEGRA_GPIO_PBB4
+#define CAMERA_FLASH_ACT_GPIO TEGRA_GPIO_PD2
+#define NCT1008_THERM2_GPIO TEGRA_GPIO_PN6
+
+static int ventana_camera_init(void)
+{
+ int err;
+
+ tegra_gpio_enable(CAMERA_POWER_GPIO);
+ gpio_request(CAMERA_POWER_GPIO, "camera_power_en");
+ gpio_direction_output(CAMERA_POWER_GPIO, 1);
+ gpio_export(CAMERA_POWER_GPIO, false);
+
+ tegra_gpio_enable(CAMERA_CSI_MUX_SEL_GPIO);
+ gpio_request(CAMERA_CSI_MUX_SEL_GPIO, "camera_csi_sel");
+ gpio_direction_output(CAMERA_CSI_MUX_SEL_GPIO, 0);
+ gpio_export(CAMERA_CSI_MUX_SEL_GPIO, false);
+
+ err = gpio_request(CAMERA_FLASH_ACT_GPIO, "torch_gpio_act");
+ if (err < 0) {
+ pr_err("gpio_request failed for gpio %d\n",
+ CAMERA_FLASH_ACT_GPIO);
+ } else {
+ tegra_gpio_enable(CAMERA_FLASH_ACT_GPIO);
+ gpio_direction_output(CAMERA_FLASH_ACT_GPIO, 0);
+ gpio_export(CAMERA_FLASH_ACT_GPIO, false);
+ }
+ return 0;
+}
+
+/* left ov5650 is CAM2 which is on csi_a */
+static int ventana_left_ov5650_power_on(void)
+{
+ gpio_direction_output(CAMERA_CSI_MUX_SEL_GPIO, 0);
+ gpio_direction_output(AVDD_DSI_CSI_ENB_GPIO, 1);
+ gpio_direction_output(CAM2_LDO_SHUTDN_L_GPIO, 1);
+ mdelay(5);
+ gpio_direction_output(CAM2_PWR_DN_GPIO, 0);
+ mdelay(5);
+ gpio_direction_output(CAM2_RST_L_GPIO, 0);
+ mdelay(1);
+ gpio_direction_output(CAM2_RST_L_GPIO, 1);
+ mdelay(20);
+ return 0;
+}
+
+static int ventana_left_ov5650_power_off(void)
+{
+ gpio_direction_output(AVDD_DSI_CSI_ENB_GPIO, 0);
+ gpio_direction_output(CAM2_RST_L_GPIO, 0);
+ gpio_direction_output(CAM2_PWR_DN_GPIO, 1);
+ gpio_direction_output(CAM2_LDO_SHUTDN_L_GPIO, 0);
+ return 0;
+}
+
+struct ov5650_platform_data ventana_left_ov5650_data = {
+ .power_on = ventana_left_ov5650_power_on,
+ .power_off = ventana_left_ov5650_power_off,
+};
+
+/* right ov5650 is CAM1 which is on csi_b */
+static int ventana_right_ov5650_power_on(void)
+{
+ gpio_direction_output(AVDD_DSI_CSI_ENB_GPIO, 1);
+ gpio_direction_output(CAM1_LDO_SHUTDN_L_GPIO, 1);
+ mdelay(5);
+ gpio_direction_output(CAM1_PWR_DN_GPIO, 0);
+ mdelay(5);
+ gpio_direction_output(CAM1_RST_L_GPIO, 0);
+ mdelay(1);
+ gpio_direction_output(CAM1_RST_L_GPIO, 1);
+ mdelay(20);
+ return 0;
+}
+
+static int ventana_right_ov5650_power_off(void)
+{
+ gpio_direction_output(AVDD_DSI_CSI_ENB_GPIO, 0);
+ gpio_direction_output(CAM1_RST_L_GPIO, 0);
+ gpio_direction_output(CAM1_PWR_DN_GPIO, 1);
+ gpio_direction_output(CAM1_LDO_SHUTDN_L_GPIO, 0);
+ return 0;
+}
+
+struct ov5650_platform_data ventana_right_ov5650_data = {
+ .power_on = ventana_right_ov5650_power_on,
+ .power_off = ventana_right_ov5650_power_off,
+};
+
+static int ventana_ov2710_power_on(void)
+{
+ gpio_direction_output(CAMERA_CSI_MUX_SEL_GPIO, 1);
+ gpio_direction_output(AVDD_DSI_CSI_ENB_GPIO, 1);
+ gpio_direction_output(CAM3_LDO_SHUTDN_L_GPIO, 1);
+ mdelay(5);
+ gpio_direction_output(CAM3_PWR_DN_GPIO, 0);
+ mdelay(5);
+ gpio_direction_output(CAM3_RST_L_GPIO, 0);
+ mdelay(1);
+ gpio_direction_output(CAM3_RST_L_GPIO, 1);
+ mdelay(20);
+ return 0;
+}
+
+static int ventana_ov2710_power_off(void)
+{
+ gpio_direction_output(CAM3_RST_L_GPIO, 0);
+ gpio_direction_output(CAM3_PWR_DN_GPIO, 1);
+ gpio_direction_output(CAM3_LDO_SHUTDN_L_GPIO, 0);
+ gpio_direction_output(AVDD_DSI_CSI_ENB_GPIO, 0);
+ gpio_direction_output(CAMERA_CSI_MUX_SEL_GPIO, 0);
+ return 0;
+}
+
+struct ov2710_platform_data ventana_ov2710_data = {
+ .power_on = ventana_ov2710_power_on,
+ .power_off = ventana_ov2710_power_off,
+};
+
+
+static struct sh532u_platform_data sh532u_left_pdata = {
+ .num = 1,
+ .sync = 2,
+ .dev_name = "focuser",
+ .gpio_reset = CAM2_RST_L_GPIO,
+ .gpio_en = CAM2_LDO_SHUTDN_L_GPIO,
+};
+
+static struct sh532u_platform_data sh532u_right_pdata = {
+ .num = 2,
+ .sync = 1,
+ .dev_name = "focuser",
+ .gpio_reset = CAM1_RST_L_GPIO,
+ .gpio_en = CAM1_LDO_SHUTDN_L_GPIO,
+};
+
+
+static struct nvc_torch_pin_state ventana_ssl3250a_pinstate = {
+ .mask = 0x0040, /* VGP6 */
+ .values = 0x0040,
+};
+
+static struct ssl3250a_platform_data ventana_ssl3250a_pdata = {
+ .dev_name = "torch",
+ .pinstate = &ventana_ssl3250a_pinstate,
+ .gpio_act = CAMERA_FLASH_ACT_GPIO,
+};
+
+
+static void ventana_isl29018_init(void)
+{
+ tegra_gpio_enable(ISL29018_IRQ_GPIO);
+ gpio_request(ISL29018_IRQ_GPIO, "isl29018");
+ gpio_direction_input(ISL29018_IRQ_GPIO);
+}
+
+#ifdef CONFIG_SENSORS_AK8975
+static void ventana_akm8975_init(void)
+{
+ tegra_gpio_enable(AKM8975_IRQ_GPIO);
+ gpio_request(AKM8975_IRQ_GPIO, "akm8975");
+ gpio_direction_input(AKM8975_IRQ_GPIO);
+}
+#endif
+
+static void ventana_nct1008_init(void)
+{
+ tegra_gpio_enable(NCT1008_THERM2_GPIO);
+ gpio_request(NCT1008_THERM2_GPIO, "temp_alert");
+ gpio_direction_input(NCT1008_THERM2_GPIO);
+}
+
+static struct nct1008_platform_data ventana_nct1008_pdata = {
+ .supported_hwrev = true,
+ .ext_range = false,
+ .conv_rate = 0x08,
+ .offset = 0,
+ .hysteresis = 0,
+ .shutdown_ext_limit = 115,
+ .shutdown_local_limit = 120,
+ .throttling_ext_limit = 90,
+ .alarm_fn = tegra_throttling_enable,
+};
+
+static const struct i2c_board_info ventana_i2c0_board_info[] = {
+ {
+ I2C_BOARD_INFO("isl29018", 0x44),
+ .irq = TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PZ2),
+ },
+};
+
+static const struct i2c_board_info ventana_i2c2_board_info[] = {
+ {
+ I2C_BOARD_INFO("bq20z75", 0x0B),
+ },
+};
+
+static struct pca953x_platform_data ventana_tca6416_data = {
+ .gpio_base = TEGRA_NR_GPIOS + 4, /* 4 gpios are already requested by tps6586x */
+};
+
+static struct pca954x_platform_mode ventana_pca9546_modes[] = {
+ { .adap_id = 6, .deselect_on_exit = 1 }, /* REAR CAM1 */
+ { .adap_id = 7, .deselect_on_exit = 1 }, /* REAR CAM2 */
+ { .adap_id = 8, .deselect_on_exit = 1 }, /* FRONT CAM3 */
+};
+
+static struct pca954x_platform_data ventana_pca9546_data = {
+ .modes = ventana_pca9546_modes,
+ .num_modes = ARRAY_SIZE(ventana_pca9546_modes),
+};
+
+static const struct i2c_board_info ventana_i2c3_board_info_tca6416[] = {
+ {
+ I2C_BOARD_INFO("tca6416", 0x20),
+ .platform_data = &ventana_tca6416_data,
+ },
+};
+
+static const struct i2c_board_info ventana_i2c3_board_info_pca9546[] = {
+ {
+ I2C_BOARD_INFO("pca9546", 0x70),
+ .platform_data = &ventana_pca9546_data,
+ },
+};
+
+static const struct i2c_board_info ventana_i2c3_board_info_ssl3250a[] = {
+ {
+ I2C_BOARD_INFO("ssl3250a", 0x30),
+ .platform_data = &ventana_ssl3250a_pdata,
+ },
+};
+
+static struct i2c_board_info ventana_i2c4_board_info[] = {
+ {
+ I2C_BOARD_INFO("nct1008", 0x4C),
+ .irq = TEGRA_GPIO_TO_IRQ(NCT1008_THERM2_GPIO),
+ .platform_data = &ventana_nct1008_pdata,
+ },
+
+#ifdef CONFIG_SENSORS_AK8975
+ {
+ I2C_BOARD_INFO("akm8975", 0x0C),
+ .irq = TEGRA_GPIO_TO_IRQ(AKM8975_IRQ_GPIO),
+ },
+#endif
+};
+
+static struct i2c_board_info ventana_i2c6_board_info[] = {
+ {
+ I2C_BOARD_INFO("ov5650R", 0x36),
+ .platform_data = &ventana_right_ov5650_data,
+ },
+ {
+ I2C_BOARD_INFO("sh532u", 0x72),
+ .platform_data = &sh532u_right_pdata,
+ },
+};
+
+static struct i2c_board_info ventana_i2c7_board_info[] = {
+ {
+ I2C_BOARD_INFO("ov5650L", 0x36),
+ .platform_data = &ventana_left_ov5650_data,
+ },
+ {
+ I2C_BOARD_INFO("sh532u", 0x72),
+ .platform_data = &sh532u_left_pdata,
+ },
+};
+
+static struct i2c_board_info ventana_i2c8_board_info[] = {
+ {
+ I2C_BOARD_INFO("ov2710", 0x36),
+ .platform_data = &ventana_ov2710_data,
+ },
+};
+
+#ifdef CONFIG_MPU_SENSORS_MPU3050
+static struct mpu_platform_data mpu3050_data = {
+ .int_config = 0x10,
+ .level_shifter = 0,
+ .orientation = MPU_GYRO_ORIENTATION, /* Located in board_[platformname].h */
+};
+
+static struct ext_slave_platform_data mpu3050_accel_data = {
+ .address = MPU_ACCEL_ADDR,
+ .irq = 0,
+ .adapt_num = MPU_ACCEL_BUS_NUM,
+ .bus = EXT_SLAVE_BUS_SECONDARY,
+ .orientation = MPU_ACCEL_ORIENTATION, /* Located in board_[platformname].h */
+};
+
+static struct ext_slave_platform_data mpu_compass_data = {
+ .address = MPU_COMPASS_ADDR,
+ .irq = 0,
+ .adapt_num = MPU_COMPASS_BUS_NUM,
+ .bus = EXT_SLAVE_BUS_PRIMARY,
+ .orientation = MPU_COMPASS_ORIENTATION, /* Located in board_[platformname].h */
+};
+
+static struct i2c_board_info __initdata inv_mpu_i2c2_board_info[] = {
+ {
+ I2C_BOARD_INFO(MPU_GYRO_NAME, MPU_GYRO_ADDR),
+ .irq = TEGRA_GPIO_TO_IRQ(MPU_GYRO_IRQ_GPIO),
+ .platform_data = &mpu3050_data,
+ },
+ {
+ I2C_BOARD_INFO(MPU_ACCEL_NAME, MPU_ACCEL_ADDR),
+#if MPU_ACCEL_IRQ_GPIO
+ .irq = TEGRA_GPIO_TO_IRQ(MPU_ACCEL_IRQ_GPIO),
+#endif
+ .platform_data = &mpu3050_accel_data,
+ },
+};
+
+static struct i2c_board_info __initdata inv_mpu_i2c4_board_info[] = {
+ {
+ I2C_BOARD_INFO(MPU_COMPASS_NAME, MPU_COMPASS_ADDR),
+#if MPU_COMPASS_IRQ_GPIO
+ .irq = TEGRA_GPIO_TO_IRQ(MPU_COMPASS_IRQ_GPIO),
+#endif
+ .platform_data = &mpu_compass_data,
+ },
+};
+
+static void mpuirq_init(void)
+{
+ int ret = 0;
+
+ pr_info("*** MPU START *** mpuirq_init...\n");
+
+#if MPU_ACCEL_IRQ_GPIO
+ /* ACCEL-IRQ assignment */
+ tegra_gpio_enable(MPU_ACCEL_IRQ_GPIO);
+ ret = gpio_request(MPU_ACCEL_IRQ_GPIO, MPU_ACCEL_NAME);
+ if (ret < 0) {
+ pr_err("%s: gpio_request failed %d\n", __func__, ret);
+ return;
+ }
+
+ ret = gpio_direction_input(MPU_ACCEL_IRQ_GPIO);
+ if (ret < 0) {
+ pr_err("%s: gpio_direction_input failed %d\n", __func__, ret);
+ gpio_free(MPU_ACCEL_IRQ_GPIO);
+ return;
+ }
+#endif
+
+ /* MPU-IRQ assignment */
+ tegra_gpio_enable(MPU_GYRO_IRQ_GPIO);
+ ret = gpio_request(MPU_GYRO_IRQ_GPIO, MPU_GYRO_NAME);
+ if (ret < 0) {
+ pr_err("%s: gpio_request failed %d\n", __func__, ret);
+ return;
+ }
+
+ ret = gpio_direction_input(MPU_GYRO_IRQ_GPIO);
+ if (ret < 0) {
+ pr_err("%s: gpio_direction_input failed %d\n", __func__, ret);
+ gpio_free(MPU_GYRO_IRQ_GPIO);
+ return;
+ }
+ pr_info("*** MPU END *** mpuirq_init...\n");
+
+ i2c_register_board_info(MPU_GYRO_BUS_NUM, inv_mpu_i2c2_board_info,
+ ARRAY_SIZE(inv_mpu_i2c2_board_info));
+ i2c_register_board_info(MPU_COMPASS_BUS_NUM, inv_mpu_i2c4_board_info,
+ ARRAY_SIZE(inv_mpu_i2c4_board_info));
+}
+#endif
+
+int __init ventana_sensors_init(void)
+{
+ struct board_info BoardInfo;
+
+ ventana_isl29018_init();
+#ifdef CONFIG_SENSORS_AK8975
+ ventana_akm8975_init();
+#endif
+#ifdef CONFIG_MPU_SENSORS_MPU3050
+ mpuirq_init();
+#endif
+ ventana_camera_init();
+ ventana_nct1008_init();
+
+ i2c_register_board_info(0, ventana_i2c0_board_info,
+ ARRAY_SIZE(ventana_i2c0_board_info));
+
+ tegra_get_board_info(&BoardInfo);
+
+ /*
+ * battery driver is supported on FAB.D boards and above only,
+ * since they have the necessary hardware rework
+ */
+ if (BoardInfo.sku > 0) {
+ i2c_register_board_info(2, ventana_i2c2_board_info,
+ ARRAY_SIZE(ventana_i2c2_board_info));
+ }
+
+ i2c_register_board_info(3, ventana_i2c3_board_info_ssl3250a,
+ ARRAY_SIZE(ventana_i2c3_board_info_ssl3250a));
+
+ i2c_register_board_info(4, ventana_i2c4_board_info,
+ ARRAY_SIZE(ventana_i2c4_board_info));
+
+ i2c_register_board_info(6, ventana_i2c6_board_info,
+ ARRAY_SIZE(ventana_i2c6_board_info));
+
+ i2c_register_board_info(7, ventana_i2c7_board_info,
+ ARRAY_SIZE(ventana_i2c7_board_info));
+
+ i2c_register_board_info(8, ventana_i2c8_board_info,
+ ARRAY_SIZE(ventana_i2c8_board_info));
+
+ return 0;
+}
+
+#ifdef CONFIG_TEGRA_CAMERA
+
+struct tegra_camera_gpios {
+ const char *name;
+ int gpio;
+ int enabled;
+};
+
+#define TEGRA_CAMERA_GPIO(_name, _gpio, _enabled) \
+ { \
+ .name = _name, \
+ .gpio = _gpio, \
+ .enabled = _enabled, \
+ }
+
+static struct tegra_camera_gpios ventana_camera_gpio_keys[] = {
+ [0] = TEGRA_CAMERA_GPIO("en_avdd_csi", AVDD_DSI_CSI_ENB_GPIO, 1),
+ [1] = TEGRA_CAMERA_GPIO("cam_i2c_mux_rst_lo", CAM_I2C_MUX_RST_GPIO, 1),
+
+ [2] = TEGRA_CAMERA_GPIO("cam2_ldo_shdn_lo", CAM2_LDO_SHUTDN_L_GPIO, 0),
+ [3] = TEGRA_CAMERA_GPIO("cam2_af_pwdn_lo", CAM2_AF_PWR_DN_L_GPIO, 0),
+ [4] = TEGRA_CAMERA_GPIO("cam2_pwdn", CAM2_PWR_DN_GPIO, 0),
+ [5] = TEGRA_CAMERA_GPIO("cam2_rst_lo", CAM2_RST_L_GPIO, 1),
+
+ [6] = TEGRA_CAMERA_GPIO("cam3_ldo_shdn_lo", CAM3_LDO_SHUTDN_L_GPIO, 0),
+ [7] = TEGRA_CAMERA_GPIO("cam3_af_pwdn_lo", CAM3_AF_PWR_DN_L_GPIO, 0),
+ [8] = TEGRA_CAMERA_GPIO("cam3_pwdn", CAM3_PWR_DN_GPIO, 0),
+ [9] = TEGRA_CAMERA_GPIO("cam3_rst_lo", CAM3_RST_L_GPIO, 1),
+
+ [10] = TEGRA_CAMERA_GPIO("cam1_ldo_shdn_lo", CAM1_LDO_SHUTDN_L_GPIO, 0),
+ [11] = TEGRA_CAMERA_GPIO("cam1_af_pwdn_lo", CAM1_AF_PWR_DN_L_GPIO, 0),
+ [12] = TEGRA_CAMERA_GPIO("cam1_pwdn", CAM1_PWR_DN_GPIO, 0),
+ [13] = TEGRA_CAMERA_GPIO("cam1_rst_lo", CAM1_RST_L_GPIO, 1),
+};
+
+int __init ventana_camera_late_init(void)
+{
+ int ret;
+ int i;
+ struct regulator *cam_ldo6 = NULL;
+
+ if (!machine_is_ventana())
+ return 0;
+
+ cam_ldo6 = regulator_get(NULL, "vdd_ldo6");
+ if (IS_ERR_OR_NULL(cam_ldo6)) {
+ pr_err("%s: Couldn't get regulator ldo6\n", __func__);
+ return PTR_ERR(cam_ldo6);
+ }
+
+ ret = regulator_enable(cam_ldo6);
+ if (ret){
+ pr_err("%s: Failed to enable ldo6\n", __func__);
+ goto fail_put_regulator;
+ }
+
+ i2c_new_device(i2c_get_adapter(3), ventana_i2c3_board_info_tca6416);
+
+ for (i = 0; i < ARRAY_SIZE(ventana_camera_gpio_keys); i++) {
+ ret = gpio_request(ventana_camera_gpio_keys[i].gpio,
+ ventana_camera_gpio_keys[i].name);
+ if (ret < 0) {
+ pr_err("%s: gpio_request failed for gpio #%d\n",
+ __func__, i);
+ goto fail_free_gpio;
+ }
+ gpio_direction_output(ventana_camera_gpio_keys[i].gpio,
+ ventana_camera_gpio_keys[i].enabled);
+ gpio_export(ventana_camera_gpio_keys[i].gpio, false);
+ }
+
+ i2c_new_device(i2c_get_adapter(3), ventana_i2c3_board_info_pca9546);
+
+ ventana_ov2710_power_off();
+ ventana_left_ov5650_power_off();
+ ventana_right_ov5650_power_off();
+
+ ret = regulator_disable(cam_ldo6);
+ if (ret){
+ pr_err("%s: Failed to disable ldo6\n", __func__);
+ goto fail_free_gpio;
+ }
+
+ regulator_put(cam_ldo6);
+ return 0;
+
+fail_free_gpio:
+ while (i--)
+ gpio_free(ventana_camera_gpio_keys[i].gpio);
+
+fail_put_regulator:
+ regulator_put(cam_ldo6);
+ return ret;
+}
+
+late_initcall(ventana_camera_late_init);
+
+#endif /* CONFIG_TEGRA_CAMERA */
diff --git a/arch/arm/mach-tegra/board-ventana.c b/arch/arm/mach-tegra/board-ventana.c
new file mode 100644
index 000000000000..45333fb01206
--- /dev/null
+++ b/arch/arm/mach-tegra/board-ventana.c
@@ -0,0 +1,641 @@
+/*
+ * arch/arm/mach-tegra/board-ventana.c
+ *
+ * Copyright (c) 2010-2011, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/serial_8250.h>
+#include <linux/i2c.h>
+#include <linux/i2c/panjit_ts.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/i2c-tegra.h>
+#include <linux/gpio.h>
+#include <linux/gpio_keys.h>
+#include <linux/input.h>
+#include <linux/platform_data/tegra_usb.h>
+#include <linux/mfd/tps6586x.h>
+#include <linux/memblock.h>
+#include <linux/i2c/atmel_mxt_ts.h>
+#include <linux/tegra_uart.h>
+
+#include <sound/wm8903.h>
+
+#include <mach/clk.h>
+#include <mach/iomap.h>
+#include <mach/irqs.h>
+#include <mach/pinmux.h>
+#include <mach/iomap.h>
+#include <mach/io.h>
+#include <mach/i2s.h>
+#include <mach/tegra_wm8903_pdata.h>
+
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <mach/usb_phy.h>
+
+#include "board.h"
+#include "clock.h"
+#include "board-ventana.h"
+#include "devices.h"
+#include "gpio-names.h"
+#include "fuse.h"
+#include "wakeups-t2.h"
+#include "pm.h"
+
+static struct tegra_utmip_config utmi_phy_config[] = {
+ [0] = {
+ .hssync_start_delay = 9,
+ .idle_wait_delay = 17,
+ .elastic_limit = 16,
+ .term_range_adj = 6,
+ .xcvr_setup = 15,
+ .xcvr_setup_offset = 0,
+ .xcvr_use_fuses = 1,
+ .xcvr_lsfslew = 2,
+ .xcvr_lsrslew = 2,
+ },
+ [1] = {
+ .hssync_start_delay = 9,
+ .idle_wait_delay = 17,
+ .elastic_limit = 16,
+ .term_range_adj = 6,
+ .xcvr_setup = 8,
+ .xcvr_setup_offset = 0,
+ .xcvr_use_fuses = 1,
+ .xcvr_lsfslew = 2,
+ .xcvr_lsrslew = 2,
+ },
+};
+
+static struct tegra_ulpi_config ulpi_phy_config = {
+ .reset_gpio = TEGRA_GPIO_PG2,
+ .clk = "cdev2",
+};
+
+static struct resource ventana_bcm4329_rfkill_resources[] = {
+ {
+ .name = "bcm4329_nshutdown_gpio",
+ .start = TEGRA_GPIO_PU0,
+ .end = TEGRA_GPIO_PU0,
+ .flags = IORESOURCE_IO,
+ },
+};
+
+static struct platform_device ventana_bcm4329_rfkill_device = {
+ .name = "bcm4329_rfkill",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(ventana_bcm4329_rfkill_resources),
+ .resource = ventana_bcm4329_rfkill_resources,
+};
+
+static void __init ventana_bt_rfkill(void)
+{
+ /*Add Clock Resource*/
+ clk_add_alias("bcm4329_32k_clk", ventana_bcm4329_rfkill_device.name, \
+ "blink", NULL);
+ return;
+}
+
+static struct resource ventana_bluesleep_resources[] = {
+ [0] = {
+ .name = "gpio_host_wake",
+ .start = TEGRA_GPIO_PU6,
+ .end = TEGRA_GPIO_PU6,
+ .flags = IORESOURCE_IO,
+ },
+ [1] = {
+ .name = "gpio_ext_wake",
+ .start = TEGRA_GPIO_PU1,
+ .end = TEGRA_GPIO_PU1,
+ .flags = IORESOURCE_IO,
+ },
+ [2] = {
+ .name = "host_wake",
+ .start = TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PU6),
+ .end = TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PU6),
+ .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE,
+ },
+};
+
+static struct platform_device ventana_bluesleep_device = {
+ .name = "bluesleep",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(ventana_bluesleep_resources),
+ .resource = ventana_bluesleep_resources,
+};
+
+static void __init ventana_setup_bluesleep(void)
+{
+ platform_device_register(&ventana_bluesleep_device);
+ tegra_gpio_enable(TEGRA_GPIO_PU6);
+ tegra_gpio_enable(TEGRA_GPIO_PU1);
+ return;
+}
+
+static __initdata struct tegra_clk_init_table ventana_clk_init_table[] = {
+ /* name parent rate enabled */
+ { "blink", "clk_32k", 32768, false},
+ { "pll_p_out4", "pll_p", 24000000, true },
+ { "pwm", "clk_32k", 32768, false},
+ { "i2s1", "pll_a_out0", 0, false},
+ { "i2s2", "pll_a_out0", 0, false},
+ { "spdif_out", "pll_a_out0", 0, false},
+ { NULL, NULL, 0, 0},
+};
+
+static struct tegra_ulpi_config ventana_ehci2_ulpi_phy_config = {
+ .reset_gpio = TEGRA_GPIO_PV1,
+ .clk = "cdev2",
+};
+
+static struct tegra_ehci_platform_data ventana_ehci2_ulpi_platform_data = {
+ .operating_mode = TEGRA_USB_HOST,
+ .power_down_on_bus_suspend = 1,
+ .phy_config = &ventana_ehci2_ulpi_phy_config,
+ .phy_type = TEGRA_USB_PHY_TYPE_LINK_ULPI,
+};
+
+static struct tegra_i2c_platform_data ventana_i2c1_platform_data = {
+ .adapter_nr = 0,
+ .bus_count = 1,
+ .bus_clk_rate = { 400000, 0 },
+ .slave_addr = 0x00FC,
+};
+
+static const struct tegra_pingroup_config i2c2_ddc = {
+ .pingroup = TEGRA_PINGROUP_DDC,
+ .func = TEGRA_MUX_I2C2,
+};
+
+static const struct tegra_pingroup_config i2c2_gen2 = {
+ .pingroup = TEGRA_PINGROUP_PTA,
+ .func = TEGRA_MUX_I2C2,
+};
+
+static struct tegra_i2c_platform_data ventana_i2c2_platform_data = {
+ .adapter_nr = 1,
+ .bus_count = 2,
+ .bus_clk_rate = { 100000, 10000 },
+ .bus_mux = { &i2c2_ddc, &i2c2_gen2 },
+ .bus_mux_len = { 1, 1 },
+ .slave_addr = 0x00FC,
+};
+
+static struct tegra_i2c_platform_data ventana_i2c3_platform_data = {
+ .adapter_nr = 3,
+ .bus_count = 1,
+ .bus_clk_rate = { 400000, 0 },
+ .slave_addr = 0x00FC,
+};
+
+static struct tegra_i2c_platform_data ventana_dvc_platform_data = {
+ .adapter_nr = 4,
+ .bus_count = 1,
+ .bus_clk_rate = { 400000, 0 },
+ .is_dvc = true,
+};
+
+static struct wm8903_platform_data ventana_wm8903_pdata = {
+ .irq_active_low = 0,
+ .micdet_cfg = 0,
+ .micdet_delay = 100,
+ .gpio_base = VENTANA_GPIO_WM8903(0),
+ .gpio_cfg = {
+ (WM8903_GPn_FN_DMIC_LR_CLK_OUTPUT << WM8903_GP1_FN_SHIFT),
+ (WM8903_GPn_FN_DMIC_LR_CLK_OUTPUT << WM8903_GP2_FN_SHIFT) |
+ WM8903_GP2_DIR,
+ 0,
+ WM8903_GPIO_NO_CONFIG,
+ WM8903_GPIO_NO_CONFIG,
+ },
+};
+
+static struct i2c_board_info __initdata wm8903_board_info = {
+ I2C_BOARD_INFO("wm8903", 0x1a),
+ .platform_data = &ventana_wm8903_pdata,
+ .irq = TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_CDC_IRQ),
+};
+
+static void ventana_i2c_init(void)
+{
+ tegra_i2c_device1.dev.platform_data = &ventana_i2c1_platform_data;
+ tegra_i2c_device2.dev.platform_data = &ventana_i2c2_platform_data;
+ tegra_i2c_device3.dev.platform_data = &ventana_i2c3_platform_data;
+ tegra_i2c_device4.dev.platform_data = &ventana_dvc_platform_data;
+
+ platform_device_register(&tegra_i2c_device1);
+ platform_device_register(&tegra_i2c_device2);
+ platform_device_register(&tegra_i2c_device3);
+ platform_device_register(&tegra_i2c_device4);
+
+ i2c_register_board_info(0, &wm8903_board_info, 1);
+}
+static struct platform_device *ventana_uart_devices[] __initdata = {
+ &tegra_uartb_device,
+ &tegra_uartc_device,
+ &tegra_uartd_device,
+};
+
+static struct uart_clk_parent uart_parent_clk[] = {
+ [0] = {.name = "pll_p"},
+ [1] = {.name = "pll_m"},
+ [2] = {.name = "clk_m"},
+};
+
+static struct tegra_uart_platform_data ventana_uart_pdata;
+
+static void __init uart_debug_init(void)
+{
+ unsigned long rate;
+ struct clk *c;
+
+ /* UARTD is the debug port. */
+ pr_info("Selecting UARTD as the debug console\n");
+ ventana_uart_devices[2] = &debug_uartd_device;
+ debug_uart_port_base = ((struct plat_serial8250_port *)(
+ debug_uartd_device.dev.platform_data))->mapbase;
+ debug_uart_clk = clk_get_sys("serial8250.0", "uartd");
+
+ /* Clock enable for the debug channel */
+ if (!IS_ERR_OR_NULL(debug_uart_clk)) {
+ rate = ((struct plat_serial8250_port *)(
+ debug_uartd_device.dev.platform_data))->uartclk;
+ pr_info("The debug console clock name is %s\n",
+ debug_uart_clk->name);
+ c = tegra_get_clock_by_name("pll_p");
+ if (IS_ERR_OR_NULL(c))
+ pr_err("Not getting the parent clock pll_p\n");
+ else
+ clk_set_parent(debug_uart_clk, c);
+
+ clk_enable(debug_uart_clk);
+ clk_set_rate(debug_uart_clk, rate);
+ } else {
+ pr_err("Not getting the clock %s for debug console\n",
+ debug_uart_clk->name);
+ }
+}
+
+static void __init ventana_uart_init(void)
+{
+ int i;
+ struct clk *c;
+
+ for (i = 0; i < ARRAY_SIZE(uart_parent_clk); ++i) {
+ c = tegra_get_clock_by_name(uart_parent_clk[i].name);
+ if (IS_ERR_OR_NULL(c)) {
+ pr_err("Not able to get the clock for %s\n",
+ uart_parent_clk[i].name);
+ continue;
+ }
+ uart_parent_clk[i].parent_clk = c;
+ uart_parent_clk[i].fixed_clk_rate = clk_get_rate(c);
+ }
+ ventana_uart_pdata.parent_clk_list = uart_parent_clk;
+ ventana_uart_pdata.parent_clk_count = ARRAY_SIZE(uart_parent_clk);
+ tegra_uartb_device.dev.platform_data = &ventana_uart_pdata;
+ tegra_uartc_device.dev.platform_data = &ventana_uart_pdata;
+ tegra_uartd_device.dev.platform_data = &ventana_uart_pdata;
+
+ /* Register low speed only if it is selected */
+ if (!is_tegra_debug_uartport_hs())
+ uart_debug_init();
+
+ platform_add_devices(ventana_uart_devices,
+ ARRAY_SIZE(ventana_uart_devices));
+}
+
+#ifdef CONFIG_KEYBOARD_GPIO
+#define GPIO_KEY(_id, _gpio, _iswake) \
+ { \
+ .code = _id, \
+ .gpio = TEGRA_GPIO_##_gpio, \
+ .active_low = 1, \
+ .desc = #_id, \
+ .type = EV_KEY, \
+ .wakeup = _iswake, \
+ .debounce_interval = 10, \
+ }
+
+static struct gpio_keys_button ventana_keys[] = {
+ [0] = GPIO_KEY(KEY_FIND, PQ3, 0),
+ [1] = GPIO_KEY(KEY_HOME, PQ1, 0),
+ [2] = GPIO_KEY(KEY_BACK, PQ2, 0),
+ [3] = GPIO_KEY(KEY_VOLUMEUP, PQ5, 0),
+ [4] = GPIO_KEY(KEY_VOLUMEDOWN, PQ4, 0),
+ [5] = GPIO_KEY(KEY_POWER, PV2, 1),
+ [6] = GPIO_KEY(KEY_MENU, PC7, 0),
+};
+
+#define PMC_WAKE_STATUS 0x14
+
+static int ventana_wakeup_key(void)
+{
+ unsigned long status =
+ readl(IO_ADDRESS(TEGRA_PMC_BASE) + PMC_WAKE_STATUS);
+
+ return status & TEGRA_WAKE_GPIO_PV2 ? KEY_POWER : KEY_RESERVED;
+}
+
+static struct gpio_keys_platform_data ventana_keys_platform_data = {
+ .buttons = ventana_keys,
+ .nbuttons = ARRAY_SIZE(ventana_keys),
+ .wakeup_key = ventana_wakeup_key,
+};
+
+static struct platform_device ventana_keys_device = {
+ .name = "gpio-keys",
+ .id = 0,
+ .dev = {
+ .platform_data = &ventana_keys_platform_data,
+ },
+};
+
+static void ventana_keys_init(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ventana_keys); i++)
+ tegra_gpio_enable(ventana_keys[i].gpio);
+}
+#endif
+
+static struct platform_device tegra_camera = {
+ .name = "tegra_camera",
+ .id = -1,
+};
+
+static struct tegra_wm8903_platform_data ventana_audio_pdata = {
+ .gpio_spkr_en = TEGRA_GPIO_SPKR_EN,
+ .gpio_hp_det = TEGRA_GPIO_HP_DET,
+ .gpio_hp_mute = -1,
+ .gpio_int_mic_en = TEGRA_GPIO_INT_MIC_EN,
+ .gpio_ext_mic_en = TEGRA_GPIO_EXT_MIC_EN,
+};
+
+static struct platform_device ventana_audio_device = {
+ .name = "tegra-snd-wm8903",
+ .id = 0,
+ .dev = {
+ .platform_data = &ventana_audio_pdata,
+ },
+};
+
+static struct platform_device *ventana_devices[] __initdata = {
+ &tegra_pmu_device,
+ &tegra_gart_device,
+ &tegra_aes_device,
+#ifdef CONFIG_KEYBOARD_GPIO
+ &ventana_keys_device,
+#endif
+ &tegra_wdt_device,
+ &tegra_avp_device,
+ &tegra_camera,
+ &tegra_i2s_device1,
+ &tegra_i2s_device2,
+ &tegra_spdif_device,
+ &tegra_das_device,
+ &spdif_dit_device,
+ &bluetooth_dit_device,
+ &ventana_bcm4329_rfkill_device,
+ &tegra_pcm_device,
+ &ventana_audio_device,
+};
+
+
+static struct mxt_platform_data atmel_mxt_info = {
+ .x_line = 27,
+ .y_line = 42,
+ .x_size = 768,
+ .y_size = 1366,
+ .blen = 0x20,
+ .threshold = 0x3C,
+ .voltage = 3300000,
+ .orient = MXT_ROTATED_90,
+ .irqflags = IRQF_TRIGGER_FALLING,
+};
+
+static struct i2c_board_info __initdata i2c_info[] = {
+ {
+ I2C_BOARD_INFO("atmel_mxt_ts", 0x5A),
+ .irq = TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PV6),
+ .platform_data = &atmel_mxt_info,
+ },
+};
+
+static int __init ventana_touch_init_atmel(void)
+{
+ tegra_gpio_enable(TEGRA_GPIO_PV6);
+ tegra_gpio_enable(TEGRA_GPIO_PQ7);
+
+ gpio_request(TEGRA_GPIO_PV6, "atmel-irq");
+ gpio_direction_input(TEGRA_GPIO_PV6);
+
+ gpio_request(TEGRA_GPIO_PQ7, "atmel-reset");
+ gpio_direction_output(TEGRA_GPIO_PQ7, 0);
+ msleep(1);
+ gpio_set_value(TEGRA_GPIO_PQ7, 1);
+ msleep(100);
+
+ i2c_register_board_info(0, i2c_info, 1);
+
+ return 0;
+}
+
+static struct panjit_i2c_ts_platform_data panjit_data = {
+ .gpio_reset = TEGRA_GPIO_PQ7,
+};
+
+static struct i2c_board_info __initdata ventana_i2c_bus1_touch_info[] = {
+ {
+ I2C_BOARD_INFO("panjit_touch", 0x3),
+ .irq = TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PV6),
+ .platform_data = &panjit_data,
+ },
+};
+
+static int __init ventana_touch_init_panjit(void)
+{
+ tegra_gpio_enable(TEGRA_GPIO_PV6);
+
+ tegra_gpio_enable(TEGRA_GPIO_PQ7);
+ i2c_register_board_info(0, ventana_i2c_bus1_touch_info, 1);
+
+ return 0;
+}
+
+static struct usb_phy_plat_data tegra_usb_phy_pdata[] = {
+ [0] = {
+ .instance = 0,
+ .vbus_irq = TPS6586X_INT_BASE + TPS6586X_INT_USB_DET,
+ .vbus_gpio = TEGRA_GPIO_PD0,
+ },
+ [1] = {
+ .instance = 1,
+ .vbus_gpio = -1,
+ },
+ [2] = {
+ .instance = 2,
+ .vbus_gpio = TEGRA_GPIO_PD3,
+ },
+};
+
+static struct tegra_ehci_platform_data tegra_ehci_pdata[] = {
+ [0] = {
+ .phy_config = &utmi_phy_config[0],
+ .operating_mode = TEGRA_USB_HOST,
+ .power_down_on_bus_suspend = 1,
+ },
+ [1] = {
+ .phy_config = &ulpi_phy_config,
+ .operating_mode = TEGRA_USB_HOST,
+ .power_down_on_bus_suspend = 1,
+ .phy_type = TEGRA_USB_PHY_TYPE_LINK_ULPI,
+ },
+ [2] = {
+ .phy_config = &utmi_phy_config[1],
+ .operating_mode = TEGRA_USB_HOST,
+ .power_down_on_bus_suspend = 1,
+ .hotplug = 1,
+ },
+};
+
+static struct tegra_otg_platform_data tegra_otg_pdata = {
+ .ehci_device = &tegra_ehci1_device,
+ .ehci_pdata = &tegra_ehci_pdata[0],
+};
+
+static int __init ventana_gps_init(void)
+{
+ struct clk *clk32 = clk_get_sys(NULL, "blink");
+ if (!IS_ERR(clk32)) {
+ clk_set_rate(clk32,clk32->parent->rate);
+ clk_enable(clk32);
+ }
+
+ tegra_gpio_enable(TEGRA_GPIO_PZ3);
+ return 0;
+}
+
+static void ventana_power_off(void)
+{
+ int ret;
+
+ ret = tps6586x_power_off();
+ if (ret)
+ pr_err("ventana: failed to power off\n");
+
+ while(1);
+}
+
+static void __init ventana_power_off_init(void)
+{
+ pm_power_off = ventana_power_off;
+}
+
+static void ventana_usb_init(void)
+{
+ tegra_usb_phy_init(tegra_usb_phy_pdata, ARRAY_SIZE(tegra_usb_phy_pdata));
+ /* OTG should be the first to be registered */
+ tegra_otg_device.dev.platform_data = &tegra_otg_pdata;
+ platform_device_register(&tegra_otg_device);
+
+ platform_device_register(&tegra_udc_device);
+ platform_device_register(&tegra_ehci2_device);
+
+ tegra_ehci3_device.dev.platform_data=&tegra_ehci_pdata[2];
+ platform_device_register(&tegra_ehci3_device);
+}
+
+static void __init tegra_ventana_init(void)
+{
+ struct board_info BoardInfo;
+
+ tegra_clk_init_from_table(ventana_clk_init_table);
+ ventana_pinmux_init();
+ ventana_i2c_init();
+ ventana_uart_init();
+ tegra_ehci2_device.dev.platform_data
+ = &ventana_ehci2_ulpi_platform_data;
+ platform_add_devices(ventana_devices, ARRAY_SIZE(ventana_devices));
+
+ ventana_sdhci_init();
+ ventana_charge_init();
+ ventana_regulator_init();
+ ventana_charger_init();
+
+ tegra_get_board_info(&BoardInfo);
+
+ /* boards with sku > 0 have atmel touch panels */
+ if (BoardInfo.sku) {
+ pr_info("Initializing Atmel touch driver\n");
+ ventana_touch_init_atmel();
+ } else {
+ pr_info("Initializing Panjit touch driver\n");
+ ventana_touch_init_panjit();
+ }
+
+#ifdef CONFIG_KEYBOARD_GPIO
+ ventana_keys_init();
+#endif
+
+ ventana_usb_init();
+ ventana_gps_init();
+ ventana_panel_init();
+ ventana_sensors_init();
+ ventana_bt_rfkill();
+ ventana_power_off_init();
+ ventana_emc_init();
+
+ ventana_setup_bluesleep();
+ tegra_release_bootloader_fb();
+}
+
+int __init tegra_ventana_protected_aperture_init(void)
+{
+ if (!machine_is_ventana())
+ return 0;
+
+ tegra_protected_aperture_init(tegra_grhost_aperture);
+ return 0;
+}
+late_initcall(tegra_ventana_protected_aperture_init);
+
+void __init tegra_ventana_reserve(void)
+{
+ if (memblock_reserve(0x0, 4096) < 0)
+ pr_warn("Cannot reserve first 4K of memory for safety\n");
+
+ tegra_reserve(SZ_256M, SZ_8M, SZ_16M);
+}
+
+MACHINE_START(VENTANA, "ventana")
+ .boot_params = 0x00000100,
+ .map_io = tegra_map_common_io,
+ .reserve = tegra_ventana_reserve,
+ .init_early = tegra_init_early,
+ .init_irq = tegra_init_irq,
+ .timer = &tegra_timer,
+ .init_machine = tegra_ventana_init,
+MACHINE_END
diff --git a/arch/arm/mach-tegra/board-ventana.h b/arch/arm/mach-tegra/board-ventana.h
new file mode 100644
index 000000000000..61b75363a19c
--- /dev/null
+++ b/arch/arm/mach-tegra/board-ventana.h
@@ -0,0 +1,89 @@
+/*
+ * arch/arm/mach-tegra/board-ventana.h
+ *
+ * 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.
+ *
+ */
+
+#ifndef _MACH_TEGRA_BOARD_VENTANA_H
+#define _MACH_TEGRA_BOARD_VENTANA_H
+
+int ventana_charge_init(void);
+int ventana_regulator_init(void);
+int ventana_sdhci_init(void);
+int ventana_pinmux_init(void);
+int ventana_panel_init(void);
+int ventana_sensors_init(void);
+int ventana_kbc_init(void);
+int ventana_emc_init(void);
+int ventana_charger_init(void);
+
+/* external gpios */
+
+/* TPS6586X gpios */
+#define TPS6586X_GPIO_BASE TEGRA_NR_GPIOS
+#define AVDD_DSI_CSI_ENB_GPIO (TPS6586X_GPIO_BASE + 1) /* gpio2 */
+
+/* TCA6416 gpios */
+#define TCA6416_GPIO_BASE (TEGRA_NR_GPIOS + 4)
+#define CAM1_PWR_DN_GPIO (TCA6416_GPIO_BASE + 0) /* gpio0 */
+#define CAM1_RST_L_GPIO (TCA6416_GPIO_BASE + 1) /* gpio1 */
+#define CAM1_AF_PWR_DN_L_GPIO (TCA6416_GPIO_BASE + 2) /* gpio2 */
+#define CAM1_LDO_SHUTDN_L_GPIO (TCA6416_GPIO_BASE + 3) /* gpio3 */
+#define CAM2_PWR_DN_GPIO (TCA6416_GPIO_BASE + 4) /* gpio4 */
+#define CAM2_RST_L_GPIO (TCA6416_GPIO_BASE + 5) /* gpio5 */
+#define CAM2_AF_PWR_DN_L_GPIO (TCA6416_GPIO_BASE + 6) /* gpio6 */
+#define CAM2_LDO_SHUTDN_L_GPIO (TCA6416_GPIO_BASE + 7) /* gpio7 */
+#define CAM3_PWR_DN_GPIO (TCA6416_GPIO_BASE + 8) /* gpio8 */
+#define CAM3_RST_L_GPIO (TCA6416_GPIO_BASE + 9) /* gpio9 */
+#define CAM3_AF_PWR_DN_L_GPIO (TCA6416_GPIO_BASE + 10) /* gpio10 */
+#define CAM3_LDO_SHUTDN_L_GPIO (TCA6416_GPIO_BASE + 11) /* gpio11 */
+#define CAM_I2C_MUX_RST_GPIO (TCA6416_GPIO_BASE + 15) /* gpio15 */
+#define TCA6416_GPIO_END (TCA6416_GPIO_BASE + 31)
+
+/* WM8903 GPIOs */
+#define VENTANA_GPIO_WM8903(_x_) (TCA6416_GPIO_END + 1 + (_x_))
+#define VENTANA_GPIO_WM8903_END VENTANA_GPIO_WM8903(4)
+
+/* Audio-related GPIOs */
+#define TEGRA_GPIO_CDC_IRQ TEGRA_GPIO_PX3
+#define TEGRA_GPIO_SPKR_EN VENTANA_GPIO_WM8903(2)
+#define TEGRA_GPIO_HP_DET TEGRA_GPIO_PW2
+#define TEGRA_GPIO_HP_DET TEGRA_GPIO_PW2
+#define TEGRA_GPIO_INT_MIC_EN TEGRA_GPIO_PX0
+#define TEGRA_GPIO_EXT_MIC_EN TEGRA_GPIO_PX1
+
+/* AC detect GPIO */
+#define AC_PRESENT_GPIO TEGRA_GPIO_PV3
+
+/* Interrupt numbers from external peripherals */
+#define TPS6586X_INT_BASE TEGRA_NR_IRQS
+#define TPS6586X_INT_END (TPS6586X_INT_BASE + 32)
+
+/* Invensense MPU Definitions */
+#define MPU_GYRO_NAME "mpu3050"
+#define MPU_GYRO_IRQ_GPIO TEGRA_GPIO_PZ4
+#define MPU_GYRO_ADDR 0x68
+#define MPU_GYRO_BUS_NUM 0
+#define MPU_GYRO_ORIENTATION { 0, -1, 0, -1, 0, 0, 0, 0, -1 }
+#define MPU_ACCEL_NAME "kxtf9"
+#define MPU_ACCEL_IRQ_GPIO 0 /* Disable ACCELIRQ: TEGRA_GPIO_PN4 */
+#define MPU_ACCEL_ADDR 0x0F
+#define MPU_ACCEL_BUS_NUM 0
+#define MPU_ACCEL_ORIENTATION { 0, -1, 0, -1, 0, 0, 0, 0, -1 }
+#define MPU_COMPASS_NAME "ak8975"
+#define MPU_COMPASS_IRQ_GPIO TEGRA_GPIO_PN5
+#define MPU_COMPASS_ADDR 0x0C
+#define MPU_COMPASS_BUS_NUM 4
+#define MPU_COMPASS_ORIENTATION { 1, 0, 0, 0, 1, 0, 0, 0, 1 }
+
+#endif
diff --git a/arch/arm/mach-tegra/board-whistler-baseband.c b/arch/arm/mach-tegra/board-whistler-baseband.c
new file mode 100644
index 000000000000..ff376d260306
--- /dev/null
+++ b/arch/arm/mach-tegra/board-whistler-baseband.c
@@ -0,0 +1,226 @@
+/*
+ * arch/arm/mach-tegra/board-whistler-baseband.c
+ *
+ * Copyright (C) 2011 NVIDIA Corporation
+ *
+ * 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/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/tegra_caif.h>
+#include <mach/tegra_usb_modem_power.h>
+
+#include "board.h"
+#include "board-whistler-baseband.h"
+
+static int baseband_phy_on(void);
+static int baseband_phy_off(void);
+static void baseband_phy_restore_start(void);
+static void baseband_phy_restore_end(void);
+
+static struct wake_lock mdm_wake_lock;
+
+static struct gpio modem_gpios[] = {
+ {MODEM_PWR_ON, GPIOF_OUT_INIT_LOW, "MODEM PWR ON"},
+ {MODEM_RESET, GPIOF_IN, "MODEM RESET"},
+ {BB_RST_OUT, GPIOF_IN, "BB RST OUT"},
+ {MDM2AP_ACK, GPIOF_IN, "MDM2AP_ACK"},
+ {AP2MDM_ACK2, GPIOF_OUT_INIT_HIGH, "AP2MDM ACK2"},
+ {AP2MDM_ACK, GPIOF_OUT_INIT_LOW, "AP2MDM ACK"},
+ {TEGRA_GPIO_PY3, GPIOF_IN, "ULPI_STP"},
+ {TEGRA_GPIO_PY1, GPIOF_OUT_INIT_LOW, "ULPI_DIR"},
+ {TEGRA_GPIO_PO1, GPIOF_OUT_INIT_LOW, "ULPI_D0"},
+ {TEGRA_GPIO_PO2, GPIOF_OUT_INIT_LOW, "ULPI_D1"},
+};
+
+static __initdata struct tegra_pingroup_config whistler_null_ulpi_pinmux[] = {
+ {TEGRA_PINGROUP_UAA, TEGRA_MUX_ULPI, TEGRA_PUPD_NORMAL,
+ TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_UAB, TEGRA_MUX_ULPI, TEGRA_PUPD_NORMAL,
+ TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_UDA, TEGRA_MUX_ULPI, TEGRA_PUPD_NORMAL,
+ TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_UAC, TEGRA_MUX_RSVD4, TEGRA_PUPD_NORMAL,
+ TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_SDIO1, TEGRA_MUX_UARTA, TEGRA_PUPD_PULL_UP,
+ TEGRA_TRI_NORMAL},
+};
+
+static struct tegra_ulpi_trimmer e1219_trimmer = { 10, 1, 1, 1 };
+
+static struct tegra_ulpi_config ehci2_null_ulpi_phy_config = {
+ .trimmer = &e1219_trimmer,
+ .post_phy_on = baseband_phy_on,
+ .pre_phy_off = baseband_phy_off,
+ .phy_restore_start = baseband_phy_restore_start,
+ .phy_restore_end = baseband_phy_restore_end,
+};
+
+static struct tegra_ehci_platform_data ehci2_null_ulpi_platform_data = {
+ .operating_mode = TEGRA_USB_HOST,
+ .power_down_on_bus_suspend = 0,
+ .phy_config = &ehci2_null_ulpi_phy_config,
+ .phy_type = TEGRA_USB_PHY_TYPE_NULL_ULPI,
+};
+
+static int __init tegra_null_ulpi_init(void)
+{
+ tegra_ehci2_device.dev.platform_data = &ehci2_null_ulpi_platform_data;
+ platform_device_register(&tegra_ehci2_device);
+ return 0;
+}
+
+static irqreturn_t mdm_start_thread(int irq, void *data)
+{
+ if (gpio_get_value(BB_RST_OUT)) {
+ pr_info("BB_RST_OUT high\n");
+ } else {
+ pr_info("BB_RST_OUT low\n");
+ /* hold wait lock to complete the enumeration */
+ wake_lock_timeout(&mdm_wake_lock, HZ * 10);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int baseband_phy_on(void)
+{
+ static bool phy_init;
+
+ if (!phy_init) {
+ /* set AP2MDM_ACK2 low */
+ gpio_set_value(AP2MDM_ACK2, 0);
+ phy_init = true;
+ }
+ pr_info("%s\n", __func__);
+ return 0;
+}
+
+static int baseband_phy_off(void)
+{
+ pr_info("%s\n", __func__);
+ return 0;
+}
+
+static void baseband_phy_restore_start(void)
+{
+ /* set AP2MDM_ACK2 high */
+ gpio_set_value(AP2MDM_ACK2, 1);
+}
+
+static void baseband_phy_restore_end(void)
+{
+ /* set AP2MDM_ACK2 low */
+ gpio_set_value(AP2MDM_ACK2, 0);
+}
+
+static void baseband_start(void)
+{
+ /*
+ * Leave baseband powered OFF.
+ * User-space daemons will take care of powering it up.
+ */
+ pr_info("%s\n", __func__);
+ gpio_set_value(MODEM_PWR_ON, 0);
+}
+
+static void baseband_reset(void)
+{
+ /* Initiate power cycle on baseband sub system */
+ pr_info("%s\n", __func__);
+ gpio_set_value(MODEM_PWR_ON, 0);
+ mdelay(200);
+ gpio_set_value(MODEM_PWR_ON, 1);
+}
+
+static int baseband_init(void)
+{
+ int irq;
+ int ret;
+
+ ret = gpio_request_array(modem_gpios, ARRAY_SIZE(modem_gpios));
+ if (ret)
+ return ret;
+
+ /* enable pull-up for BB_RST_OUT */
+ tegra_pinmux_set_pullupdown(TEGRA_PINGROUP_UAC,
+ TEGRA_PUPD_PULL_UP);
+
+ tegra_gpio_enable(MODEM_PWR_ON);
+ tegra_gpio_enable(MODEM_RESET);
+ tegra_gpio_enable(AP2MDM_ACK2);
+ tegra_gpio_enable(BB_RST_OUT);
+ tegra_gpio_enable(AP2MDM_ACK);
+ tegra_gpio_enable(MDM2AP_ACK);
+ tegra_gpio_enable(TEGRA_GPIO_PY3);
+ tegra_gpio_enable(TEGRA_GPIO_PY1);
+ tegra_gpio_enable(TEGRA_GPIO_PO1);
+ tegra_gpio_enable(TEGRA_GPIO_PO2);
+
+ /* export GPIO for user space access through sysfs */
+ gpio_export(MODEM_PWR_ON, false);
+
+ /* phy init */
+ tegra_null_ulpi_init();
+
+ wake_lock_init(&mdm_wake_lock, WAKE_LOCK_SUSPEND, "mdm_lock");
+
+ /* enable IRQ for BB_RST_OUT */
+ irq = gpio_to_irq(BB_RST_OUT);
+
+ ret = request_threaded_irq(irq, NULL, mdm_start_thread,
+ IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
+ "mdm_start", NULL);
+ if (ret < 0) {
+ pr_err("%s: request_threaded_irq error\n", __func__);
+ return ret;
+ }
+
+ ret = enable_irq_wake(irq);
+ if (ret) {
+ pr_err("%s: enable_irq_wake error\n", __func__);
+ free_irq(irq, NULL);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct tegra_modem_operations baseband_operations = {
+ .init = baseband_init,
+ .start = baseband_start,
+ .reset = baseband_reset,
+};
+
+static struct tegra_usb_modem_power_platform_data baseband_pdata = {
+ .ops = &baseband_operations,
+ .wake_gpio = MDM2AP_ACK2,
+ .flags = IRQF_TRIGGER_FALLING,
+};
+
+static struct platform_device icera_baseband_device = {
+ .name = "tegra_usb_modem_power",
+ .id = -1,
+ .dev = {
+ .platform_data = &baseband_pdata,
+ },
+};
+
+int __init whistler_baseband_init(void)
+{
+ tegra_pinmux_config_table(whistler_null_ulpi_pinmux,
+ ARRAY_SIZE(whistler_null_ulpi_pinmux));
+ platform_device_register(&icera_baseband_device);
+ return 0;
+}
diff --git a/arch/arm/mach-tegra/board-whistler-baseband.h b/arch/arm/mach-tegra/board-whistler-baseband.h
new file mode 100644
index 000000000000..b9e107f166f4
--- /dev/null
+++ b/arch/arm/mach-tegra/board-whistler-baseband.h
@@ -0,0 +1,76 @@
+/*
+ * arch/arm/mach-tegra/board-whistler-baseband.h
+ *
+ * Copyright (C) 2011 NVIDIA Corporation
+ *
+ * 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 BOARD_WHISTLER_BASEBAND_H
+#define BOARD_WHISTLER_BASEBAND_H
+
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/spi/spi.h>
+#include <linux/platform_data/tegra_usb.h>
+#include <mach/usb_phy.h>
+#include <linux/resource.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/wakelock.h>
+#include <asm/mach-types.h>
+#include <mach/pinmux.h>
+#include <mach/spi.h>
+#include "clock.h"
+#include "devices.h"
+#include "gpio-names.h"
+
+#define BOARD_WHISTLER_BASEBAND_U3XX 0
+#define BOARD_WHISTLER_BASEBAND_N731 1
+#define BOARD_WHISTLER_BASEBAND_SPI_LOOPBACK 2
+#define BOARD_WHISTLER_BASEBAND_HSIC 3
+
+#define TEGRA_CAIF_SSPI_GPIO_RESET TEGRA_GPIO_PV0
+#define TEGRA_CAIF_SSPI_GPIO_POWER TEGRA_GPIO_PV1
+#define TEGRA_CAIF_SSPI_GPIO_AWR TEGRA_GPIO_PZ0
+#define TEGRA_CAIF_SSPI_GPIO_CWR TEGRA_GPIO_PY6
+#define TEGRA_CAIF_SSPI_GPIO_SPI_INT TEGRA_GPIO_PO6
+#define TEGRA_CAIF_SSPI_GPIO_SS TEGRA_GPIO_PV2
+
+#define MODEM_PWR_ON TEGRA_GPIO_PV1
+#define MODEM_RESET TEGRA_GPIO_PV0
+
+/* Rainbow1 and 570 */
+#define AWR TEGRA_GPIO_PZ0
+#define CWR TEGRA_GPIO_PY6
+#define SPI_INT TEGRA_GPIO_PO6
+#define SPI_SLAVE_SEL TEGRA_GPIO_PV2
+
+/* Icera 450 GPIO */
+#define AP2MDM_ACK TEGRA_GPIO_PZ0
+#define MDM2AP_ACK TEGRA_GPIO_PY6
+#define AP2MDM_ACK2 TEGRA_GPIO_PU2
+#define MDM2AP_ACK2 TEGRA_GPIO_PV2
+#define BB_RST_OUT TEGRA_GPIO_PV3
+
+
+struct whistler_baseband {
+ struct tegra_clk_init_table *clk_init;
+ struct platform_device **platform_device;
+ int platform_device_size;
+ struct spi_board_info *spi_board_info;
+ int spi_board_info_size;
+};
+
+int whistler_baseband_init(void);
+#endif /* BOARD_WHISTLER_BASEBAND_H */
diff --git a/arch/arm/mach-tegra/board-whistler-kbc.c b/arch/arm/mach-tegra/board-whistler-kbc.c
new file mode 100644
index 000000000000..d46ae22bba05
--- /dev/null
+++ b/arch/arm/mach-tegra/board-whistler-kbc.c
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2010-2011 NVIDIA, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/device.h>
+
+#include <mach/clk.h>
+#include <mach/iomap.h>
+#include <mach/irqs.h>
+#include <mach/pinmux.h>
+#include <mach/iomap.h>
+#include <mach/io.h>
+#include <mach/kbc.h>
+
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+
+/*
+* Scrollwheel is connected to KBC pins but has it's own
+* driver using those pins as gpio.
+* In case of using scrollwheel Row3 and Col3/4/5
+* should NOT be configured as KBC
+*/
+#ifdef CONFIG_INPUT_ALPS_GPIO_SCROLLWHEEL
+#define WHISTLER_ROW_COUNT 3
+#define WHISTLER_COL_COUNT 2
+#else
+#define WHISTLER_ROW_COUNT 4
+#define WHISTLER_COL_COUNT 2
+#endif
+
+#ifdef CONFIG_INPUT_ALPS_GPIO_SCROLLWHEEL
+static const u32 whistler_keymap[] = {
+ KEY(0, 0, KEY_POWER),
+ KEY(0, 1, KEY_RESERVED),
+ KEY(1, 0, KEY_HOME),
+ KEY(1, 1, KEY_BACK),
+ KEY(2, 0, KEY_RESERVED),
+ KEY(2, 1, KEY_MENU),
+};
+#else
+static const u32 whistler_keymap[] = {
+ KEY(0, 0, KEY_POWER),
+ KEY(0, 1, KEY_RESERVED),
+ KEY(1, 0, KEY_HOME),
+ KEY(1, 1, KEY_BACK),
+ KEY(2, 0, KEY_RESERVED),
+ KEY(2, 1, KEY_MENU),
+ KEY(3, 0, KEY_RESERVED),
+ KEY(3, 1, KEY_RESERVED),
+};
+#endif
+
+static const struct matrix_keymap_data whistler_keymap_data = {
+ .keymap = whistler_keymap,
+ .keymap_size = ARRAY_SIZE(whistler_keymap),
+};
+
+static struct tegra_kbc_wake_key whistler_wake_cfg[] = {
+ [0] = {
+ .row = 0,
+ .col = 0,
+ },
+};
+
+static struct tegra_kbc_platform_data whistler_kbc_platform_data = {
+ .debounce_cnt = 20,
+ .repeat_cnt = 50 * 32,
+ .wake_cnt = 1,
+ .wake_cfg = &whistler_wake_cfg[0],
+ .keymap_data = &whistler_keymap_data,
+ .use_fn_map = false,
+ .wakeup = true,
+};
+
+static struct resource whistler_kbc_resources[] = {
+ [0] = {
+ .start = TEGRA_KBC_BASE,
+ .end = TEGRA_KBC_BASE + TEGRA_KBC_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = INT_KBC,
+ .end = INT_KBC,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device whistler_kbc_device = {
+ .name = "tegra-kbc",
+ .id = -1,
+ .dev = {
+ .platform_data = &whistler_kbc_platform_data,
+ },
+ .resource = whistler_kbc_resources,
+ .num_resources = ARRAY_SIZE(whistler_kbc_resources),
+};
+
+int __init whistler_kbc_init(void)
+{
+ struct tegra_kbc_platform_data *data = &whistler_kbc_platform_data;
+ int i;
+
+ pr_info("KBC: whistler_kbc_init\n");
+ for (i = 0; i < WHISTLER_ROW_COUNT; i++) {
+ data->pin_cfg[i].num = i;
+ data->pin_cfg[i].is_row = true;
+ data->pin_cfg[i].en = true;
+ }
+ for (i = 0; i < WHISTLER_COL_COUNT; i++) {
+ data->pin_cfg[i + KBC_PIN_GPIO_16].num = i;
+ data->pin_cfg[i + KBC_PIN_GPIO_16].en = true;
+ }
+
+ platform_device_register(&whistler_kbc_device);
+ return 0;
+}
diff --git a/arch/arm/mach-tegra/board-whistler-memory.c b/arch/arm/mach-tegra/board-whistler-memory.c
new file mode 100644
index 000000000000..918e96d5c463
--- /dev/null
+++ b/arch/arm/mach-tegra/board-whistler-memory.c
@@ -0,0 +1,569 @@
+/*
+ * Copyright (C) 2011 NVIDIA, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+
+#include "board-whistler.h"
+#include "tegra2_emc.h"
+#include "board.h"
+
+static const struct tegra_emc_table whistler_emc_tables_elpida_300Mhz[] = {
+ {
+ .rate = 25000, /* SDRAM frquency */
+ .regs = {
+ 0x00000002, /* RC */
+ 0x00000006, /* RFC */
+ 0x00000003, /* RAS */
+ 0x00000003, /* RP */
+ 0x00000006, /* R2W */
+ 0x00000004, /* W2R */
+ 0x00000002, /* R2P */
+ 0x00000009, /* W2P */
+ 0x00000003, /* RD_RCD */
+ 0x00000003, /* WR_RCD */
+ 0x00000002, /* RRD */
+ 0x00000002, /* REXT */
+ 0x00000002, /* WDV */
+ 0x00000004, /* QUSE */
+ 0x00000003, /* QRST */
+ 0x00000008, /* QSAFE */
+ 0x0000000b, /* RDV */
+ 0x0000004d, /* REFRESH */
+ 0x00000000, /* BURST_REFRESH_NUM */
+ 0x00000003, /* PDEX2WR */
+ 0x00000003, /* PDEX2RD */
+ 0x00000003, /* PCHG2PDEN */
+ 0x00000008, /* ACT2PDEN */
+ 0x00000001, /* AR2PDEN */
+ 0x0000000a, /* RW2PDEN */
+ 0x00000004, /* TXSR */
+ 0x00000003, /* TCKE */
+ 0x00000008, /* TFAW */
+ 0x00000004, /* TRPAB */
+ 0x00000006, /* TCLKSTABLE */
+ 0x00000002, /* TCLKSTOP */
+ 0x00000068, /* TREFBW */
+ 0x00000003, /* QUSE_EXTRA */
+ 0x00000003, /* FBIO_CFG6 */
+ 0x00000000, /* ODT_WRITE */
+ 0x00000000, /* ODT_READ */
+ 0x00000082, /* FBIO_CFG5 */
+ 0xa06a04ae, /* CFG_DIG_DLL */
+ 0x0001f000, /* DLL_XFORM_DQS */
+ 0x00000000, /* DLL_XFORM_QUSE */
+ 0x00000000, /* ZCAL_REF_CNT */
+ 0x00000003, /* ZCAL_WAIT_CNT */
+ 0x00000000, /* AUTO_CAL_INTERVAL */
+ 0x00000000, /* CFG_CLKTRIM_0 */
+ 0x00000000, /* CFG_CLKTRIM_1 */
+ 0x00000000, /* CFG_CLKTRIM_2 */
+ }
+ },
+ {
+ .rate = 50000, /* SDRAM frequency */
+ .regs = {
+ 0x00000003, /* RC */
+ 0x00000007, /* RFC */
+ 0x00000003, /* RAS */
+ 0x00000003, /* RP */
+ 0x00000006, /* R2W */
+ 0x00000004, /* W2R */
+ 0x00000002, /* R2P */
+ 0x00000009, /* W2P */
+ 0x00000003, /* RD_RCD */
+ 0x00000003, /* WR_RCD */
+ 0x00000002, /* RRD */
+ 0x00000002, /* REXT */
+ 0x00000002, /* WDV */
+ 0x00000005, /* QUSE */
+ 0x00000003, /* QRST */
+ 0x00000008, /* QSAFE */
+ 0x0000000b, /* RDV */
+ 0x0000009f, /* REFRESH */
+ 0x00000000, /* BURST_REFRESH_NUM */
+ 0x00000003, /* PDEX2WR */
+ 0x00000003, /* PDEX2RD */
+ 0x00000003, /* PCHG2PDEN */
+ 0x00000008, /* ACT2PDEN */
+ 0x00000001, /* AR2PDEN */
+ 0x0000000a, /* RW2PDEN */
+ 0x00000007, /* TXSR */
+ 0x00000003, /* TCKE */
+ 0x00000008, /* TFAW */
+ 0x00000004, /* TRPAB */
+ 0x00000006, /* TCLKSTABLE */
+ 0x00000002, /* TCLKSTOP */
+ 0x000000d0, /* TREFBW */
+ 0x00000004, /* QUSE_EXTRA */
+ 0x00000000, /* FBIO_CFG6 */
+ 0x00000000, /* ODT_WRITE */
+ 0x00000000, /* ODT_READ */
+ 0x00000082, /* FBIO_CFG5 */
+ 0xa06a04ae, /* CFG_DIG_DLL */
+ 0x0001f000, /* DLL_XFORM_DQS */
+ 0x00000000, /* DLL_XFORM_QUSE */
+ 0x00000000, /* ZCAL_REF_CNT */
+ 0x00000005, /* ZCAL_WAIT_CNT */
+ 0x00000000, /* AUTO_CAL_INTERVAL */
+ 0x00000000, /* CFG_CLKTRIM_0 */
+ 0x00000000, /* CFG_CLKTRIM_1 */
+ 0x00000000, /* CFG_CLKTRIM_2 */
+ }
+ },
+ {
+ .rate = 75000, /* SDRAM frequency */
+ .regs = {
+ 0x00000005, /* RC */
+ 0x0000000a, /* RFC */
+ 0x00000004, /* RAS */
+ 0x00000003, /* RP */
+ 0x00000006, /* R2W */
+ 0x00000004, /* W2R */
+ 0x00000002, /* R2P */
+ 0x00000009, /* W2P */
+ 0x00000003, /* RD_RCD */
+ 0x00000003, /* WR_RCD */
+ 0x00000002, /* RRD */
+ 0x00000002, /* REXT */
+ 0x00000002, /* WDV */
+ 0x00000005, /* QUSE */
+ 0x00000003, /* QRST */
+ 0x00000008, /* QSAFE */
+ 0x0000000b, /* RDV */
+ 0x000000ff, /* REFRESH */
+ 0x00000000, /* BURST_REFRESH_NUM */
+ 0x00000003, /* PDEX2WR */
+ 0x00000003, /* PDEX2RD */
+ 0x00000003, /* PCHG2PDEN */
+ 0x00000008, /* ACT2PDEN */
+ 0x00000001, /* AR2PDEN */
+ 0x0000000a, /* RW2PDEN */
+ 0x0000000b, /* TXSR */
+ 0x00000003, /* TCKE */
+ 0x00000008, /* TFAW */
+ 0x00000004, /* TRPAB */
+ 0x00000006, /* TCLKSTABLE */
+ 0x00000002, /* TCLKSTOP */
+ 0x00000138, /* TREFBW */
+ 0x00000004, /* QUSE_EXTRA */
+ 0x00000000, /* FBIO_CFG6 */
+ 0x00000000, /* ODT_WRITE */
+ 0x00000000, /* ODT_READ */
+ 0x00000082, /* FBIO_CFG5 */
+ 0xa06a04ae, /* CFG_DIG_DLL */
+ 0x0001f000, /* DLL_XFORM_DQS */
+ 0x00000000, /* DLL_XFORM_QUSE */
+ 0x00000000, /* ZCAL_REF_CNT */
+ 0x00000007, /* ZCAL_WAIT_CNT */
+ 0x00000000, /* AUTO_CAL_INTERVAL */
+ 0x00000000, /* CFG_CLKTRIM_0 */
+ 0x00000000, /* CFG_CLKTRIM_1 */
+ 0x00000000, /* CFG_CLKTRIM_2 */
+ }
+ },
+ {
+ .rate = 150000, /* SDRAM frequency */
+ .regs = {
+ 0x00000009, /* RC */
+ 0x00000014, /* RFC */
+ 0x00000007, /* RAS */
+ 0x00000004, /* RP */
+ 0x00000006, /* R2W */
+ 0x00000004, /* W2R */
+ 0x00000002, /* R2P */
+ 0x00000009, /* W2P */
+ 0x00000003, /* RD_RCD */
+ 0x00000003, /* WR_RCD */
+ 0x00000002, /* RRD */
+ 0x00000002, /* REXT */
+ 0x00000002, /* WDV */
+ 0x00000005, /* QUSE */
+ 0x00000003, /* QRST */
+ 0x00000008, /* QSAFE */
+ 0x0000000b, /* RDV */
+ 0x0000021f, /* REFRESH */
+ 0x00000000, /* BURST_REFRESH_NUM */
+ 0x00000003, /* PDEX2WR */
+ 0x00000003, /* PDEX2RD */
+ 0x00000004, /* PCHG2PDEN */
+ 0x00000008, /* ACT2PDEN */
+ 0x00000001, /* AR2PDEN */
+ 0x0000000a, /* RW2PDEN */
+ 0x00000015, /* TXSR */
+ 0x00000003, /* TCKE */
+ 0x00000008, /* TFAW */
+ 0x00000004, /* TRPAB */
+ 0x00000006, /* TCLKSTABLE */
+ 0x00000002, /* TCLKSTOP */
+ 0x00000270, /* TREFBW */
+ 0x00000000, /* QUSE_EXTRA */
+ 0x00000001, /* FBIO_CFG6 */
+ 0x00000000, /* ODT_WRITE */
+ 0x00000000, /* ODT_READ */
+ 0x00000082, /* FBIO_CFG5 */
+ 0xA04C04AE, /* CFG_DIG_DLL */
+ 0x007FC010, /* DLL_XFORM_DQS */
+ 0x00000000, /* DLL_XFORM_QUSE */
+ 0x00000000, /* ZCAL_REF_CNT */
+ 0x0000000e, /* ZCAL_WAIT_CNT */
+ 0x00000000, /* AUTO_CAL_INTERVAL */
+ 0x00000000, /* CFG_CLKTRIM_0 */
+ 0x00000000, /* CFG_CLKTRIM_1 */
+ 0x00000000, /* CFG_CLKTRIM_2 */
+ }
+ },
+ {
+ .rate = 300000, /* SDRAM frequency */
+ .regs = {
+ 0x00000012, /* RC */
+ 0x00000027, /* RFC */
+ 0x0000000D, /* RAS */
+ 0x00000007, /* RP */
+ 0x00000007, /* R2W */
+ 0x00000005, /* W2R */
+ 0x00000003, /* R2P */
+ 0x00000009, /* W2P */
+ 0x00000006, /* RD_RCD */
+ 0x00000006, /* WR_RCD */
+ 0x00000003, /* RRD */
+ 0x00000003, /* REXT */
+ 0x00000002, /* WDV */
+ 0x00000006, /* QUSE */
+ 0x00000003, /* QRST */
+ 0x00000009, /* QSAFE */
+ 0x0000000c, /* RDV */
+ 0x0000045f, /* REFRESH */
+ 0x00000000, /* BURST_REFRESH_NUM */
+ 0x00000004, /* PDEX2WR */
+ 0x00000004, /* PDEX2RD */
+ 0x00000007, /* PCHG2PDEN */
+ 0x00000008, /* ACT2PDEN */
+ 0x00000001, /* AR2PDEN */
+ 0x0000000e, /* RW2PDEN */
+ 0x0000002A, /* TXSR */
+ 0x00000003, /* TCKE */
+ 0x0000000F, /* TFAW */
+ 0x00000008, /* TRPAB */
+ 0x00000005, /* TCLKSTABLE */
+ 0x00000002, /* TCLKSTOP */
+ 0x000004E1, /* TREFBW */
+ 0x00000005, /* QUSE_EXTRA */
+ 0x00000002, /* FBIO_CFG6 */
+ 0x00000000, /* ODT_WRITE */
+ 0x00000000, /* ODT_READ */
+ 0x00000282, /* FBIO_CFG5 */
+ 0xE03C048B, /* CFG_DIG_DLL */
+ 0x007FC010, /* DLL_XFORM_DQS */
+ 0x00000000, /* DLL_XFORM_QUSE */
+ 0x00000000, /* ZCAL_REF_CNT */
+ 0x0000001B, /* ZCAL_WAIT_CNT */
+ 0x00000000, /* AUTO_CAL_INTERVAL */
+ 0x00000000, /* CFG_CLKTRIM_0 */
+ 0x00000000, /* CFG_CLKTRIM_1 */
+ 0x00000000, /* CFG_CLKTRIM_2 */
+ }
+ }
+};
+
+static const struct tegra_emc_table whistler_emc_tables_elpida_400Mhz[] = {
+ {
+ .rate = 23750, /* SDRAM frquency */
+ .regs = {
+ 0x00000002, /* RC */
+ 0x00000006, /* RFC */
+ 0x00000003, /* RAS */
+ 0x00000003, /* RP */
+ 0x00000006, /* R2W */
+ 0x00000004, /* W2R */
+ 0x00000002, /* R2P */
+ 0x0000000b, /* W2P */
+ 0x00000003, /* RD_RCD */
+ 0x00000003, /* WR_RCD */
+ 0x00000002, /* RRD */
+ 0x00000002, /* REXT */
+ 0x00000003, /* WDV */
+ 0x00000005, /* QUSE */
+ 0x00000004, /* QRST */
+ 0x00000008, /* QSAFE */
+ 0x0000000c, /* RDV */
+ 0x00000047, /* REFRESH */
+ 0x00000000, /* BURST_REFRESH_NUM */
+ 0x00000003, /* PDEX2WR */
+ 0x00000003, /* PDEX2RD */
+ 0x00000003, /* PCHG2PDEN */
+ 0x00000008, /* ACT2PDEN */
+ 0x00000001, /* AR2PDEN */
+ 0x0000000b, /* RW2PDEN */
+ 0x00000004, /* TXSR */
+ 0x00000003, /* TCKE */
+ 0x00000008, /* TFAW */
+ 0x00000004, /* TRPAB */
+ 0x00000008, /* TCLKSTABLE */
+ 0x00000002, /* TCLKSTOP */
+ 0x00000060, /* TREFBW */
+ 0x00000004, /* QUSE_EXTRA */
+ 0x00000003, /* FBIO_CFG6 */
+ 0x00000000, /* ODT_WRITE */
+ 0x00000000, /* ODT_READ */
+ 0x00000082, /* FBIO_CFG5 */
+ 0xa0ae04ae, /* CFG_DIG_DLL */
+ 0x0001f800, /* DLL_XFORM_DQS */
+ 0x00000000, /* DLL_XFORM_QUSE */
+ 0x00000000, /* ZCAL_REF_CNT */
+ 0x00000003, /* ZCAL_WAIT_CNT */
+ 0x00000000, /* AUTO_CAL_INTERVAL */
+ 0x00000000, /* CFG_CLKTRIM_0 */
+ 0x00000000, /* CFG_CLKTRIM_1 */
+ 0x00000000, /* CFG_CLKTRIM_2 */
+ }
+ },
+ {
+ .rate = 63333, /* SDRAM frquency */
+ .regs = {
+ 0x00000004, /* RC */
+ 0x00000009, /* RFC */
+ 0x00000003, /* RAS */
+ 0x00000003, /* RP */
+ 0x00000006, /* R2W */
+ 0x00000004, /* W2R */
+ 0x00000002, /* R2P */
+ 0x0000000b, /* W2P */
+ 0x00000003, /* RD_RCD */
+ 0x00000003, /* WR_RCD */
+ 0x00000002, /* RRD */
+ 0x00000002, /* REXT */
+ 0x00000003, /* WDV */
+ 0x00000006, /* QUSE */
+ 0x00000004, /* QRST */
+ 0x00000008, /* QSAFE */
+ 0x0000000c, /* RDV */
+ 0x000000c4, /* REFRESH */
+ 0x00000000, /* BURST_REFRESH_NUM */
+ 0x00000003, /* PDEX2WR */
+ 0x00000003, /* PDEX2RD */
+ 0x00000003, /* PCHG2PDEN */
+ 0x00000008, /* ACT2PDEN */
+ 0x00000001, /* AR2PDEN */
+ 0x0000000b, /* RW2PDEN */
+ 0x00000009, /* TXSR */
+ 0x00000003, /* TCKE */
+ 0x00000008, /* TFAW */
+ 0x00000004, /* TRPAB */
+ 0x00000008, /* TCLKSTABLE */
+ 0x00000002, /* TCLKSTOP */
+ 0x00000107, /* TREFBW */
+ 0x00000005, /* QUSE_EXTRA */
+ 0x00000000, /* FBIO_CFG6 */
+ 0x00000000, /* ODT_WRITE */
+ 0x00000000, /* ODT_READ */
+ 0x00000082, /* FBIO_CFG5 */
+ 0xa0ae04ae, /* CFG_DIG_DLL */
+ 0x0001f800, /* DLL_XFORM_DQS */
+ 0x00000000, /* DLL_XFORM_QUSE */
+ 0x00000000, /* ZCAL_REF_CNT */
+ 0x00000006, /* ZCAL_WAIT_CNT */
+ 0x00000000, /* AUTO_CAL_INTERVAL */
+ 0x00000000, /* CFG_CLKTRIM_0 */
+ 0x00000000, /* CFG_CLKTRIM_1 */
+ 0x00000000, /* CFG_CLKTRIM_2 */
+ }
+ },
+ {
+ .rate = 95000, /* SDRAM frquency */
+ .regs = {
+ 0x00000006, /* RC */
+ 0x0000000d, /* RFC */
+ 0x00000004, /* RAS */
+ 0x00000003, /* RP */
+ 0x00000006, /* R2W */
+ 0x00000004, /* W2R */
+ 0x00000002, /* R2P */
+ 0x0000000b, /* W2P */
+ 0x00000003, /* RD_RCD */
+ 0x00000003, /* WR_RCD */
+ 0x00000002, /* RRD */
+ 0x00000002, /* REXT */
+ 0x00000003, /* WDV */
+ 0x00000006, /* QUSE */
+ 0x00000004, /* QRST */
+ 0x00000008, /* QSAFE */
+ 0x0000000c, /* RDV */
+ 0x0000013f, /* REFRESH */
+ 0x00000000, /* BURST_REFRESH_NUM */
+ 0x00000003, /* PDEX2WR */
+ 0x00000003, /* PDEX2RD */
+ 0x00000003, /* PCHG2PDEN */
+ 0x00000008, /* ACT2PDEN */
+ 0x00000001, /* AR2PDEN */
+ 0x0000000b, /* RW2PDEN */
+ 0x0000000e, /* TXSR */
+ 0x00000003, /* TCKE */
+ 0x00000008, /* TFAW */
+ 0x00000004, /* TRPAB */
+ 0x00000008, /* TCLKSTABLE */
+ 0x00000002, /* TCLKSTOP */
+ 0x0000018c, /* TREFBW */
+ 0x00000005, /* QUSE_EXTRA */
+ 0x00000001, /* FBIO_CFG6 */
+ 0x00000000, /* ODT_WRITE */
+ 0x00000000, /* ODT_READ */
+ 0x00000082, /* FBIO_CFG5 */
+ 0xa0ae04ae, /* CFG_DIG_DLL */
+ 0x0001f000, /* DLL_XFORM_DQS */
+ 0x00000000, /* DLL_XFORM_QUSE */
+ 0x00000000, /* ZCAL_REF_CNT */
+ 0x00000009, /* ZCAL_WAIT_CNT */
+ 0x00000000, /* AUTO_CAL_INTERVAL */
+ 0x00000000, /* CFG_CLKTRIM_0 */
+ 0x00000000, /* CFG_CLKTRIM_1 */
+ 0x00000000, /* CFG_CLKTRIM_2 */
+ }
+ },
+ {
+ .rate = 190000, /* SDRAM frquency */
+ .regs = {
+ 0x0000000c, /* RC */
+ 0x00000019, /* RFC */
+ 0x00000008, /* RAS */
+ 0x00000004, /* RP */
+ 0x00000007, /* R2W */
+ 0x00000004, /* W2R */
+ 0x00000002, /* R2P */
+ 0x0000000b, /* W2P */
+ 0x00000004, /* RD_RCD */
+ 0x00000004, /* WR_RCD */
+ 0x00000002, /* RRD */
+ 0x00000003, /* REXT */
+ 0x00000003, /* WDV */
+ 0x00000006, /* QUSE */
+ 0x00000004, /* QRST */
+ 0x00000009, /* QSAFE */
+ 0x0000000d, /* RDV */
+ 0x000002bf, /* REFRESH */
+ 0x00000000, /* BURST_REFRESH_NUM */
+ 0x00000003, /* PDEX2WR */
+ 0x00000003, /* PDEX2RD */
+ 0x00000004, /* PCHG2PDEN */
+ 0x00000008, /* ACT2PDEN */
+ 0x00000001, /* AR2PDEN */
+ 0x0000000c, /* RW2PDEN */
+ 0x0000001b, /* TXSR */
+ 0x00000003, /* TCKE */
+ 0x0000000a, /* TFAW */
+ 0x00000004, /* TRPAB */
+ 0x00000008, /* TCLKSTABLE */
+ 0x00000002, /* TCLKSTOP */
+ 0x00000317, /* TREFBW */
+ 0x00000005, /* QUSE_EXTRA */
+ 0x00000002, /* FBIO_CFG6 */
+ 0x00000000, /* ODT_WRITE */
+ 0x00000000, /* ODT_READ */
+ 0x00000082, /* FBIO_CFG5 */
+ 0xa06204ae, /* CFG_DIG_DLL */
+ 0x007f7010, /* DLL_XFORM_DQS */
+ 0x00000000, /* DLL_XFORM_QUSE */
+ 0x00000000, /* ZCAL_REF_CNT */
+ 0x00000012, /* ZCAL_WAIT_CNT */
+ 0x00000000, /* AUTO_CAL_INTERVAL */
+ 0x00000000, /* CFG_CLKTRIM_0 */
+ 0x00000000, /* CFG_CLKTRIM_1 */
+ 0x00000000, /* CFG_CLKTRIM_2 */
+ }
+ },
+ {
+ .rate = 380000, /* SDRAM frquency */
+ .regs = {
+ 0x00000017, /* RC */
+ 0x00000032, /* RFC */
+ 0x00000010, /* RAS */
+ 0x00000007, /* RP */
+ 0x00000008, /* R2W */
+ 0x00000005, /* W2R */
+ 0x00000003, /* R2P */
+ 0x0000000b, /* W2P */
+ 0x00000007, /* RD_RCD */
+ 0x00000007, /* WR_RCD */
+ 0x00000004, /* RRD */
+ 0x00000003, /* REXT */
+ 0x00000003, /* WDV */
+ 0x00000007, /* QUSE */
+ 0x00000004, /* QRST */
+ 0x0000000a, /* QSAFE */
+ 0x0000000e, /* RDV */
+ 0x0000059f, /* REFRESH */
+ 0x00000000, /* BURST_REFRESH_NUM */
+ 0x00000004, /* PDEX2WR */
+ 0x00000004, /* PDEX2RD */
+ 0x00000007, /* PCHG2PDEN */
+ 0x00000008, /* ACT2PDEN */
+ 0x00000001, /* AR2PDEN */
+ 0x00000011, /* RW2PDEN */
+ 0x00000036, /* TXSR */
+ 0x00000003, /* TCKE */
+ 0x00000013, /* TFAW */
+ 0x00000008, /* TRPAB */
+ 0x00000007, /* TCLKSTABLE */
+ 0x00000002, /* TCLKSTOP */
+ 0x0000062d, /* TREFBW */
+ 0x00000006, /* QUSE_EXTRA */
+ 0x00000003, /* FBIO_CFG6 */
+ 0x00000000, /* ODT_WRITE */
+ 0x00000000, /* ODT_READ */
+ 0x00000282, /* FBIO_CFG5 */
+ 0xe044048b, /* CFG_DIG_DLL */
+ 0x007fb010, /* DLL_XFORM_DQS */
+ 0x00000000, /* DLL_XFORM_QUSE */
+ 0x00000000, /* ZCAL_REF_CNT */
+ 0x00000023, /* ZCAL_WAIT_CNT */
+ 0x00000000, /* AUTO_CAL_INTERVAL */
+ 0x00000000, /* CFG_CLKTRIM_0 */
+ 0x00000000, /* CFG_CLKTRIM_1 */
+ 0x00000000, /* CFG_CLKTRIM_2 */
+ }
+ }
+};
+
+static const struct tegra_emc_chip whistler_emc_chips[] = {
+ {
+ .description = "Elpida 300MHz",
+ .mem_manufacturer_id = 0x0303,
+ .mem_revision_id1 = 0,
+ .mem_revision_id2 = 0,
+ .mem_pid = 0x1414,
+ .table = whistler_emc_tables_elpida_300Mhz,
+ .table_size = ARRAY_SIZE(whistler_emc_tables_elpida_300Mhz)
+ },
+ {
+ .description = "Elpida 300MHz",
+ .mem_manufacturer_id = 0x0303,
+ .mem_revision_id1 = 0,
+ .mem_revision_id2 = 0,
+ .mem_pid = 0x5454,
+ .table = whistler_emc_tables_elpida_300Mhz,
+ .table_size = ARRAY_SIZE(whistler_emc_tables_elpida_300Mhz)
+ },
+};
+
+int __init whistler_emc_init(void)
+{
+ tegra_init_emc(whistler_emc_chips,
+ ARRAY_SIZE(whistler_emc_chips));
+
+ return 0;
+}
diff --git a/arch/arm/mach-tegra/board-whistler-panel.c b/arch/arm/mach-tegra/board-whistler-panel.c
new file mode 100644
index 000000000000..0f1912fdd427
--- /dev/null
+++ b/arch/arm/mach-tegra/board-whistler-panel.c
@@ -0,0 +1,383 @@
+/*
+ * arch/arm/mach-tegra/board-whistler-panel.c
+ *
+ * Copyright (c) 2010-2011, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/resource.h>
+#include <asm/mach-types.h>
+#include <linux/platform_device.h>
+#include <linux/earlysuspend.h>
+#include <linux/kernel.h>
+#include <linux/pwm_backlight.h>
+#include <linux/tegra_pwm_bl.h>
+#include <linux/nvhost.h>
+#include <mach/nvmap.h>
+#include <mach/irqs.h>
+#include <mach/iomap.h>
+#include <mach/dc.h>
+#include <mach/fb.h>
+
+#include "devices.h"
+#include "gpio-names.h"
+#include "board.h"
+
+#define whistler_hdmi_hpd TEGRA_GPIO_PN7
+
+#ifdef CONFIG_TEGRA_DC
+static struct regulator *whistler_hdmi_reg = NULL;
+static struct regulator *whistler_hdmi_pll = NULL;
+#endif
+
+/*
+ * In case which_pwm is TEGRA_PWM_PM0,
+ * gpio_conf_to_sfio should be TEGRA_GPIO_PW0: set LCD_CS1_N pin to SFIO
+ * In case which_pwm is TEGRA_PWM_PM1,
+ * gpio_conf_to_sfio should be TEGRA_GPIO_PW1: set LCD_M1 pin to SFIO
+ */
+static struct platform_tegra_pwm_backlight_data whistler_disp1_backlight_data = {
+ .which_dc = 0,
+ .which_pwm = TEGRA_PWM_PM1,
+ .max_brightness = 256,
+ .dft_brightness = 77,
+ .gpio_conf_to_sfio = TEGRA_GPIO_PW1,
+ .switch_to_sfio = &tegra_gpio_disable,
+ .period = 0x1F,
+ .clk_div = 3,
+ .clk_select = 2,
+};
+
+static struct platform_device whistler_disp1_backlight_device = {
+ .name = "tegra-pwm-bl",
+ .id = -1,
+ .dev = {
+ .platform_data = &whistler_disp1_backlight_data,
+ },
+};
+
+#ifdef CONFIG_TEGRA_DC
+static int whistler_hdmi_enable(void)
+{
+ if (!whistler_hdmi_reg) {
+ whistler_hdmi_reg = regulator_get(NULL, "avdd_hdmi"); /* LD011 */
+ if (IS_ERR_OR_NULL(whistler_hdmi_reg)) {
+ pr_err("hdmi: couldn't get regulator avdd_hdmi\n");
+ whistler_hdmi_reg = NULL;
+ return PTR_ERR(whistler_hdmi_reg);
+ }
+ }
+ regulator_enable(whistler_hdmi_reg);
+
+ if (!whistler_hdmi_pll) {
+ whistler_hdmi_pll = regulator_get(NULL, "avdd_hdmi_pll"); /* LD06 */
+ if (IS_ERR_OR_NULL(whistler_hdmi_pll)) {
+ pr_err("hdmi: couldn't get regulator avdd_hdmi_pll\n");
+ whistler_hdmi_pll = NULL;
+ regulator_disable(whistler_hdmi_reg);
+ whistler_hdmi_reg = NULL;
+ return PTR_ERR(whistler_hdmi_pll);
+ }
+ }
+ regulator_enable(whistler_hdmi_pll);
+ return 0;
+}
+
+static int whistler_hdmi_disable(void)
+{
+ regulator_disable(whistler_hdmi_reg);
+ regulator_disable(whistler_hdmi_pll);
+ return 0;
+}
+
+static struct resource whistler_disp1_resources[] = {
+ {
+ .name = "irq",
+ .start = INT_DISPLAY_GENERAL,
+ .end = INT_DISPLAY_GENERAL,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "regs",
+ .start = TEGRA_DISPLAY_BASE,
+ .end = TEGRA_DISPLAY_BASE + TEGRA_DISPLAY_SIZE-1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .name = "fbmem",
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct resource whistler_disp2_resources[] = {
+ {
+ .name = "irq",
+ .start = INT_DISPLAY_B_GENERAL,
+ .end = INT_DISPLAY_B_GENERAL,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "regs",
+ .start = TEGRA_DISPLAY2_BASE,
+ .end = TEGRA_DISPLAY2_BASE + TEGRA_DISPLAY2_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .name = "fbmem",
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .name = "hdmi_regs",
+ .start = TEGRA_HDMI_BASE,
+ .end = TEGRA_HDMI_BASE + TEGRA_HDMI_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct tegra_dc_mode whistler_panel_modes[] = {
+ {
+ .pclk = 27000000,
+ .h_ref_to_sync = 4,
+ .v_ref_to_sync = 2,
+ .h_sync_width = 10,
+ .v_sync_width = 3,
+ .h_back_porch = 20,
+ .v_back_porch = 3,
+ .h_active = 800,
+ .v_active = 480,
+ .h_front_porch = 70,
+ .v_front_porch = 3,
+ },
+};
+
+static struct tegra_dc_out_pin whistler_dc_out_pins[] = {
+ {
+ .name = TEGRA_DC_OUT_PIN_H_SYNC,
+ .pol = TEGRA_DC_OUT_PIN_POL_LOW,
+ },
+ {
+ .name = TEGRA_DC_OUT_PIN_V_SYNC,
+ .pol = TEGRA_DC_OUT_PIN_POL_LOW,
+ },
+ {
+ .name = TEGRA_DC_OUT_PIN_PIXEL_CLOCK,
+ .pol = TEGRA_DC_OUT_PIN_POL_LOW,
+ },
+};
+
+static u8 whistler_dc_out_pin_sel_config[] = {
+ TEGRA_PIN_OUT_CONFIG_SEL_LM1_PM1,
+};
+
+static struct tegra_dc_out whistler_disp1_out = {
+ .type = TEGRA_DC_OUT_RGB,
+
+ .align = TEGRA_DC_ALIGN_MSB,
+ .order = TEGRA_DC_ORDER_RED_BLUE,
+
+ .height = 54, /* mm */
+ .width = 90, /* mm */
+
+ .modes = whistler_panel_modes,
+ .n_modes = ARRAY_SIZE(whistler_panel_modes),
+
+ .out_pins = whistler_dc_out_pins,
+ .n_out_pins = ARRAY_SIZE(whistler_dc_out_pins),
+
+ .out_sel_configs = whistler_dc_out_pin_sel_config,
+ .n_out_sel_configs = ARRAY_SIZE(whistler_dc_out_pin_sel_config),
+};
+
+static struct tegra_dc_out whistler_disp2_out = {
+ .type = TEGRA_DC_OUT_HDMI,
+ .flags = TEGRA_DC_OUT_HOTPLUG_HIGH,
+
+ .dcc_bus = 1,
+ .hotplug_gpio = whistler_hdmi_hpd,
+
+ .max_pixclock = KHZ2PICOS(148500),
+
+ .align = TEGRA_DC_ALIGN_MSB,
+ .order = TEGRA_DC_ORDER_RED_BLUE,
+
+ .enable = whistler_hdmi_enable,
+ .disable = whistler_hdmi_disable,
+};
+
+static struct tegra_fb_data whistler_fb_data = {
+ .win = 0,
+ .xres = 800,
+ .yres = 480,
+ .bits_per_pixel = 32,
+ .flags = TEGRA_FB_FLIP_ON_PROBE,
+};
+
+static struct tegra_fb_data whistler_hdmi_fb_data = {
+ .win = 0,
+ .xres = 800,
+ .yres = 480,
+ .bits_per_pixel = 32,
+ .flags = TEGRA_FB_FLIP_ON_PROBE,
+};
+
+
+static struct tegra_dc_platform_data whistler_disp1_pdata = {
+ .flags = TEGRA_DC_FLAG_ENABLED,
+ .default_out = &whistler_disp1_out,
+ .fb = &whistler_fb_data,
+};
+
+static struct nvhost_device whistler_disp1_device = {
+ .name = "tegradc",
+ .id = 0,
+ .resource = whistler_disp1_resources,
+ .num_resources = ARRAY_SIZE(whistler_disp1_resources),
+ .dev = {
+ .platform_data = &whistler_disp1_pdata,
+ },
+};
+
+static struct tegra_dc_platform_data whistler_disp2_pdata = {
+ .flags = 0,
+ .default_out = &whistler_disp2_out,
+ .fb = &whistler_hdmi_fb_data,
+};
+
+static struct nvhost_device whistler_disp2_device = {
+ .name = "tegradc",
+ .id = 1,
+ .resource = whistler_disp2_resources,
+ .num_resources = ARRAY_SIZE(whistler_disp2_resources),
+ .dev = {
+ .platform_data = &whistler_disp2_pdata,
+ },
+};
+#endif
+
+static struct nvmap_platform_carveout whistler_carveouts[] = {
+ [0] = NVMAP_HEAP_CARVEOUT_IRAM_INIT,
+ [1] = {
+ .name = "generic-0",
+ .usage_mask = NVMAP_HEAP_CARVEOUT_GENERIC,
+ .base = 0x18C00000,
+ .size = SZ_128M - 0xC00000,
+ .buddy_size = SZ_32K,
+ },
+};
+
+static struct nvmap_platform_data whistler_nvmap_data = {
+ .carveouts = whistler_carveouts,
+ .nr_carveouts = ARRAY_SIZE(whistler_carveouts),
+};
+
+static struct platform_device whistler_nvmap_device = {
+ .name = "tegra-nvmap",
+ .id = -1,
+ .dev = {
+ .platform_data = &whistler_nvmap_data,
+ },
+};
+
+static struct platform_device *whistler_gfx_devices[] __initdata = {
+ &whistler_nvmap_device,
+#ifdef CONFIG_TEGRA_GRHOST
+ &tegra_grhost_device,
+#endif
+ &whistler_disp1_backlight_device,
+};
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+/* put early_suspend/late_resume handlers here for the display in order
+ * to keep the code out of the display driver, keeping it closer to upstream
+ */
+struct early_suspend whistler_panel_early_suspender;
+
+static void whistler_panel_early_suspend(struct early_suspend *h)
+{
+ unsigned i;
+ for (i = 0; i < num_registered_fb; i++)
+ fb_blank(registered_fb[i], FB_BLANK_POWERDOWN);
+#ifdef CONFIG_TEGRA_CONVSERVATIVE_GOV_ON_EARLYSUPSEND
+ cpufreq_save_default_governor();
+ cpufreq_set_conservative_governor();
+ cpufreq_set_conservative_governor_param(
+ SET_CONSERVATIVE_GOVERNOR_UP_THRESHOLD,
+ SET_CONSERVATIVE_GOVERNOR_DOWN_THRESHOLD);
+#endif
+}
+
+static void whistler_panel_late_resume(struct early_suspend *h)
+{
+ unsigned i;
+#ifdef CONFIG_TEGRA_CONVSERVATIVE_GOV_ON_EARLYSUPSEND
+ cpufreq_restore_default_governor();
+#endif
+ for (i = 0; i < num_registered_fb; i++)
+ fb_blank(registered_fb[i], FB_BLANK_UNBLANK);
+}
+#endif
+
+int __init whistler_panel_init(void)
+{
+ int err;
+ struct resource __maybe_unused *res;
+
+ tegra_gpio_enable(whistler_hdmi_hpd);
+ gpio_request(whistler_hdmi_hpd, "hdmi_hpd");
+ gpio_direction_input(whistler_hdmi_hpd);
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ whistler_panel_early_suspender.suspend = whistler_panel_early_suspend;
+ whistler_panel_early_suspender.resume = whistler_panel_late_resume;
+ whistler_panel_early_suspender.level = EARLY_SUSPEND_LEVEL_DISABLE_FB;
+ register_early_suspend(&whistler_panel_early_suspender);
+#endif
+ whistler_carveouts[1].base = tegra_carveout_start;
+ whistler_carveouts[1].size = tegra_carveout_size;
+
+ err = platform_add_devices(whistler_gfx_devices,
+ ARRAY_SIZE(whistler_gfx_devices));
+
+#if defined(CONFIG_TEGRA_GRHOST) && defined(CONFIG_TEGRA_DC)
+ res = nvhost_get_resource_byname(&whistler_disp1_device,
+ IORESOURCE_MEM, "fbmem");
+ res->start = tegra_fb_start;
+ res->end = tegra_fb_start + tegra_fb_size - 1;
+#endif
+
+ /* Copy the bootloader fb to the fb. */
+ tegra_move_framebuffer(tegra_fb_start, tegra_bootloader_fb_start,
+ min(tegra_fb_size, tegra_bootloader_fb_size));
+
+#if defined(CONFIG_TEGRA_GRHOST) && defined(CONFIG_TEGRA_DC)
+ res = nvhost_get_resource_byname(&whistler_disp2_device,
+ IORESOURCE_MEM, "fbmem");
+ res->start = tegra_fb2_start;
+ res->end = tegra_fb2_start + tegra_fb2_size - 1;
+
+ if (!err)
+ err = nvhost_device_register(&whistler_disp1_device);
+
+ if (!err)
+ err = nvhost_device_register(&whistler_disp2_device);
+#endif
+
+ return err;
+}
+
diff --git a/arch/arm/mach-tegra/board-whistler-pinmux.c b/arch/arm/mach-tegra/board-whistler-pinmux.c
new file mode 100644
index 000000000000..12e9975061ca
--- /dev/null
+++ b/arch/arm/mach-tegra/board-whistler-pinmux.c
@@ -0,0 +1,177 @@
+/*
+ * arch/arm/mach-tegra/board-whistler-pinmux.c
+ *
+ * Copyright (C) 2010-2011 NVIDIA Corporation
+ *
+ * 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/init.h>
+#include <linux/gpio.h>
+#include <mach/pinmux.h>
+
+#include "board-whistler.h"
+#include "gpio-names.h"
+
+#define DEFAULT_DRIVE(_name) \
+ { \
+ .pingroup = TEGRA_DRIVE_PINGROUP_##_name, \
+ .hsm = TEGRA_HSM_DISABLE, \
+ .schmitt = TEGRA_SCHMITT_ENABLE, \
+ .drive = TEGRA_DRIVE_DIV_1, \
+ .pull_down = TEGRA_PULL_31, \
+ .pull_up = TEGRA_PULL_31, \
+ .slew_rising = TEGRA_SLEW_SLOWEST, \
+ .slew_falling = TEGRA_SLEW_SLOWEST, \
+ }
+
+
+static __initdata struct tegra_drive_pingroup_config whistler_drive_pinmux[] = {
+ DEFAULT_DRIVE(DBG),
+ DEFAULT_DRIVE(DDC),
+ DEFAULT_DRIVE(VI1),
+ DEFAULT_DRIVE(VI2),
+ DEFAULT_DRIVE(SDIO1),
+};
+
+static __initdata struct tegra_pingroup_config whistler_pinmux[] = {
+ {TEGRA_PINGROUP_ATA, TEGRA_MUX_GMI, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_ATB, TEGRA_MUX_GMI, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_ATC, TEGRA_MUX_SDIO4, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_ATD, TEGRA_MUX_SDIO4, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_ATE, TEGRA_MUX_GMI, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_CDEV1, TEGRA_MUX_PLLA_OUT, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_CDEV2, TEGRA_MUX_OSC, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_TRISTATE},
+ {TEGRA_PINGROUP_CRTP, TEGRA_MUX_CRT, TEGRA_PUPD_NORMAL, TEGRA_TRI_TRISTATE},
+ {TEGRA_PINGROUP_CSUS, TEGRA_MUX_VI_SENSOR_CLK, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_DAP1, TEGRA_MUX_DAP1, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_DAP2, TEGRA_MUX_DAP2, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_DAP3, TEGRA_MUX_DAP3, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_DAP4, TEGRA_MUX_DAP4, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_DDC, TEGRA_MUX_I2C2, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_DTA, TEGRA_MUX_VI, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_DTB, TEGRA_MUX_VI, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_DTC, TEGRA_MUX_VI, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_DTD, TEGRA_MUX_VI, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_DTE, TEGRA_MUX_RSVD1, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_DTF, TEGRA_MUX_I2C3, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_GMA, TEGRA_MUX_GMI, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_GMB, TEGRA_MUX_GMI, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_GMC, TEGRA_MUX_GMI, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_GMD, TEGRA_MUX_GMI, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_GME, TEGRA_MUX_DAP5, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_GPU, TEGRA_MUX_GMI, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_GPU7, TEGRA_MUX_RTCK, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_GPV, TEGRA_MUX_PCIE, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_HDINT, TEGRA_MUX_HDMI, TEGRA_PUPD_NORMAL, TEGRA_TRI_TRISTATE},
+ {TEGRA_PINGROUP_I2CP, TEGRA_MUX_I2C, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_IRRX, TEGRA_MUX_UARTB, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_IRTX, TEGRA_MUX_UARTB, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_KBCA, TEGRA_MUX_KBC, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_KBCB, TEGRA_MUX_SDIO2, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_KBCC, TEGRA_MUX_KBC, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_KBCD, TEGRA_MUX_SDIO2, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_KBCE, TEGRA_MUX_KBC, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_KBCF, TEGRA_MUX_KBC, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LCSN, TEGRA_MUX_SPI3, TEGRA_PUPD_PULL_UP, TEGRA_TRI_TRISTATE},
+ {TEGRA_PINGROUP_LD0, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LD1, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LD10, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LD11, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LD12, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LD13, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LD14, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LD15, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LD16, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LD17, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LD2, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LD3, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LD4, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LD5, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LD6, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LD7, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LD8, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LD9, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LDC, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_PULL_UP, TEGRA_TRI_TRISTATE},
+ {TEGRA_PINGROUP_LDI, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LHP0, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LHP1, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LHP2, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LHS, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LM0, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_NORMAL, TEGRA_TRI_TRISTATE},
+ {TEGRA_PINGROUP_LM1, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LPP, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LPW0, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_PULL_UP, TEGRA_TRI_TRISTATE},
+ {TEGRA_PINGROUP_LPW1, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_PULL_UP, TEGRA_TRI_TRISTATE},
+ {TEGRA_PINGROUP_LPW2, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LSC0, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LSC1, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_PULL_UP, TEGRA_TRI_TRISTATE},
+ {TEGRA_PINGROUP_LSCK, TEGRA_MUX_SPI3, TEGRA_PUPD_PULL_UP, TEGRA_TRI_TRISTATE},
+ {TEGRA_PINGROUP_LSDA, TEGRA_MUX_SPI3, TEGRA_PUPD_PULL_UP, TEGRA_TRI_TRISTATE},
+ {TEGRA_PINGROUP_LSDI, TEGRA_MUX_SPI3, TEGRA_PUPD_PULL_UP, TEGRA_TRI_TRISTATE},
+ {TEGRA_PINGROUP_LSPI, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LVP0, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LVP1, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LVS, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_OWC, TEGRA_MUX_OWR, TEGRA_PUPD_PULL_UP, TEGRA_TRI_TRISTATE},
+ {TEGRA_PINGROUP_PMC, TEGRA_MUX_PWR_ON, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_PTA, TEGRA_MUX_HDMI, TEGRA_PUPD_PULL_UP, TEGRA_TRI_TRISTATE},
+ {TEGRA_PINGROUP_RM, TEGRA_MUX_I2C, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_SDB, TEGRA_MUX_SDIO3, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_SDC, TEGRA_MUX_SDIO3, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_SDD, TEGRA_MUX_SDIO3, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_SDIO1, TEGRA_MUX_RSVD1, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_SLXA, TEGRA_MUX_SDIO3, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_SLXC, TEGRA_MUX_SDIO3, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_SLXD, TEGRA_MUX_SDIO3, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_SLXK, TEGRA_MUX_SDIO3, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_SPDI, TEGRA_MUX_RSVD2, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_SPDO, TEGRA_MUX_RSVD2, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_SPIA, TEGRA_MUX_SPI3, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_TRISTATE},
+ {TEGRA_PINGROUP_SPIB, TEGRA_MUX_SPI3, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_TRISTATE},
+ {TEGRA_PINGROUP_SPIC, TEGRA_MUX_SPI3, TEGRA_PUPD_PULL_UP, TEGRA_TRI_TRISTATE},
+ {TEGRA_PINGROUP_SPID, TEGRA_MUX_SPI2_ALT, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_SPIE, TEGRA_MUX_SPI2_ALT, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_SPIF, TEGRA_MUX_SPI2, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_SPIG, TEGRA_MUX_SPI2_ALT, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_SPIH, TEGRA_MUX_SPI2_ALT, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_UAA, TEGRA_MUX_UARTA, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_UAB, TEGRA_MUX_UARTA, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_UAC, TEGRA_MUX_OWR, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_UAD, TEGRA_MUX_IRDA, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_UCA, TEGRA_MUX_UARTC, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_UCB, TEGRA_MUX_UARTC, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_UDA, TEGRA_MUX_SPI1, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_CK32, TEGRA_MUX_NONE, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_DDRC, TEGRA_MUX_NONE, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_PMCA, TEGRA_MUX_NONE, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_PMCB, TEGRA_MUX_NONE, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_PMCC, TEGRA_MUX_NONE, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_PMCD, TEGRA_MUX_NONE, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_PMCE, TEGRA_MUX_NONE, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_XM2C, TEGRA_MUX_NONE, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_XM2D, TEGRA_MUX_NONE, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+};
+
+static struct tegra_gpio_table gpio_table[] = {
+ { .gpio = TEGRA_GPIO_HP_DET, .enable = true },
+};
+
+int __init whistler_pinmux_init(void)
+{
+ tegra_pinmux_config_table(whistler_pinmux, ARRAY_SIZE(whistler_pinmux));
+ tegra_drive_pinmux_config_table(whistler_drive_pinmux,
+ ARRAY_SIZE(whistler_drive_pinmux));
+
+ tegra_gpio_config(gpio_table, ARRAY_SIZE(gpio_table));
+ return 0;
+}
diff --git a/arch/arm/mach-tegra/board-whistler-power.c b/arch/arm/mach-tegra/board-whistler-power.c
new file mode 100644
index 000000000000..89ca697b92ba
--- /dev/null
+++ b/arch/arm/mach-tegra/board-whistler-power.c
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2010-2011 NVIDIA, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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/i2c.h>
+#include <linux/pda_power.h>
+#include <linux/platform_device.h>
+#include <linux/resource.h>
+#include <linux/regulator/machine.h>
+#include <linux/mfd/max8907c.h>
+#include <linux/regulator/max8907c-regulator.h>
+#include <linux/gpio.h>
+#include <linux/io.h>
+
+#include <mach/iomap.h>
+#include <mach/irqs.h>
+
+#include "gpio-names.h"
+#include "fuse.h"
+#include "pm.h"
+#include "wakeups-t2.h"
+#include "board.h"
+
+#define PMC_CTRL 0x0
+#define PMC_CTRL_INTR_LOW (1 << 17)
+
+static struct regulator_consumer_supply max8907c_SD1_supply[] = {
+ REGULATOR_SUPPLY("vdd_cpu", NULL),
+};
+
+static struct regulator_consumer_supply max8907c_SD2_supply[] = {
+ REGULATOR_SUPPLY("vdd_core", NULL),
+ REGULATOR_SUPPLY("vdd_aon", NULL),
+};
+
+static struct regulator_consumer_supply max8907c_SD3_supply[] = {
+ REGULATOR_SUPPLY("vddio_sys", NULL),
+};
+
+static struct regulator_consumer_supply max8907c_LDO1_supply[] = {
+ REGULATOR_SUPPLY("vddio_rx_ddr", NULL),
+};
+
+static struct regulator_consumer_supply max8907c_LDO2_supply[] = {
+ REGULATOR_SUPPLY("avdd_plla", NULL),
+};
+
+static struct regulator_consumer_supply max8907c_LDO3_supply[] = {
+ REGULATOR_SUPPLY("vdd_vcom_1v8b", NULL),
+};
+
+static struct regulator_consumer_supply max8907c_LDO4_supply[] = {
+ REGULATOR_SUPPLY("avdd_usb", NULL),
+ REGULATOR_SUPPLY("avdd_usb_pll", NULL),
+};
+
+static struct regulator_consumer_supply max8907c_LDO5_supply[] = {
+};
+
+static struct regulator_consumer_supply max8907c_LDO6_supply[] = {
+ REGULATOR_SUPPLY("avdd_hdmi_pll", NULL),
+};
+
+static struct regulator_consumer_supply max8907c_LDO7_supply[] = {
+ REGULATOR_SUPPLY("avddio_audio", NULL),
+};
+
+static struct regulator_consumer_supply max8907c_LDO8_supply[] = {
+ REGULATOR_SUPPLY("vdd_vcom_3v0", NULL),
+};
+
+static struct regulator_consumer_supply max8907c_LDO9_supply[] = {
+ REGULATOR_SUPPLY("vdd_cam1", NULL),
+};
+
+static struct regulator_consumer_supply max8907c_LDO10_supply[] = {
+ REGULATOR_SUPPLY("avdd_usb_ic", NULL),
+};
+
+static struct regulator_consumer_supply max8907c_LDO11_supply[] = {
+ REGULATOR_SUPPLY("vddio_pex_clk", NULL),
+ REGULATOR_SUPPLY("avdd_hdmi", NULL),
+};
+
+static struct regulator_consumer_supply max8907c_LDO12_supply[] = {
+ REGULATOR_SUPPLY("vddio_sdio", NULL),
+};
+
+static struct regulator_consumer_supply max8907c_LDO13_supply[] = {
+ REGULATOR_SUPPLY("vdd_vcore_phtn", NULL),
+ REGULATOR_SUPPLY("vdd_vcore_af", NULL),
+};
+
+static struct regulator_consumer_supply max8907c_LDO14_supply[] = {
+ REGULATOR_SUPPLY("avdd_vdac", NULL),
+};
+
+static struct regulator_consumer_supply max8907c_LDO15_supply[] = {
+ REGULATOR_SUPPLY("vdd_vcore_temp", NULL),
+ REGULATOR_SUPPLY("vdd_vcore_hdcp", NULL),
+};
+
+static struct regulator_consumer_supply max8907c_LDO16_supply[] = {
+ REGULATOR_SUPPLY("vdd_vbrtr", NULL),
+};
+
+static struct regulator_consumer_supply max8907c_LDO17_supply[] = {
+ REGULATOR_SUPPLY("vddio_mipi", NULL),
+};
+
+static struct regulator_consumer_supply max8907c_LDO18_supply[] = {
+ REGULATOR_SUPPLY("vddio_vi", NULL),
+ REGULATOR_SUPPLY("vcsi", "tegra_camera"),
+};
+
+static struct regulator_consumer_supply max8907c_LDO19_supply[] = {
+ REGULATOR_SUPPLY("vddio_lx", NULL),
+};
+
+static struct regulator_consumer_supply max8907c_LDO20_supply[] = {
+ REGULATOR_SUPPLY("vddio_ddr_1v2", NULL),
+ REGULATOR_SUPPLY("vddio_hsic", NULL),
+};
+
+#define MAX8907C_REGULATOR_DEVICE(_id, _minmv, _maxmv) \
+static struct regulator_init_data max8907c_##_id##_data = { \
+ .constraints = { \
+ .min_uV = (_minmv), \
+ .max_uV = (_maxmv), \
+ .valid_modes_mask = (REGULATOR_MODE_NORMAL | \
+ REGULATOR_MODE_STANDBY), \
+ .valid_ops_mask = (REGULATOR_CHANGE_MODE | \
+ REGULATOR_CHANGE_STATUS | \
+ REGULATOR_CHANGE_VOLTAGE), \
+ }, \
+ .num_consumer_supplies = ARRAY_SIZE(max8907c_##_id##_supply), \
+ .consumer_supplies = max8907c_##_id##_supply, \
+}; \
+static struct platform_device max8907c_##_id##_device = { \
+ .name = "max8907c-regulator", \
+ .id = MAX8907C_##_id, \
+ .dev = { \
+ .platform_data = &max8907c_##_id##_data, \
+ }, \
+}
+
+MAX8907C_REGULATOR_DEVICE(SD1, 637500, 1425000);
+MAX8907C_REGULATOR_DEVICE(SD2, 637500, 1425000);
+MAX8907C_REGULATOR_DEVICE(SD3, 750000, 3900000);
+MAX8907C_REGULATOR_DEVICE(LDO1, 750000, 3900000);
+MAX8907C_REGULATOR_DEVICE(LDO2, 650000, 2225000);
+MAX8907C_REGULATOR_DEVICE(LDO3, 650000, 2225000);
+MAX8907C_REGULATOR_DEVICE(LDO4, 750000, 3900000);
+MAX8907C_REGULATOR_DEVICE(LDO5, 750000, 3900000);
+MAX8907C_REGULATOR_DEVICE(LDO6, 750000, 3900000);
+MAX8907C_REGULATOR_DEVICE(LDO7, 750000, 3900000);
+MAX8907C_REGULATOR_DEVICE(LDO8, 750000, 3900000);
+MAX8907C_REGULATOR_DEVICE(LDO9, 750000, 3900000);
+MAX8907C_REGULATOR_DEVICE(LDO10, 750000, 3900000);
+MAX8907C_REGULATOR_DEVICE(LDO11, 750000, 3900000);
+MAX8907C_REGULATOR_DEVICE(LDO12, 750000, 3900000);
+MAX8907C_REGULATOR_DEVICE(LDO13, 750000, 3900000);
+MAX8907C_REGULATOR_DEVICE(LDO14, 750000, 3900000);
+MAX8907C_REGULATOR_DEVICE(LDO15, 750000, 3900000);
+MAX8907C_REGULATOR_DEVICE(LDO16, 750000, 3900000);
+MAX8907C_REGULATOR_DEVICE(LDO17, 650000, 2225000);
+MAX8907C_REGULATOR_DEVICE(LDO18, 650000, 2225000);
+MAX8907C_REGULATOR_DEVICE(LDO19, 750000, 3900000);
+MAX8907C_REGULATOR_DEVICE(LDO20, 750000, 3900000);
+
+static struct platform_device *whistler_max8907c_power_devices[] = {
+ &max8907c_SD1_device,
+ &max8907c_SD2_device,
+ &max8907c_SD3_device,
+ &max8907c_LDO1_device,
+ &max8907c_LDO2_device,
+ &max8907c_LDO3_device,
+ &max8907c_LDO4_device,
+ &max8907c_LDO5_device,
+ &max8907c_LDO6_device,
+ &max8907c_LDO7_device,
+ &max8907c_LDO8_device,
+ &max8907c_LDO9_device,
+ &max8907c_LDO10_device,
+ &max8907c_LDO11_device,
+ &max8907c_LDO12_device,
+ &max8907c_LDO13_device,
+ &max8907c_LDO14_device,
+ &max8907c_LDO15_device,
+ &max8907c_LDO16_device,
+ &max8907c_LDO17_device,
+ &max8907c_LDO18_device,
+ &max8907c_LDO19_device,
+ &max8907c_LDO20_device,
+};
+
+static int whistler_max8907c_setup(void)
+{
+ int ret;
+
+ /*
+ * Configure PWREN, and attach CPU V1 rail to it.
+ * TODO: h/w events (power cycle, reset, battery low) auto-disables PWREN.
+ * Only soft reset (not supported) requires s/w to disable PWREN explicitly
+ */
+ ret = max8907c_pwr_en_config();
+ if (ret != 0)
+ return ret;
+
+ return max8907c_pwr_en_attach();
+}
+
+static struct max8907c_platform_data max8907c_pdata = {
+ .num_subdevs = ARRAY_SIZE(whistler_max8907c_power_devices),
+ .subdevs = whistler_max8907c_power_devices,
+ .irq_base = TEGRA_NR_IRQS,
+ .max8907c_setup = whistler_max8907c_setup,
+};
+
+static struct i2c_board_info __initdata whistler_regulators[] = {
+ {
+ I2C_BOARD_INFO("max8907c", 0x3C),
+ .irq = INT_EXTERNAL_PMU,
+ .platform_data = &max8907c_pdata,
+ },
+};
+
+static struct tegra_suspend_platform_data whistler_suspend_data = {
+ .cpu_timer = 2000,
+ .cpu_off_timer = 1000,
+ .suspend_mode = TEGRA_SUSPEND_LP0,
+ .core_timer = 0x7e,
+ .core_off_timer = 0xc00,
+ .corereq_high = true,
+ .sysclkreq_high = true,
+ .combined_req = true,
+};
+
+int __init whistler_regulator_init(void)
+{
+ void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE);
+ void __iomem *chip_id = IO_ADDRESS(TEGRA_APB_MISC_BASE) + 0x804;
+ u32 pmc_ctrl;
+ u32 minor;
+
+ minor = (readl(chip_id) >> 16) & 0xf;
+ /* A03 (but not A03p) chips do not support LP0 */
+ if (minor == 3 && !(tegra_spare_fuse(18) || tegra_spare_fuse(19)))
+ whistler_suspend_data.suspend_mode = TEGRA_SUSPEND_LP1;
+
+ /* configure the power management controller to trigger PMU
+ * interrupts when low */
+ pmc_ctrl = readl(pmc + PMC_CTRL);
+ writel(pmc_ctrl | PMC_CTRL_INTR_LOW, pmc + PMC_CTRL);
+
+ i2c_register_board_info(4, whistler_regulators, 1);
+
+ tegra_deep_sleep = max8907c_deep_sleep;
+
+ tegra_init_suspend(&whistler_suspend_data);
+
+ return 0;
+}
diff --git a/arch/arm/mach-tegra/board-whistler-sdhci.c b/arch/arm/mach-tegra/board-whistler-sdhci.c
new file mode 100644
index 000000000000..08ebe33ae8b0
--- /dev/null
+++ b/arch/arm/mach-tegra/board-whistler-sdhci.c
@@ -0,0 +1,248 @@
+/*
+ * arch/arm/mach-tegra/board-whistler-sdhci.c
+ *
+ * Copyright (C) 2010 Google, Inc.
+ * Copyright (C) 2011 NVIDIA Corporation.
+ *
+ * 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/resource.h>
+#include <linux/platform_device.h>
+#include <linux/wlan_plat.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/mmc/host.h>
+
+#include <asm/mach-types.h>
+#include <mach/irqs.h>
+#include <mach/iomap.h>
+#include <mach/sdhci.h>
+
+#include "gpio-names.h"
+#include "board.h"
+
+#define WHISTLER_WLAN_PWR TEGRA_GPIO_PK5
+#define WHISTLER_WLAN_RST TEGRA_GPIO_PK6
+#define WHISTLER_WLAN_WOW TEGRA_GPIO_PU5
+
+#define WHISTLER_EXT_SDCARD_DETECT TEGRA_GPIO_PI5
+
+static void (*wifi_status_cb)(int card_present, void *dev_id);
+static void *wifi_status_cb_devid;
+
+static int whistler_wifi_status_register(
+ void (*sdhcicallback)(int card_present, void *dev_id),
+ void *dev_id)
+{
+ if (wifi_status_cb)
+ return -EAGAIN;
+ wifi_status_cb = sdhcicallback;
+ wifi_status_cb_devid = dev_id;
+ return 0;
+}
+
+static int whistler_wifi_set_carddetect(int val)
+{
+ pr_debug("%s: %d\n", __func__, val);
+ if (wifi_status_cb)
+ wifi_status_cb(val, wifi_status_cb_devid);
+ else
+ pr_warning("%s: Nobody to notify\n", __func__);
+ return 0;
+}
+
+static int whistler_wifi_power(int on)
+{
+ gpio_set_value(WHISTLER_WLAN_PWR, on);
+ mdelay(100);
+ gpio_set_value(WHISTLER_WLAN_RST, on);
+ mdelay(200);
+
+ return 0;
+}
+
+static int whistler_wifi_reset(int on)
+{
+ pr_debug("%s: do nothing\n", __func__);
+ return 0;
+}
+
+
+static struct wifi_platform_data whistler_wifi_control = {
+ .set_power = whistler_wifi_power,
+ .set_reset = whistler_wifi_reset,
+ .set_carddetect = whistler_wifi_set_carddetect,
+};
+
+static struct resource wifi_resource[] = {
+ [0] = {
+ .name = "bcm4329_wlan_irq",
+ .start = TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PU5),
+ .end = TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PU5),
+ .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL | IORESOURCE_IRQ_SHAREABLE,
+ },
+};
+
+static struct platform_device whistler_wifi_device = {
+ .name = "bcm4329_wlan",
+ .id = 1,
+ .num_resources = 1,
+ .resource = wifi_resource,
+ .dev = {
+ .platform_data = &whistler_wifi_control,
+ },
+};
+
+static struct resource sdhci_resource1[] = {
+ [0] = {
+ .start = INT_SDMMC2,
+ .end = INT_SDMMC2,
+ .flags = IORESOURCE_IRQ,
+ },
+ [1] = {
+ .start = TEGRA_SDMMC2_BASE,
+ .end = TEGRA_SDMMC2_BASE + TEGRA_SDMMC2_SIZE-1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+
+static struct resource sdhci_resource2[] = {
+ [0] = {
+ .start = INT_SDMMC3,
+ .end = INT_SDMMC3,
+ .flags = IORESOURCE_IRQ,
+ },
+ [1] = {
+ .start = TEGRA_SDMMC3_BASE,
+ .end = TEGRA_SDMMC3_BASE + TEGRA_SDMMC3_SIZE-1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct resource sdhci_resource3[] = {
+ [0] = {
+ .start = INT_SDMMC4,
+ .end = INT_SDMMC4,
+ .flags = IORESOURCE_IRQ,
+ },
+ [1] = {
+ .start = TEGRA_SDMMC4_BASE,
+ .end = TEGRA_SDMMC4_BASE + TEGRA_SDMMC4_SIZE-1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct embedded_sdio_data embedded_sdio_data1 = {
+ .cccr = {
+ .sdio_vsn = 2,
+ .multi_block = 1,
+ .low_speed = 0,
+ .wide_bus = 0,
+ .high_power = 1,
+ .high_speed = 1,
+ },
+ .cis = {
+ .vendor = 0x02d0,
+ .device = 0x4329,
+ },
+};
+
+static struct tegra_sdhci_platform_data tegra_sdhci_platform_data1 = {
+ .mmc_data = {
+ .register_status_notify = whistler_wifi_status_register,
+ .embedded_sdio = &embedded_sdio_data1,
+ .built_in = 1,
+ },
+ .cd_gpio = -1,
+ .wp_gpio = -1,
+ .power_gpio = -1,
+ .max_clk_limit = 25000000,
+};
+
+static struct tegra_sdhci_platform_data tegra_sdhci_platform_data2 = {
+ .cd_gpio = WHISTLER_EXT_SDCARD_DETECT,
+ .wp_gpio = -1,
+ .power_gpio = -1,
+};
+
+static struct tegra_sdhci_platform_data tegra_sdhci_platform_data3 = {
+ .cd_gpio = -1,
+ .wp_gpio = -1,
+ .power_gpio = -1,
+ .mmc_data = {
+ .built_in = 1,
+ }
+};
+
+static struct platform_device tegra_sdhci_device1 = {
+ .name = "sdhci-tegra",
+ .id = 1,
+ .resource = sdhci_resource1,
+ .num_resources = ARRAY_SIZE(sdhci_resource1),
+ .dev = {
+ .platform_data = &tegra_sdhci_platform_data1,
+ },
+};
+
+static struct platform_device tegra_sdhci_device2 = {
+ .name = "sdhci-tegra",
+ .id = 2,
+ .resource = sdhci_resource2,
+ .num_resources = ARRAY_SIZE(sdhci_resource2),
+ .dev = {
+ .platform_data = &tegra_sdhci_platform_data2,
+ },
+};
+
+static struct platform_device tegra_sdhci_device3 = {
+ .name = "sdhci-tegra",
+ .id = 3,
+ .resource = sdhci_resource3,
+ .num_resources = ARRAY_SIZE(sdhci_resource3),
+ .dev = {
+ .platform_data = &tegra_sdhci_platform_data3,
+ },
+};
+
+static int __init whistler_wifi_init(void)
+{
+ gpio_request(WHISTLER_WLAN_PWR, "wlan_power");
+ gpio_request(WHISTLER_WLAN_RST, "wlan_rst");
+ gpio_request(WHISTLER_WLAN_WOW, "bcmsdh_sdmmc");
+
+ tegra_gpio_enable(WHISTLER_WLAN_PWR);
+ tegra_gpio_enable(WHISTLER_WLAN_RST);
+ tegra_gpio_enable(WHISTLER_WLAN_WOW);
+
+ gpio_direction_output(WHISTLER_WLAN_PWR, 0);
+ gpio_direction_output(WHISTLER_WLAN_RST, 0);
+ gpio_direction_input(WHISTLER_WLAN_WOW);
+
+ platform_device_register(&whistler_wifi_device);
+ return 0;
+}
+int __init whistler_sdhci_init(void)
+{
+ int ret;
+
+ tegra_gpio_enable(WHISTLER_EXT_SDCARD_DETECT);
+
+ platform_device_register(&tegra_sdhci_device3);
+ platform_device_register(&tegra_sdhci_device2);
+ platform_device_register(&tegra_sdhci_device1);
+
+ whistler_wifi_init();
+ return 0;
+}
diff --git a/arch/arm/mach-tegra/board-whistler-sensors.c b/arch/arm/mach-tegra/board-whistler-sensors.c
new file mode 100644
index 000000000000..5177770f4cbe
--- /dev/null
+++ b/arch/arm/mach-tegra/board-whistler-sensors.c
@@ -0,0 +1,406 @@
+/*
+ * arch/arm/mach-tegra/board-whistler-sensors.c
+ *
+ * Copyright (c) 2010-2011, NVIDIA CORPORATION, All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Neither the name of NVIDIA CORPORATION nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <mach/gpio.h>
+#include <media/ov5650.h>
+#include <media/soc380.h>
+#include <linux/regulator/consumer.h>
+#include <linux/err.h>
+#include <linux/adt7461.h>
+#include <generated/mach-types.h>
+#include <linux/gpio.h>
+#include <linux/i2c/pca953x.h>
+
+#include <mach/tegra_odm_fuses.h>
+
+#include "gpio-names.h"
+#include "cpu-tegra.h"
+#include "board-whistler.h"
+
+#define CAMERA1_PWDN_GPIO TEGRA_GPIO_PT2
+#define CAMERA1_RESET_GPIO TEGRA_GPIO_PD2
+#define CAMERA2_PWDN_GPIO TEGRA_GPIO_PBB5
+#define CAMERA2_RESET_GPIO TEGRA_GPIO_PBB1
+#define CAMERA_AF_PD_GPIO TEGRA_GPIO_PT3
+#define CAMERA_FLASH_EN1_GPIO TEGRA_GPIO_PBB4
+#define CAMERA_FLASH_EN2_GPIO TEGRA_GPIO_PA0
+
+#define FUSE_POWER_EN_GPIO (TCA6416_GPIO_BASE + 2)
+
+#define ADXL34X_IRQ_GPIO TEGRA_GPIO_PAA1
+#define ISL29018_IRQ_GPIO TEGRA_GPIO_PK2
+#define ADT7461_IRQ_GPIO TEGRA_GPIO_PI2
+
+static struct regulator *reg_avdd_cam1; /* LDO9 */
+static struct regulator *reg_vdd_af; /* LDO13 */
+static struct regulator *reg_vdd_mipi; /* LDO17 */
+static struct regulator *reg_vddio_vi; /* LDO18 */
+
+static int whistler_camera_init(void)
+{
+ tegra_gpio_enable(CAMERA1_PWDN_GPIO);
+ gpio_request(CAMERA1_PWDN_GPIO, "camera1_powerdown");
+ gpio_direction_output(CAMERA1_PWDN_GPIO, 0);
+ gpio_export(CAMERA1_PWDN_GPIO, false);
+
+ tegra_gpio_enable(CAMERA1_RESET_GPIO);
+ gpio_request(CAMERA1_RESET_GPIO, "camera1_reset");
+ gpio_direction_output(CAMERA1_RESET_GPIO, 0);
+ gpio_export(CAMERA1_RESET_GPIO, false);
+
+ tegra_gpio_enable(CAMERA2_PWDN_GPIO);
+ gpio_request(CAMERA2_PWDN_GPIO, "camera2_powerdown");
+ gpio_direction_output(CAMERA2_PWDN_GPIO, 0);
+ gpio_export(CAMERA2_PWDN_GPIO, false);
+
+ tegra_gpio_enable(CAMERA2_RESET_GPIO);
+ gpio_request(CAMERA2_RESET_GPIO, "camera2_reset");
+ gpio_direction_output(CAMERA2_RESET_GPIO, 0);
+ gpio_export(CAMERA2_RESET_GPIO, false);
+
+ tegra_gpio_enable(CAMERA_AF_PD_GPIO);
+ gpio_request(CAMERA_AF_PD_GPIO, "camera_autofocus");
+ gpio_direction_output(CAMERA_AF_PD_GPIO, 0);
+ gpio_export(CAMERA_AF_PD_GPIO, false);
+
+ tegra_gpio_enable(CAMERA_FLASH_EN1_GPIO);
+ gpio_request(CAMERA_FLASH_EN1_GPIO, "camera_flash_en1");
+ gpio_direction_output(CAMERA_FLASH_EN1_GPIO, 0);
+ gpio_export(CAMERA_FLASH_EN1_GPIO, false);
+
+ tegra_gpio_enable(CAMERA_FLASH_EN2_GPIO);
+ gpio_request(CAMERA_FLASH_EN2_GPIO, "camera_flash_en2");
+ gpio_direction_output(CAMERA_FLASH_EN2_GPIO, 0);
+ gpio_export(CAMERA_FLASH_EN2_GPIO, false);
+
+ gpio_set_value(CAMERA1_PWDN_GPIO, 1);
+ mdelay(5);
+
+ return 0;
+}
+
+static int whistler_ov5650_power_on(void)
+{
+ gpio_set_value(CAMERA1_PWDN_GPIO, 0);
+
+ if (!reg_avdd_cam1) {
+ reg_avdd_cam1 = regulator_get(NULL, "vdd_cam1");
+ if (IS_ERR_OR_NULL(reg_avdd_cam1)) {
+ pr_err("whistler_ov5650_power_on: vdd_cam1 failed\n");
+ reg_avdd_cam1 = NULL;
+ return PTR_ERR(reg_avdd_cam1);
+ }
+ regulator_enable(reg_avdd_cam1);
+ }
+ mdelay(5);
+
+ if (!reg_vdd_mipi) {
+ reg_vdd_mipi = regulator_get(NULL, "vddio_mipi");
+ if (IS_ERR_OR_NULL(reg_vdd_mipi)) {
+ pr_err("whistler_ov5650_power_on: vddio_mipi failed\n");
+ reg_vdd_mipi = NULL;
+ return PTR_ERR(reg_vdd_mipi);
+ }
+ regulator_enable(reg_vdd_mipi);
+ }
+ mdelay(5);
+
+ if (!reg_vdd_af) {
+ reg_vdd_af = regulator_get(NULL, "vdd_vcore_af");
+ if (IS_ERR_OR_NULL(reg_vdd_af)) {
+ pr_err("whistler_ov5650_power_on: vdd_vcore_af failed\n");
+ reg_vdd_af = NULL;
+ return PTR_ERR(reg_vdd_af);
+ }
+ regulator_enable(reg_vdd_af);
+ }
+ mdelay(5);
+
+ gpio_set_value(CAMERA1_RESET_GPIO, 1);
+ mdelay(10);
+ gpio_set_value(CAMERA1_RESET_GPIO, 0);
+ mdelay(5);
+ gpio_set_value(CAMERA1_RESET_GPIO, 1);
+ mdelay(20);
+ gpio_set_value(CAMERA_AF_PD_GPIO, 1);
+
+ return 0;
+}
+
+static int whistler_ov5650_power_off(void)
+{
+ gpio_set_value(CAMERA_AF_PD_GPIO, 0);
+ gpio_set_value(CAMERA1_PWDN_GPIO, 1);
+ gpio_set_value(CAMERA1_RESET_GPIO, 0);
+
+ if (reg_avdd_cam1) {
+ regulator_disable(reg_avdd_cam1);
+ regulator_put(reg_avdd_cam1);
+ reg_avdd_cam1 = NULL;
+ }
+
+ if (reg_vdd_mipi) {
+ regulator_disable(reg_vdd_mipi);
+ regulator_put(reg_vdd_mipi);
+ reg_vdd_mipi = NULL;
+ }
+
+ if (reg_vdd_af) {
+ regulator_disable(reg_vdd_af);
+ regulator_put(reg_vdd_af);
+ reg_vdd_af = NULL;
+ }
+
+ return 0;
+}
+
+static int whistler_soc380_power_on(void)
+{
+ gpio_set_value(CAMERA2_PWDN_GPIO, 0);
+
+ if (!reg_vddio_vi) {
+ reg_vddio_vi = regulator_get(NULL, "vddio_vi");
+ if (IS_ERR_OR_NULL(reg_vddio_vi)) {
+ pr_err("whistler_soc380_power_on: vddio_vi failed\n");
+ reg_vddio_vi = NULL;
+ return PTR_ERR(reg_vddio_vi);
+ }
+ regulator_set_voltage(reg_vddio_vi, 1800*1000, 1800*1000);
+ mdelay(5);
+ regulator_enable(reg_vddio_vi);
+ }
+
+ if (!reg_avdd_cam1) {
+ reg_avdd_cam1 = regulator_get(NULL, "vdd_cam1");
+ if (IS_ERR_OR_NULL(reg_avdd_cam1)) {
+ pr_err("whistler_soc380_power_on: vdd_cam1 failed\n");
+ reg_avdd_cam1 = NULL;
+ return PTR_ERR(reg_avdd_cam1);
+ }
+ regulator_enable(reg_avdd_cam1);
+ }
+ mdelay(5);
+
+ gpio_set_value(CAMERA2_RESET_GPIO, 1);
+ mdelay(10);
+ gpio_set_value(CAMERA2_RESET_GPIO, 0);
+ mdelay(5);
+ gpio_set_value(CAMERA2_RESET_GPIO, 1);
+ mdelay(20);
+
+ return 0;
+
+}
+
+static int whistler_soc380_power_off(void)
+{
+ gpio_set_value(CAMERA2_PWDN_GPIO, 1);
+ gpio_set_value(CAMERA2_RESET_GPIO, 0);
+
+ if (reg_avdd_cam1) {
+ regulator_disable(reg_avdd_cam1);
+ regulator_put(reg_avdd_cam1);
+ reg_avdd_cam1 = NULL;
+ }
+ if (reg_vddio_vi) {
+ regulator_disable(reg_vddio_vi);
+ regulator_put(reg_vddio_vi);
+ reg_vddio_vi = NULL;
+ }
+
+ return 0;
+}
+
+struct ov5650_platform_data whistler_ov5650_data = {
+ .power_on = whistler_ov5650_power_on,
+ .power_off = whistler_ov5650_power_off,
+};
+
+struct soc380_platform_data whistler_soc380_data = {
+ .power_on = whistler_soc380_power_on,
+ .power_off = whistler_soc380_power_off,
+};
+
+static int whistler_fuse_power_en(int enb)
+{
+ int ret;
+
+ ret = gpio_request(FUSE_POWER_EN_GPIO, "fuse_power_en");
+ if (ret) {
+ pr_err("%s: gpio_request fail (%d)\n", __func__, ret);
+ return ret;
+ }
+
+ ret = gpio_direction_output(FUSE_POWER_EN_GPIO, enb);
+ if (ret) {
+ pr_err("%s: gpio_direction_output fail (%d)\n", __func__, ret);
+ return ret;
+ }
+
+ gpio_free(FUSE_POWER_EN_GPIO);
+ return 0;
+}
+
+static struct pca953x_platform_data whistler_tca6416_data = {
+ .gpio_base = TCA6416_GPIO_BASE,
+};
+
+static struct i2c_board_info whistler_i2c3_board_info[] = {
+ {
+ I2C_BOARD_INFO("ov5650", 0x36),
+ .platform_data = &whistler_ov5650_data,
+ },
+ {
+ I2C_BOARD_INFO("ad5820", 0x0c),
+ },
+ {
+ I2C_BOARD_INFO("soc380", 0x3C),
+ .platform_data = &whistler_soc380_data,
+ },
+};
+
+static void whistler_adxl34x_init(void)
+{
+ tegra_gpio_enable(ADXL34X_IRQ_GPIO);
+ gpio_request(ADXL34X_IRQ_GPIO, "adxl34x");
+ gpio_direction_input(ADXL34X_IRQ_GPIO);
+}
+
+static void whistler_isl29018_init(void)
+{
+ tegra_gpio_enable(ISL29018_IRQ_GPIO);
+ gpio_request(ISL29018_IRQ_GPIO, "isl29018");
+ gpio_direction_input(ISL29018_IRQ_GPIO);
+}
+
+static struct i2c_board_info whistler_i2c1_board_info[] = {
+ {
+ I2C_BOARD_INFO("adxl34x", 0x1D),
+ .irq = TEGRA_GPIO_TO_IRQ(ADXL34X_IRQ_GPIO),
+ },
+ {
+ I2C_BOARD_INFO("isl29018", 0x44),
+ .irq = TEGRA_GPIO_TO_IRQ(ISL29018_IRQ_GPIO),
+ },
+};
+
+static void whistler_adt7461_init(void)
+{
+ tegra_gpio_enable(ADT7461_IRQ_GPIO);
+ gpio_request(ADT7461_IRQ_GPIO, "adt7461");
+ gpio_direction_input(ADT7461_IRQ_GPIO);
+}
+
+static struct adt7461_platform_data whistler_adt7461_pdata = {
+ .supported_hwrev = true,
+ .ext_range = false,
+ .therm2 = true,
+ .conv_rate = 0x05,
+ .offset = 0,
+ .hysteresis = 0,
+ .shutdown_ext_limit = 115,
+ .shutdown_local_limit = 120,
+ .throttling_ext_limit = 90,
+ .alarm_fn = tegra_throttling_enable,
+};
+
+static struct i2c_board_info whistler_i2c4_board_info[] = {
+ {
+ I2C_BOARD_INFO("adt7461", 0x4C),
+ .irq = TEGRA_GPIO_TO_IRQ(ADT7461_IRQ_GPIO),
+ .platform_data = &whistler_adt7461_pdata,
+ },
+ {
+ I2C_BOARD_INFO("tca6416", 0x20),
+ .platform_data = &whistler_tca6416_data,
+ },
+};
+
+int __init whistler_sensors_init(void)
+{
+ whistler_camera_init();
+
+ whistler_adxl34x_init();
+
+ whistler_isl29018_init();
+
+ whistler_adt7461_init();
+
+ i2c_register_board_info(0, whistler_i2c1_board_info,
+ ARRAY_SIZE(whistler_i2c1_board_info));
+
+ i2c_register_board_info(4, whistler_i2c4_board_info,
+ ARRAY_SIZE(whistler_i2c4_board_info));
+
+ i2c_register_board_info(3, whistler_i2c3_board_info,
+ ARRAY_SIZE(whistler_i2c3_board_info));
+
+ tegra_fuse_regulator_en = whistler_fuse_power_en;
+
+ return 0;
+}
+
+int __init whistler_sensor_late_init(void)
+{
+ int ret;
+
+ if (!machine_is_whistler())
+ return 0;
+
+ reg_vddio_vi = regulator_get(NULL, "vddio_vi");
+ if (IS_ERR_OR_NULL(reg_vddio_vi)) {
+ pr_err("%s: Couldn't get regulator vddio_vi\n", __func__);
+ return PTR_ERR(reg_vddio_vi);
+ }
+
+ /* set vddio_vi voltage to 1.8v */
+ ret = regulator_set_voltage(reg_vddio_vi, 1800*1000, 1800*1000);
+ if (ret) {
+ pr_err("%s: Failed to set vddio_vi to 1.8v\n", __func__);
+ goto fail_put_regulator;
+ }
+
+ regulator_put(reg_vddio_vi);
+ reg_vddio_vi = NULL;
+ return 0;
+
+fail_put_regulator:
+ regulator_put(reg_vddio_vi);
+ reg_vddio_vi = NULL;
+ return ret;
+}
+
+late_initcall(whistler_sensor_late_init);
+
diff --git a/arch/arm/mach-tegra/board-whistler.c b/arch/arm/mach-tegra/board-whistler.c
new file mode 100644
index 000000000000..9e5ea0d19df2
--- /dev/null
+++ b/arch/arm/mach-tegra/board-whistler.c
@@ -0,0 +1,599 @@
+/*
+ * arch/arm/mach-tegra/board-whistler.c
+ *
+ * Copyright (c) 2010 - 2011, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/serial_8250.h>
+#include <linux/i2c.h>
+#include <linux/synaptics_i2c_rmi.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/i2c-tegra.h>
+#include <linux/gpio.h>
+#include <linux/gpio_scrollwheel.h>
+#include <linux/input.h>
+#include <linux/platform_data/tegra_usb.h>
+#include <linux/mfd/max8907c.h>
+#include <linux/memblock.h>
+#include <linux/tegra_uart.h>
+
+#include <mach/clk.h>
+#include <mach/iomap.h>
+#include <mach/irqs.h>
+#include <mach/pinmux.h>
+#include <mach/iomap.h>
+#include <mach/io.h>
+#include <mach/i2s.h>
+#include <mach/tegra_wm8753_pdata.h>
+#include <sound/tlv320aic326x.h>
+
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <mach/usb_phy.h>
+
+#include "board.h"
+#include "clock.h"
+#include "board-whistler.h"
+#include "devices.h"
+#include "gpio-names.h"
+#include "fuse.h"
+#include "pm.h"
+#include "board-whistler-baseband.h"
+
+#define USB1_VBUS_GPIO TCA6416_GPIO_BASE
+
+static struct plat_serial8250_port debug_uartb_platform_data[] = {
+ {
+ .membase = IO_ADDRESS(TEGRA_UARTB_BASE),
+ .mapbase = TEGRA_UARTB_BASE,
+ .irq = INT_UARTB,
+ .flags = UPF_BOOT_AUTOCONF | UPF_FIXED_TYPE,
+ .type = PORT_TEGRA,
+ .iotype = UPIO_MEM,
+ .regshift = 2,
+ .uartclk = 216000000,
+ }, {
+ .flags = 0,
+ }
+};
+
+static struct platform_device debug_uartb = {
+ .name = "serial8250",
+ .id = PLAT8250_DEV_PLATFORM,
+ .dev = {
+ .platform_data = debug_uartb_platform_data,
+ },
+};
+
+static struct plat_serial8250_port debug_uarta_platform_data[] = {
+ {
+ .membase = IO_ADDRESS(TEGRA_UARTA_BASE),
+ .mapbase = TEGRA_UARTA_BASE,
+ .irq = INT_UARTA,
+ .flags = UPF_BOOT_AUTOCONF | UPF_FIXED_TYPE,
+ .type = PORT_TEGRA,
+ .iotype = UPIO_MEM,
+ .regshift = 2,
+ .uartclk = 216000000,
+ }, {
+ .flags = 0,
+ }
+};
+
+static struct platform_device debug_uarta = {
+ .name = "serial8250",
+ .id = PLAT8250_DEV_PLATFORM,
+ .dev = {
+ .platform_data = debug_uarta_platform_data,
+ },
+};
+
+static struct platform_device *whistler_uart_devices[] __initdata = {
+ &tegra_uarta_device,
+ &tegra_uartb_device,
+ &tegra_uartc_device,
+};
+
+struct uart_clk_parent uart_parent_clk[] = {
+ [0] = {.name = "pll_p"},
+ [1] = {.name = "pll_m"},
+ [2] = {.name = "clk_m"},
+};
+
+static struct tegra_uart_platform_data whistler_uart_pdata;
+
+static void __init uart_debug_init(void)
+{
+ unsigned long rate;
+ struct clk *debug_uart_clk;
+ struct clk *c;
+ int modem_id = tegra_get_modem_id();
+
+ if (modem_id == 0x1) {
+ /* UARTB is the debug port. */
+ pr_info("Selecting UARTB as the debug console\n");
+ whistler_uart_devices[1] = &debug_uartb;
+ debug_uart_clk = clk_get_sys("serial8250.0", "uartb");
+
+ /* Clock enable for the debug channel */
+ if (!IS_ERR_OR_NULL(debug_uart_clk)) {
+ rate = debug_uartb_platform_data[0].uartclk;
+ pr_info("The debug console clock name is %s\n",
+ debug_uart_clk->name);
+ c = tegra_get_clock_by_name("pll_p");
+ if (IS_ERR_OR_NULL(c))
+ pr_err("Not getting the parent clock pll_p\n");
+ else
+ clk_set_parent(debug_uart_clk, c);
+
+ clk_enable(debug_uart_clk);
+ clk_set_rate(debug_uart_clk, rate);
+ } else {
+ pr_err("Not getting the clock %s for debug console\n",
+ debug_uart_clk->name);
+ }
+ } else {
+ /* UARTA is the debug port. */
+ pr_info("Selecting UARTA as the debug console\n");
+ whistler_uart_devices[0] = &debug_uarta;
+ debug_uart_clk = clk_get_sys("serial8250.0", "uarta");
+
+ /* Clock enable for the debug channel */
+ if (!IS_ERR_OR_NULL(debug_uart_clk)) {
+ rate = debug_uarta_platform_data[0].uartclk;
+ pr_info("The debug console clock name is %s\n",
+ debug_uart_clk->name);
+ c = tegra_get_clock_by_name("pll_p");
+ if (IS_ERR_OR_NULL(c))
+ pr_err("Not getting the parent clock pll_p\n");
+ else
+ clk_set_parent(debug_uart_clk, c);
+
+ clk_enable(debug_uart_clk);
+ clk_set_rate(debug_uart_clk, rate);
+ } else {
+ pr_err("Not getting the clock %s for debug console\n",
+ debug_uart_clk->name);
+ }
+ }
+}
+
+static void __init whistler_uart_init(void)
+{
+ int i;
+ struct clk *c;
+
+ for (i = 0; i < ARRAY_SIZE(uart_parent_clk); ++i) {
+ c = tegra_get_clock_by_name(uart_parent_clk[i].name);
+ if (IS_ERR_OR_NULL(c)) {
+ pr_err("Not able to get the clock for %s\n",
+ uart_parent_clk[i].name);
+ continue;
+ }
+ uart_parent_clk[i].parent_clk = c;
+ uart_parent_clk[i].fixed_clk_rate = clk_get_rate(c);
+ }
+ whistler_uart_pdata.parent_clk_list = uart_parent_clk;
+ whistler_uart_pdata.parent_clk_count = ARRAY_SIZE(uart_parent_clk);
+
+ tegra_uarta_device.dev.platform_data = &whistler_uart_pdata;
+ tegra_uartb_device.dev.platform_data = &whistler_uart_pdata;
+ tegra_uartc_device.dev.platform_data = &whistler_uart_pdata;
+
+ if (!is_tegra_debug_uartport_hs())
+ uart_debug_init();
+
+ platform_add_devices(whistler_uart_devices,
+ ARRAY_SIZE(whistler_uart_devices));
+}
+
+static struct resource whistler_bcm4329_rfkill_resources[] = {
+ {
+ .name = "bcm4329_nshutdown_gpio",
+ .start = TEGRA_GPIO_PU0,
+ .end = TEGRA_GPIO_PU0,
+ .flags = IORESOURCE_IO,
+ },
+};
+
+static struct platform_device whistler_bcm4329_rfkill_device = {
+ .name = "bcm4329_rfkill",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(whistler_bcm4329_rfkill_resources),
+ .resource = whistler_bcm4329_rfkill_resources,
+};
+
+static struct resource whistler_bluesleep_resources[] = {
+ [0] = {
+ .name = "gpio_host_wake",
+ .start = TEGRA_GPIO_PU6,
+ .end = TEGRA_GPIO_PU6,
+ .flags = IORESOURCE_IO,
+ },
+ [1] = {
+ .name = "gpio_ext_wake",
+ .start = TEGRA_GPIO_PU1,
+ .end = TEGRA_GPIO_PU1,
+ .flags = IORESOURCE_IO,
+ },
+ [2] = {
+ .name = "host_wake",
+ .start = TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PU6),
+ .end = TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PU6),
+ .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE,
+ },
+};
+
+static struct platform_device whistler_bluesleep_device = {
+ .name = "bluesleep",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(whistler_bluesleep_resources),
+ .resource = whistler_bluesleep_resources,
+};
+
+static void __init whistler_setup_bluesleep(void)
+{
+ platform_device_register(&whistler_bluesleep_device);
+ tegra_gpio_enable(TEGRA_GPIO_PU6);
+ tegra_gpio_enable(TEGRA_GPIO_PU1);
+ return;
+}
+
+static struct tegra_utmip_config utmi_phy_config[] = {
+ [0] = {
+ .hssync_start_delay = 9,
+ .idle_wait_delay = 17,
+ .elastic_limit = 16,
+ .term_range_adj = 6,
+ .xcvr_setup = 15,
+ .xcvr_lsfslew = 2,
+ .xcvr_lsrslew = 2,
+ },
+ [1] = {
+ .hssync_start_delay = 9,
+ .idle_wait_delay = 17,
+ .elastic_limit = 16,
+ .term_range_adj = 6,
+ .xcvr_setup = 8,
+ .xcvr_lsfslew = 2,
+ .xcvr_lsrslew = 2,
+ },
+};
+
+static struct tegra_ulpi_config ulpi_phy_config = {
+ .reset_gpio = TEGRA_GPIO_PG2,
+ .clk = "clk_dev2",
+};
+
+static __initdata struct tegra_clk_init_table whistler_clk_init_table[] = {
+ /* name parent rate enabled */
+ { "pwm", "clk_32k", 32768, false},
+ { "kbc", "clk_32k", 32768, true},
+ { "sdmmc2", "pll_p", 25000000, false},
+ { "i2s1", "pll_a_out0", 0, false},
+ { "i2s2", "pll_a_out0", 0, false},
+ { "spdif_out", "pll_a_out0", 0, false},
+ { NULL, NULL, 0, 0},
+};
+
+static struct tegra_i2c_platform_data whistler_i2c1_platform_data = {
+ .adapter_nr = 0,
+ .bus_count = 1,
+ .bus_clk_rate = { 400000, 0 },
+};
+
+static const struct tegra_pingroup_config i2c2_ddc = {
+ .pingroup = TEGRA_PINGROUP_DDC,
+ .func = TEGRA_MUX_I2C2,
+};
+
+static const struct tegra_pingroup_config i2c2_gen2 = {
+ .pingroup = TEGRA_PINGROUP_PTA,
+ .func = TEGRA_MUX_I2C2,
+};
+
+static struct tegra_i2c_platform_data whistler_i2c2_platform_data = {
+ .adapter_nr = 1,
+ .bus_count = 2,
+ .bus_clk_rate = { 100000, 100000 },
+ .bus_mux = { &i2c2_ddc, &i2c2_gen2 },
+ .bus_mux_len = { 1, 1 },
+};
+
+static struct tegra_i2c_platform_data whistler_i2c3_platform_data = {
+ .adapter_nr = 3,
+ .bus_count = 1,
+ .bus_clk_rate = { 400000, 0 },
+};
+
+static struct tegra_i2c_platform_data whistler_dvc_platform_data = {
+ .adapter_nr = 4,
+ .bus_count = 1,
+ .bus_clk_rate = { 400000, 0 },
+ .is_dvc = true,
+};
+
+static struct aic326x_pdata whistler_aic3262_pdata = {
+ /* debounce time */
+ .debounce_time_ms = 512,
+};
+
+static struct i2c_board_info __initdata wm8753_board_info[] = {
+ {
+ I2C_BOARD_INFO("wm8753", 0x1a),
+ .irq = TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_HP_DET),
+ },
+ {
+ I2C_BOARD_INFO("tlv320aic3262", 0x18),
+ .platform_data = &whistler_aic3262_pdata,
+ .irq = TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_HP_DET),
+ },
+};
+
+static void whistler_i2c_init(void)
+{
+ tegra_i2c_device1.dev.platform_data = &whistler_i2c1_platform_data;
+ tegra_i2c_device2.dev.platform_data = &whistler_i2c2_platform_data;
+ tegra_i2c_device3.dev.platform_data = &whistler_i2c3_platform_data;
+ tegra_i2c_device4.dev.platform_data = &whistler_dvc_platform_data;
+
+ platform_device_register(&tegra_i2c_device4);
+ platform_device_register(&tegra_i2c_device3);
+ platform_device_register(&tegra_i2c_device2);
+ platform_device_register(&tegra_i2c_device1);
+
+ i2c_register_board_info(4, wm8753_board_info,
+ ARRAY_SIZE(wm8753_board_info));
+}
+
+#define GPIO_SCROLL(_pinaction, _gpio, _desc) \
+{ \
+ .pinaction = GPIO_SCROLLWHEEL_PIN_##_pinaction, \
+ .gpio = TEGRA_GPIO_##_gpio, \
+ .desc = _desc, \
+ .active_low = 1, \
+ .debounce_interval = 2, \
+}
+
+static struct gpio_scrollwheel_button scroll_keys[] = {
+ [0] = GPIO_SCROLL(ONOFF, PR3, "sw_onoff"),
+ [1] = GPIO_SCROLL(PRESS, PQ5, "sw_press"),
+ [2] = GPIO_SCROLL(ROT1, PQ3, "sw_rot1"),
+ [3] = GPIO_SCROLL(ROT2, PQ4, "sw_rot2"),
+};
+
+static struct gpio_scrollwheel_platform_data whistler_scroll_platform_data = {
+ .buttons = scroll_keys,
+ .nbuttons = ARRAY_SIZE(scroll_keys),
+};
+
+static struct platform_device whistler_scroll_device = {
+ .name = "alps-gpio-scrollwheel",
+ .id = 0,
+ .dev = {
+ .platform_data = &whistler_scroll_platform_data,
+ },
+};
+
+static struct platform_device tegra_camera = {
+ .name = "tegra_camera",
+ .id = -1,
+};
+
+static struct tegra_wm8753_platform_data whistler_audio_pdata = {
+ .gpio_spkr_en = -1,
+ .gpio_hp_det = TEGRA_GPIO_HP_DET,
+ .gpio_hp_mute = -1,
+ .gpio_int_mic_en = -1,
+ .gpio_ext_mic_en = -1,
+ .debounce_time_hp = 200,
+};
+
+static struct platform_device whistler_audio_device1 = {
+ .name = "tegra-snd-aic326x",
+ .id = 0,
+ .dev = {
+ .platform_data = &whistler_audio_pdata,
+ },
+};
+
+static struct platform_device whistler_audio_device2 = {
+ .name = "tegra-snd-wm8753",
+ .id = 0,
+ .dev = {
+ .platform_data = &whistler_audio_pdata,
+ },
+};
+
+static struct platform_device *whistler_devices[] __initdata = {
+ &tegra_pmu_device,
+ &tegra_udc_device,
+ &tegra_gart_device,
+ &tegra_wdt_device,
+ &tegra_avp_device,
+ &whistler_scroll_device,
+ &tegra_camera,
+ &tegra_i2s_device1,
+ &tegra_i2s_device2,
+ &tegra_spdif_device,
+ &tegra_das_device,
+ &spdif_dit_device,
+ &bluetooth_dit_device,
+ &whistler_bcm4329_rfkill_device,
+ &tegra_pcm_device,
+ &whistler_audio_device1,
+ &whistler_audio_device2,
+};
+
+static struct synaptics_i2c_rmi_platform_data synaptics_pdata = {
+ .flags = SYNAPTICS_FLIP_X | SYNAPTICS_FLIP_Y | SYNAPTICS_SWAP_XY,
+ .irqflags = IRQF_TRIGGER_LOW,
+};
+
+static const struct i2c_board_info whistler_i2c_touch_info[] = {
+ {
+ I2C_BOARD_INFO("synaptics-rmi-ts", 0x20),
+ .irq = TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PC6),
+ .platform_data = &synaptics_pdata,
+ },
+};
+
+static int __init whistler_touch_init(void)
+{
+ tegra_gpio_enable(TEGRA_GPIO_PC6);
+ i2c_register_board_info(0, whistler_i2c_touch_info, 1);
+
+ return 0;
+}
+
+static int __init whistler_scroll_init(void)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(scroll_keys); i++)
+ tegra_gpio_enable(scroll_keys[i].gpio);
+
+ return 0;
+}
+
+static struct usb_phy_plat_data tegra_usb_phy_pdata[] = {
+ [0] = {
+ .instance = 0,
+ .vbus_irq = MAX8907C_INT_BASE + MAX8907C_IRQ_VCHG_DC_R,
+ .vbus_gpio = USB1_VBUS_GPIO,
+ },
+ [1] = {
+ .instance = 1,
+ .vbus_gpio = -1,
+ },
+ [2] = {
+ .instance = 2,
+ .vbus_gpio = -1,
+ },
+};
+
+static struct tegra_ehci_platform_data tegra_ehci_pdata[] = {
+ [0] = {
+ .phy_config = &utmi_phy_config[0],
+ .operating_mode = TEGRA_USB_HOST,
+ .power_down_on_bus_suspend = 1,
+ },
+ [1] = {
+ .phy_config = &ulpi_phy_config,
+ .operating_mode = TEGRA_USB_HOST,
+ .power_down_on_bus_suspend = 1,
+ },
+ [2] = {
+ .phy_config = &utmi_phy_config[1],
+ .operating_mode = TEGRA_USB_HOST,
+ .power_down_on_bus_suspend = 1,
+ },
+};
+
+static struct tegra_otg_platform_data tegra_otg_pdata = {
+ .ehci_device = &tegra_ehci1_device,
+ .ehci_pdata = &tegra_ehci_pdata[0],
+};
+
+static int __init whistler_gps_init(void)
+{
+ tegra_gpio_enable(TEGRA_GPIO_PU4);
+ return 0;
+}
+
+static void whistler_power_off(void)
+{
+ int ret;
+
+ ret = max8907c_power_off();
+ if (ret)
+ pr_err("whistler: failed to power off\n");
+
+ while (1);
+}
+
+static void __init whistler_power_off_init(void)
+{
+ pm_power_off = whistler_power_off;
+}
+
+static void whistler_usb_init(void)
+{
+ tegra_usb_phy_init(tegra_usb_phy_pdata, ARRAY_SIZE(tegra_usb_phy_pdata));
+
+ tegra_otg_device.dev.platform_data = &tegra_otg_pdata;
+ platform_device_register(&tegra_otg_device);
+
+ tegra_ehci3_device.dev.platform_data = &tegra_ehci_pdata[2];
+ platform_device_register(&tegra_ehci3_device);
+}
+
+static void __init tegra_whistler_init(void)
+{
+ char serial[20];
+
+ tegra_clk_init_from_table(whistler_clk_init_table);
+ whistler_pinmux_init();
+ whistler_i2c_init();
+ whistler_uart_init();
+ platform_add_devices(whistler_devices, ARRAY_SIZE(whistler_devices));
+
+ whistler_sdhci_init();
+ whistler_regulator_init();
+ whistler_panel_init();
+ whistler_sensors_init();
+ whistler_touch_init();
+ whistler_kbc_init();
+ whistler_gps_init();
+ whistler_usb_init();
+ whistler_scroll_init();
+ whistler_power_off_init();
+ whistler_emc_init();
+ whistler_baseband_init();
+ whistler_setup_bluesleep();
+ tegra_release_bootloader_fb();
+}
+
+int __init tegra_whistler_protected_aperture_init(void)
+{
+ tegra_protected_aperture_init(tegra_grhost_aperture);
+ return 0;
+}
+
+void __init tegra_whistler_reserve(void)
+{
+ if (memblock_reserve(0x0, 4096) < 0)
+ pr_warn("Cannot reserve first 4K of memory for safety\n");
+
+ tegra_reserve(SZ_128M, SZ_8M, SZ_16M);
+}
+
+MACHINE_START(WHISTLER, "whistler")
+ .boot_params = 0x00000100,
+ .map_io = tegra_map_common_io,
+ .reserve = tegra_whistler_reserve,
+ .init_early = tegra_init_early,
+ .init_irq = tegra_init_irq,
+ .timer = &tegra_timer,
+ .init_machine = tegra_whistler_init,
+MACHINE_END
diff --git a/arch/arm/mach-tegra/board-whistler.h b/arch/arm/mach-tegra/board-whistler.h
new file mode 100644
index 000000000000..a31be96e915b
--- /dev/null
+++ b/arch/arm/mach-tegra/board-whistler.h
@@ -0,0 +1,39 @@
+/*
+ * arch/arm/mach-tegra/board-whistler.h
+ *
+ * Copyright (C) 2010 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 _MACH_TEGRA_BOARD_WHISTLER_H
+#define _MACH_TEGRA_BOARD_WHISTLER_H
+
+int whistler_regulator_init(void);
+int whistler_sdhci_init(void);
+int whistler_pinmux_init(void);
+int whistler_panel_init(void);
+int whistler_kbc_init(void);
+int whistler_sensors_init(void);
+int whistler_baseband_init(void);
+int whistler_emc_init(void);
+
+/* Interrupt numbers from external peripherals */
+#define MAX8907C_INT_BASE TEGRA_NR_IRQS
+#define MAX8907C_INT_END (MAX8907C_INT_BASE + 31)
+
+/* Audio-related GPIOs */
+#define TEGRA_GPIO_HP_DET TEGRA_GPIO_PW3
+
+/* TCA6416 GPIO expander */
+#define TCA6416_GPIO_BASE (TEGRA_NR_GPIOS)
+
+#endif
diff --git a/arch/arm/mach-tegra/board.h b/arch/arm/mach-tegra/board.h
index 1d14df7eb7de..11c10f38b0b1 100644
--- a/arch/arm/mach-tegra/board.h
+++ b/arch/arm/mach-tegra/board.h
@@ -2,6 +2,7 @@
* arch/arm/mach-tegra/board.h
*
* Copyright (C) 2010 Google, Inc.
+ * Copyright (C) 2011 NVIDIA Corporation.
*
* Author:
* Colin Cross <ccross@google.com>
@@ -22,14 +23,94 @@
#define __MACH_TEGRA_BOARD_H
#include <linux/types.h>
+#include <linux/power_supply.h>
+
+#define NVMAP_HEAP_CARVEOUT_IRAM_INIT \
+ { .name = "iram", \
+ .usage_mask = NVMAP_HEAP_CARVEOUT_IRAM, \
+ .base = TEGRA_IRAM_BASE + TEGRA_RESET_HANDLER_SIZE, \
+ .size = TEGRA_IRAM_SIZE - TEGRA_RESET_HANDLER_SIZE, \
+ .buddy_size = 0, /* no buddy allocation for IRAM */ \
+ }
void tegra_assert_system_reset(char mode, const char *cmd);
void __init tegra_init_early(void);
+void __init tegra_mc_init(void);
void __init tegra_map_common_io(void);
void __init tegra_init_irq(void);
void __init tegra_init_clock(void);
+void __init tegra_reserve(unsigned long carveout_size, unsigned long fb_size,
+ unsigned long fb2_size);
int __init tegra_pcie_init(bool init_port0, bool init_port1);
+void tegra_init_cache(bool init);
+void __init tegra_release_bootloader_fb(void);
+void __init tegra_protected_aperture_init(unsigned long aperture);
+void tegra_move_framebuffer(unsigned long to, unsigned long from,
+ unsigned long size);
+bool is_tegra_debug_uartport_hs(void);
+int get_tegra_uart_debug_port_id(void);
+int arb_lost_recovery(int scl_gpio, int sda_gpio);
+
+extern unsigned long tegra_bootloader_fb_start;
+extern unsigned long tegra_bootloader_fb_size;
+extern unsigned long tegra_fb_start;
+extern unsigned long tegra_fb_size;
+extern unsigned long tegra_fb2_start;
+extern unsigned long tegra_fb2_size;
+extern unsigned long tegra_carveout_start;
+extern unsigned long tegra_carveout_size;
+extern unsigned long tegra_vpr_start;
+extern unsigned long tegra_vpr_size;
+extern unsigned long tegra_lp0_vec_start;
+extern unsigned long tegra_lp0_vec_size;
+extern bool tegra_lp0_vec_relocate;
+extern unsigned long tegra_grhost_aperture;
extern struct sys_timer tegra_timer;
+
+enum board_fab {
+ BOARD_FAB_A = 0,
+ BOARD_FAB_B,
+ BOARD_FAB_C,
+ BOARD_FAB_D,
+};
+
+struct board_info {
+ u16 board_id;
+ u16 sku;
+ u8 fab;
+ u8 major_revision;
+ u8 minor_revision;
+};
+
+enum panel_type {
+ panel_type_lvds = 0,
+ panel_type_dsi,
+};
+
+enum audio_codec_type {
+ audio_codec_none,
+ audio_codec_wm8903,
+};
+
+void tegra_get_board_info(struct board_info *);
+void tegra_get_pmu_board_info(struct board_info *bi);
+void tegra_get_display_board_info(struct board_info *bi);
+#ifdef CONFIG_TEGRA_CONVSERVATIVE_GOV_ON_EARLYSUPSEND
+#define SET_CONSERVATIVE_GOVERNOR_UP_THRESHOLD 95
+#define SET_CONSERVATIVE_GOVERNOR_DOWN_THRESHOLD 50
+
+void cpufreq_save_default_governor(void);
+void cpufreq_restore_default_governor(void);
+void cpufreq_set_conservative_governor(void);
+void cpufreq_set_conservative_governor_param(int up_th, int down_th);
+#endif
+int get_core_edp(void);
+enum panel_type get_panel_type(void);
+int tegra_get_modem_id(void);
+enum power_supply_type get_power_supply_type(void);
+enum audio_codec_type get_audio_codec_type(void);
+int get_maximum_cpu_current_supported(void);
+
#endif
diff --git a/arch/arm/mach-tegra/clock.c b/arch/arm/mach-tegra/clock.c
index f8d41ffc0ca9..ec483c840672 100644
--- a/arch/arm/mach-tegra/clock.c
+++ b/arch/arm/mach-tegra/clock.c
@@ -5,6 +5,8 @@
* Author:
* Colin Cross <ccross@google.com>
*
+ * Copyright (C) 2010-2011 NVIDIA Corporation
+ *
* 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.
@@ -24,34 +26,52 @@
#include <linux/init.h>
#include <linux/list.h>
#include <linux/module.h>
-#include <linux/sched.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <trace/events/power.h>
#include <mach/clk.h>
#include "board.h"
#include "clock.h"
+#include "dvfs.h"
+
+#define DISABLE_BOOT_CLOCKS 1
/*
* Locking:
*
- * Each struct clk has a spinlock.
+ * Each struct clk has a lock. Depending on the cansleep flag, that lock
+ * may be a spinlock or a mutex. For most clocks, the spinlock is sufficient,
+ * and using the spinlock allows the clock to be manipulated from an interrupt
+ * or while holding a spinlock. Some clocks may need to adjust a regulator
+ * in order to maintain the required voltage for a new frequency. Those
+ * clocks set the cansleep flag, and take a mutex so that the regulator api
+ * can be used while holding the lock.
*
* To avoid AB-BA locking problems, locks must always be traversed from child
* clock to parent clock. For example, when enabling a clock, the clock's lock
* is taken, and then clk_enable is called on the parent, which take's the
- * parent clock's lock. There is one exceptions to this ordering: When dumping
- * the clock tree through debugfs. In this case, clk_lock_all is called,
- * which attemps to iterate through the entire list of clocks and take every
- * clock lock. If any call to spin_trylock fails, all locked clocks are
- * unlocked, and the process is retried. When all the locks are held,
- * the only clock operation that can be called is clk_get_rate_all_locked.
+ * parent clock's lock. There are two exceptions to this ordering:
+ * 1. When setting a clock as cansleep, in which case the entire list of clocks
+ * is traversed to set the children as cansleep as well. This must occur
+ * during init, before any calls to clk_get, so no other clock locks can
+ * get taken.
+ * 2. When dumping the clock tree through debugfs. In this case, clk_lock_all
+ * is called, which attemps to iterate through the entire list of clocks
+ * and take every clock lock. If any call to clk_trylock fails, a locked
+ * clocks are unlocked, and the process is retried. When all the locks
+ * are held, the only clock operation that can be called is
+ * clk_get_rate_all_locked.
*
* Within a single clock, no clock operation can call another clock operation
- * on itself, except for clk_get_rate_locked and clk_set_rate_locked. Any
- * clock operation can call any other clock operation on any of it's possible
- * parents.
+ * on itself, except for clk_xxx_locked. Any clock operation can call any other
+ * clock operation on any of it's possible parents.
+ *
+ * clk_set_cansleep is used to mark a clock as sleeping. It is called during
+ * dvfs (Dynamic Voltage and Frequency Scaling) init on any clock that has a
+ * dvfs requirement, and propagated to all possible children of sleeping clock.
*
* An additional mutex, clock_list_lock, is used to protect the list of all
* clocks.
@@ -59,6 +79,10 @@
* The clock operations must lock internally to protect against
* read-modify-write on registers that are shared by multiple clocks
*/
+
+/* FIXME: remove and never ignore overclock */
+#define IGNORE_PARENT_OVERCLOCK 0
+
static DEFINE_MUTEX(clock_list_lock);
static LIST_HEAD(clocks);
@@ -76,8 +100,21 @@ struct clk *tegra_get_clock_by_name(const char *name)
mutex_unlock(&clock_list_lock);
return ret;
}
+EXPORT_SYMBOL(tegra_get_clock_by_name);
+
+static void clk_stats_update(struct clk *c)
+{
+ u64 cur_jiffies = get_jiffies_64();
+
+ if (c->refcnt) {
+ c->stats.time_on = cputime64_add(c->stats.time_on,
+ cputime64_sub(cur_jiffies, c->stats.last_update));
+ }
+
+ c->stats.last_update = cur_jiffies;
+}
-/* Must be called with c->spinlock held */
+/* Must be called with clk_lock(c) held */
static unsigned long clk_predict_rate_from_parent(struct clk *c, struct clk *p)
{
u64 rate;
@@ -93,7 +130,17 @@ static unsigned long clk_predict_rate_from_parent(struct clk *c, struct clk *p)
return rate;
}
-/* Must be called with c->spinlock held */
+unsigned long clk_get_max_rate(struct clk *c)
+{
+ return c->max_rate;
+}
+
+unsigned long clk_get_min_rate(struct clk *c)
+{
+ return c->min_rate;
+}
+
+/* Must be called with clk_lock(c) held */
unsigned long clk_get_rate_locked(struct clk *c)
{
unsigned long rate;
@@ -111,16 +158,54 @@ unsigned long clk_get_rate(struct clk *c)
unsigned long flags;
unsigned long rate;
- spin_lock_irqsave(&c->spinlock, flags);
+ clk_lock_save(c, &flags);
rate = clk_get_rate_locked(c);
- spin_unlock_irqrestore(&c->spinlock, flags);
+ clk_unlock_restore(c, &flags);
return rate;
}
EXPORT_SYMBOL(clk_get_rate);
+static void __clk_set_cansleep(struct clk *c)
+{
+ struct clk *child;
+ int i;
+ BUG_ON(mutex_is_locked(&c->mutex));
+ BUG_ON(spin_is_locked(&c->spinlock));
+
+ /* Make sure that all possible descendants of sleeping clock are
+ marked as sleeping (to eliminate "sleeping parent - non-sleeping
+ child" relationship */
+ list_for_each_entry(child, &clocks, node) {
+ bool possible_parent = (child->parent == c);
+
+ if (!possible_parent && child->inputs) {
+ for (i = 0; child->inputs[i].input; i++) {
+ if (child->inputs[i].input == c) {
+ possible_parent = true;
+ break;
+ }
+ }
+ }
+
+ if (possible_parent)
+ __clk_set_cansleep(child);
+ }
+
+ c->cansleep = true;
+}
+
+/* Must be called before any clk_get calls */
+void clk_set_cansleep(struct clk *c)
+{
+
+ mutex_lock(&clock_list_lock);
+ __clk_set_cansleep(c);
+ mutex_unlock(&clock_list_lock);
+}
+
int clk_reparent(struct clk *c, struct clk *parent)
{
c->parent = parent;
@@ -129,7 +214,7 @@ int clk_reparent(struct clk *c, struct clk *parent)
void clk_init(struct clk *c)
{
- spin_lock_init(&c->spinlock);
+ clk_lock_init(c);
if (c->ops && c->ops->init)
c->ops->init(c);
@@ -142,78 +227,117 @@ void clk_init(struct clk *c)
else
c->state = ON;
}
+ c->stats.last_update = get_jiffies_64();
mutex_lock(&clock_list_lock);
list_add(&c->node, &clocks);
mutex_unlock(&clock_list_lock);
}
-int clk_enable(struct clk *c)
+static int clk_enable_locked(struct clk *c)
{
int ret = 0;
- unsigned long flags;
+ int rate = clk_get_rate_locked(c);
+ bool set_rate = false;
- spin_lock_irqsave(&c->spinlock, flags);
+ if (rate > c->max_rate) {
+ rate = c->max_rate;
+ set_rate = true;
+ }
+
+ if (clk_is_auto_dvfs(c)) {
+ ret = tegra_dvfs_set_rate(c, rate);
+ if (ret)
+ return ret;
+ }
if (c->refcnt == 0) {
if (c->parent) {
ret = clk_enable(c->parent);
if (ret)
- goto out;
+ return ret;
}
+ if (set_rate)
+ clk_set_rate_locked(c, rate);
+
if (c->ops && c->ops->enable) {
ret = c->ops->enable(c);
+ trace_clock_enable(c->name, 1, 0);
if (ret) {
if (c->parent)
clk_disable(c->parent);
- goto out;
+ return ret;
}
c->state = ON;
c->set = true;
}
+ clk_stats_update(c);
}
c->refcnt++;
-out:
- spin_unlock_irqrestore(&c->spinlock, flags);
+
return ret;
}
-EXPORT_SYMBOL(clk_enable);
-void clk_disable(struct clk *c)
+
+int clk_enable(struct clk *c)
{
+ int ret = 0;
unsigned long flags;
- spin_lock_irqsave(&c->spinlock, flags);
+ clk_lock_save(c, &flags);
+ ret = clk_enable_locked(c);
+ clk_unlock_restore(c, &flags);
+ return ret;
+}
+EXPORT_SYMBOL(clk_enable);
+static void clk_disable_locked(struct clk *c)
+{
if (c->refcnt == 0) {
WARN(1, "Attempting to disable clock %s with refcnt 0", c->name);
- spin_unlock_irqrestore(&c->spinlock, flags);
return;
}
if (c->refcnt == 1) {
- if (c->ops && c->ops->disable)
+ if (c->ops && c->ops->disable) {
+ trace_clock_disable(c->name, 0, 0);
c->ops->disable(c);
-
+ }
if (c->parent)
clk_disable(c->parent);
c->state = OFF;
+ clk_stats_update(c);
}
c->refcnt--;
- spin_unlock_irqrestore(&c->spinlock, flags);
+ if (clk_is_auto_dvfs(c) && c->refcnt == 0)
+ tegra_dvfs_set_rate(c, 0);
}
-EXPORT_SYMBOL(clk_disable);
-int clk_set_parent(struct clk *c, struct clk *parent)
+void clk_disable(struct clk *c)
{
- int ret;
unsigned long flags;
+
+ clk_lock_save(c, &flags);
+ clk_disable_locked(c);
+ clk_unlock_restore(c, &flags);
+}
+EXPORT_SYMBOL(clk_disable);
+
+static int clk_rate_change_notify(struct clk *c, unsigned long rate)
+{
+ if (!c->rate_change_nh)
+ return -ENOSYS;
+ return raw_notifier_call_chain(c->rate_change_nh, rate, NULL);
+}
+
+int clk_set_parent_locked(struct clk *c, struct clk *parent)
+{
+ int ret = 0;
unsigned long new_rate;
unsigned long old_rate;
-
- spin_lock_irqsave(&c->spinlock, flags);
+ bool disable = false;
if (!c->ops || !c->ops->set_parent) {
ret = -ENOSYS;
@@ -223,12 +347,64 @@ int clk_set_parent(struct clk *c, struct clk *parent)
new_rate = clk_predict_rate_from_parent(c, parent);
old_rate = clk_get_rate_locked(c);
+ if (new_rate > clk_get_max_rate(c)) {
+
+ pr_err("Failed to set parent %s for %s (violates clock limit"
+ " %lu)\n", parent->name, c->name, clk_get_max_rate(c));
+#if !IGNORE_PARENT_OVERCLOCK
+ ret = -EINVAL;
+ goto out;
+#endif
+ }
+
+ /* The new clock control register setting does not take effect if
+ * clock is disabled. Later, when the clock is enabled it would run
+ * for several cycles on the old parent, which may hang h/w if the
+ * parent is already disabled. To guarantee h/w switch to the new
+ * setting enable clock while setting parent.
+ */
+ if ((c->refcnt == 0) && (c->flags & MUX)) {
+ pr_debug("Setting parent of clock %s with refcnt 0\n", c->name);
+ ret = clk_enable_locked(c);
+ if (ret)
+ goto out;
+ disable = true;
+ }
+
+ if (clk_is_auto_dvfs(c) && c->refcnt > 0 &&
+ (!c->parent || new_rate > old_rate)) {
+ ret = tegra_dvfs_set_rate(c, new_rate);
+ if (ret)
+ goto out;
+ }
+
ret = c->ops->set_parent(c, parent);
if (ret)
goto out;
+ if (clk_is_auto_dvfs(c) && c->refcnt > 0 &&
+ new_rate < old_rate)
+ ret = tegra_dvfs_set_rate(c, new_rate);
+
+ if (new_rate != old_rate)
+ clk_rate_change_notify(c, new_rate);
+
out:
- spin_unlock_irqrestore(&c->spinlock, flags);
+ if (disable)
+ clk_disable_locked(c);
+ return ret;
+}
+
+
+int clk_set_parent(struct clk *c, struct clk *parent)
+{
+ int ret = 0;
+ unsigned long flags;
+
+ clk_lock_save(c, &flags);
+ ret = clk_set_parent_locked(c, parent);
+ clk_unlock_restore(c, &flags);
+
return ret;
}
EXPORT_SYMBOL(clk_set_parent);
@@ -241,42 +417,84 @@ EXPORT_SYMBOL(clk_get_parent);
int clk_set_rate_locked(struct clk *c, unsigned long rate)
{
+ int ret = 0;
+ unsigned long old_rate, max_rate;
long new_rate;
+ bool disable = false;
- if (!c->ops || !c->ops->set_rate)
- return -ENOSYS;
+ old_rate = clk_get_rate_locked(c);
- if (rate > c->max_rate)
- rate = c->max_rate;
+ max_rate = clk_get_max_rate(c);
+ if (rate > max_rate)
+ rate = max_rate;
if (c->ops && c->ops->round_rate) {
new_rate = c->ops->round_rate(c, rate);
- if (new_rate < 0)
- return new_rate;
+ if (new_rate < 0) {
+ ret = new_rate;
+ return ret;
+ }
rate = new_rate;
}
- return c->ops->set_rate(c, rate);
+ /* The new clock control register setting does not take effect if
+ * clock is disabled. Later, when the clock is enabled it would run
+ * for several cycles on the old rate, which may over-clock module
+ * at given voltage. To guarantee h/w switch to the new setting
+ * enable clock while setting rate.
+ */
+ if ((c->refcnt == 0) && (c->flags & (DIV_U71 | DIV_U16)) &&
+ clk_is_auto_dvfs(c)) {
+ pr_debug("Setting rate of clock %s with refcnt 0\n", c->name);
+ ret = clk_enable_locked(c);
+ if (ret)
+ goto out;
+ disable = true;
+ }
+
+ if (clk_is_auto_dvfs(c) && rate > old_rate && c->refcnt > 0) {
+ ret = tegra_dvfs_set_rate(c, rate);
+ if (ret)
+ goto out;
+ }
+
+ trace_clock_set_rate(c->name, rate, 0);
+ ret = c->ops->set_rate(c, rate);
+ if (ret)
+ goto out;
+
+ if (clk_is_auto_dvfs(c) && rate < old_rate && c->refcnt > 0)
+ ret = tegra_dvfs_set_rate(c, rate);
+
+ if (rate != old_rate)
+ clk_rate_change_notify(c, rate);
+
+out:
+ if (disable)
+ clk_disable_locked(c);
+ return ret;
}
int clk_set_rate(struct clk *c, unsigned long rate)
{
- int ret;
unsigned long flags;
+ int ret;
- spin_lock_irqsave(&c->spinlock, flags);
+ if (!c->ops || !c->ops->set_rate)
+ return -ENOSYS;
+
+ clk_lock_save(c, &flags);
ret = clk_set_rate_locked(c, rate);
- spin_unlock_irqrestore(&c->spinlock, flags);
+ clk_unlock_restore(c, &flags);
return ret;
}
EXPORT_SYMBOL(clk_set_rate);
-
/* Must be called with clocks lock and all indvidual clock locks held */
unsigned long clk_get_rate_all_locked(struct clk *c)
{
@@ -303,27 +521,50 @@ unsigned long clk_get_rate_all_locked(struct clk *c)
long clk_round_rate(struct clk *c, unsigned long rate)
{
- unsigned long flags;
+ unsigned long flags, max_rate;
long ret;
- spin_lock_irqsave(&c->spinlock, flags);
+ clk_lock_save(c, &flags);
if (!c->ops || !c->ops->round_rate) {
ret = -ENOSYS;
goto out;
}
- if (rate > c->max_rate)
- rate = c->max_rate;
+ max_rate = clk_get_max_rate(c);
+ if (rate > max_rate)
+ rate = max_rate;
ret = c->ops->round_rate(c, rate);
out:
- spin_unlock_irqrestore(&c->spinlock, flags);
+ clk_unlock_restore(c, &flags);
return ret;
}
EXPORT_SYMBOL(clk_round_rate);
+static int tegra_clk_clip_rate_for_parent(struct clk *c, struct clk *p)
+{
+ unsigned long flags, max_rate, old_rate, new_rate;
+
+ clk_lock_save(c, &flags);
+
+ max_rate = clk_get_max_rate(c);
+ new_rate = clk_predict_rate_from_parent(c, p);
+ old_rate = clk_get_rate_locked(c);
+
+ clk_unlock_restore(c, &flags);
+
+ if (new_rate > max_rate) {
+ u64 rate = max_rate;
+ rate *= old_rate;
+ do_div(rate, new_rate);
+
+ return clk_set_rate(c, (unsigned long)rate);
+ }
+ return 0;
+}
+
static int tegra_clk_init_one_from_table(struct tegra_clk_init_table *table)
{
struct clk *c;
@@ -348,6 +589,14 @@ static int tegra_clk_init_one_from_table(struct tegra_clk_init_table *table)
}
if (c->parent != p) {
+ ret = tegra_clk_clip_rate_for_parent(c, p);
+ if (ret) {
+ pr_warning("Unable to clip rate for parent %s"
+ " of clock %s: %d\n",
+ table->parent, table->name, ret);
+ return -EINVAL;
+ }
+
ret = clk_set_parent(c, p);
if (ret) {
pr_warning("Unable to set parent %s of clock %s: %d\n",
@@ -387,60 +636,272 @@ EXPORT_SYMBOL(tegra_clk_init_from_table);
void tegra_periph_reset_deassert(struct clk *c)
{
- tegra2_periph_reset_deassert(c);
+ BUG_ON(!c->ops->reset);
+ c->ops->reset(c, false);
}
EXPORT_SYMBOL(tegra_periph_reset_deassert);
void tegra_periph_reset_assert(struct clk *c)
{
- tegra2_periph_reset_assert(c);
+ BUG_ON(!c->ops->reset);
+ c->ops->reset(c, true);
}
EXPORT_SYMBOL(tegra_periph_reset_assert);
+int tegra_is_clk_enabled(struct clk *c)
+{
+ return c->refcnt;
+}
+EXPORT_SYMBOL(tegra_is_clk_enabled);
+
+int tegra_clk_shared_bus_update(struct clk *c)
+{
+ int ret = 0;
+ unsigned long flags;
+
+ clk_lock_save(c, &flags);
+
+ if (c->ops && c->ops->shared_bus_update)
+ ret = c->ops->shared_bus_update(c);
+
+ clk_unlock_restore(c, &flags);
+ return ret;
+}
+
+/* dvfs initialization may lower default maximum rate */
+void __init tegra_init_max_rate(struct clk *c, unsigned long max_rate)
+{
+ struct clk *shared_bus_user;
+
+ if (c->max_rate <= max_rate)
+ return;
+
+ pr_warning("Lowering %s maximum rate from %lu to %lu\n",
+ c->name, c->max_rate, max_rate);
+
+ c->max_rate = max_rate;
+ list_for_each_entry(shared_bus_user,
+ &c->shared_bus_list, u.shared_bus_user.node) {
+ shared_bus_user->u.shared_bus_user.rate = max_rate;
+ shared_bus_user->max_rate = max_rate;
+ }
+}
+
void __init tegra_init_clock(void)
{
- tegra2_init_clocks();
+ int ret;
+ struct clk *cpu;
+ struct clk *twd;
+
+ tegra_soc_init_clocks();
+ tegra_soc_init_dvfs();
+
+ /* The twd clock is a detached child of the CPU complex clock.
+ Force an update of the twd clock after DVFS as updated the
+ CPU clock rate. */
+ cpu = tegra_get_clock_by_name("cpu");
+ twd = tegra_get_clock_by_name("twd");
+ ret = clk_set_rate(twd, clk_get_rate(cpu));
+ if (ret)
+ pr_err("Failed to set twd clock rate: %d\n", ret);
+ else
+ pr_debug("TWD clock rate: %ld\n", clk_get_rate(twd));
+}
+
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+/* On tegra 2 SoC the SDMMC clock source register have extra bits that
+ * adjust the SDMMC controller delay between the clock and data to
+ * compenstate for delays on the PCB. */
+void tegra_sdmmc_tap_delay(struct clk *c, int delay) {
+ unsigned long flags;
+
+ clk_lock_save(c, &flags);
+ tegra2_sdmmc_tap_delay(c, delay);
+
+ clk_unlock_restore(c, &flags);
+}
+#endif
+
+static bool tegra_keep_boot_clocks = false;
+static int __init tegra_keep_boot_clocks_setup(char *__unused)
+{
+ tegra_keep_boot_clocks = true;
+ return 1;
+}
+__setup("tegra_keep_boot_clocks", tegra_keep_boot_clocks_setup);
+
+/*
+ * Bootloader may not match kernel restrictions on CPU clock sources.
+ * Make sure CPU clock is sourced from either main or backup parent.
+ */
+static int tegra_sync_cpu_clock(void)
+{
+ int ret;
+ unsigned long rate;
+ struct clk *c = tegra_get_clock_by_name("cpu");
+
+ BUG_ON(!c);
+ rate = clk_get_rate(c);
+ ret = clk_set_rate(c, rate);
+ if (ret)
+ pr_err("%s: Failed to sync CPU at rate %lu\n", __func__, rate);
+ else
+ pr_info("CPU rate: %lu MHz\n", clk_get_rate(c) / 1000000);
+ return ret;
}
+late_initcall(tegra_sync_cpu_clock);
/*
- * The SDMMC controllers have extra bits in the clock source register that
- * adjust the delay between the clock and data to compenstate for delays
- * on the PCB.
+ * Iterate through all clocks, disabling any for which the refcount is 0
+ * but the clock init detected the bootloader left the clock on.
*/
-void tegra_sdmmc_tap_delay(struct clk *c, int delay)
+static int __init tegra_init_disable_boot_clocks(void)
{
+#if DISABLE_BOOT_CLOCKS
unsigned long flags;
+ struct clk *c;
- spin_lock_irqsave(&c->spinlock, flags);
- tegra2_sdmmc_tap_delay(c, delay);
- spin_unlock_irqrestore(&c->spinlock, flags);
+ mutex_lock(&clock_list_lock);
+
+ list_for_each_entry(c, &clocks, node) {
+ clk_lock_save(c, &flags);
+ if (c->refcnt == 0 && c->state == ON &&
+ c->ops && c->ops->disable) {
+ pr_warn_once("%s clocks left on by bootloader:\n",
+ tegra_keep_boot_clocks ?
+ "Prevented disabling" :
+ "Disabling");
+
+ pr_warn(" %s\n", c->name);
+
+ if (!tegra_keep_boot_clocks) {
+ c->ops->disable(c);
+ c->state = OFF;
+ }
+ }
+ clk_unlock_restore(c, &flags);
+ }
+
+ mutex_unlock(&clock_list_lock);
+#endif
+ return 0;
+}
+late_initcall(tegra_init_disable_boot_clocks);
+
+/* Several extended clock configuration bits (e.g., clock routing, clock
+ * phase control) are included in PLL and peripheral clock source
+ * registers. */
+int tegra_clk_cfg_ex(struct clk *c, enum tegra_clk_ex_param p, u32 setting)
+{
+ int ret = 0;
+ unsigned long flags;
+
+ clk_lock_save(c, &flags);
+
+ if (!c->ops || !c->ops->clk_cfg_ex) {
+ ret = -ENOSYS;
+ goto out;
+ }
+ ret = c->ops->clk_cfg_ex(c, p, setting);
+
+out:
+ clk_unlock_restore(c, &flags);
+ return ret;
+}
+
+int tegra_register_clk_rate_notifier(struct clk *c, struct notifier_block *nb)
+{
+ int ret;
+ unsigned long flags;
+
+ if (!c->rate_change_nh)
+ return -ENOSYS;
+
+ clk_lock_save(c, &flags);
+ ret = raw_notifier_chain_register(c->rate_change_nh, nb);
+ clk_unlock_restore(c, &flags);
+ return ret;
+}
+
+void tegra_unregister_clk_rate_notifier(
+ struct clk *c, struct notifier_block *nb)
+{
+ unsigned long flags;
+
+ if (!c->rate_change_nh)
+ return;
+
+ clk_lock_save(c, &flags);
+ raw_notifier_chain_unregister(c->rate_change_nh, nb);
+ clk_unlock_restore(c, &flags);
}
#ifdef CONFIG_DEBUG_FS
+/*
+ * Attempt to lock all the clocks that are marked cansleep
+ * Must be called with irqs enabled
+ */
+static int __clk_lock_all_mutexes(void)
+{
+ struct clk *c;
+
+ might_sleep();
+
+ list_for_each_entry(c, &clocks, node)
+ if (clk_cansleep(c))
+ if (!mutex_trylock(&c->mutex))
+ goto unlock_mutexes;
+
+ return 0;
+
+unlock_mutexes:
+ list_for_each_entry_continue_reverse(c, &clocks, node)
+ if (clk_cansleep(c))
+ mutex_unlock(&c->mutex);
+
+ return -EAGAIN;
+}
+
+/*
+ * Attempt to lock all the clocks that are not marked cansleep
+ * Must be called with irqs disabled
+ */
static int __clk_lock_all_spinlocks(void)
{
struct clk *c;
list_for_each_entry(c, &clocks, node)
- if (!spin_trylock(&c->spinlock))
- goto unlock_spinlocks;
+ if (!clk_cansleep(c))
+ if (!spin_trylock(&c->spinlock))
+ goto unlock_spinlocks;
return 0;
unlock_spinlocks:
list_for_each_entry_continue_reverse(c, &clocks, node)
- spin_unlock(&c->spinlock);
+ if (!clk_cansleep(c))
+ spin_unlock(&c->spinlock);
return -EAGAIN;
}
+static void __clk_unlock_all_mutexes(void)
+{
+ struct clk *c;
+
+ list_for_each_entry_reverse(c, &clocks, node)
+ if (clk_cansleep(c))
+ mutex_unlock(&c->mutex);
+}
+
static void __clk_unlock_all_spinlocks(void)
{
struct clk *c;
list_for_each_entry_reverse(c, &clocks, node)
- spin_unlock(&c->spinlock);
+ if (!clk_cansleep(c))
+ spin_unlock(&c->spinlock);
}
/*
@@ -453,6 +914,10 @@ static void clk_lock_all(void)
{
int ret;
retry:
+ ret = __clk_lock_all_mutexes();
+ if (ret)
+ goto failed_mutexes;
+
local_irq_disable();
ret = __clk_lock_all_spinlocks();
@@ -464,7 +929,9 @@ retry:
failed_spinlocks:
local_irq_enable();
- yield();
+ __clk_unlock_all_mutexes();
+failed_mutexes:
+ msleep(1);
goto retry;
}
@@ -478,16 +945,28 @@ static void clk_unlock_all(void)
__clk_unlock_all_spinlocks();
local_irq_enable();
+
+ __clk_unlock_all_mutexes();
}
static struct dentry *clk_debugfs_root;
+static void dvfs_show_one(struct seq_file *s, struct dvfs *d, int level)
+{
+ seq_printf(s, "%*s %-*s%21s%d mV\n",
+ level * 3 + 1, "",
+ 30 - level * 3, d->dvfs_rail->reg_id,
+ "",
+ d->cur_millivolts);
+}
static void clock_tree_show_one(struct seq_file *s, struct clk *c, int level)
{
struct clk *child;
const char *state = "uninit";
char div[8] = {0};
+ unsigned long rate = clk_get_rate_all_locked(c);
+ unsigned long max_rate = clk_get_max_rate(c);;
if (c->state == ON)
state = "on";
@@ -511,12 +990,19 @@ static void clock_tree_show_one(struct seq_file *s, struct clk *c, int level)
}
}
- seq_printf(s, "%*s%c%c%-*s %-6s %-3d %-8s %-10lu\n",
+ seq_printf(s, "%*s%c%c%-*s%c %-6s %-3d %-8s %-10lu",
level * 3 + 1, "",
- c->rate > c->max_rate ? '!' : ' ',
+ rate > max_rate ? '!' : ' ',
!c->set ? '*' : ' ',
30 - level * 3, c->name,
- state, c->refcnt, div, clk_get_rate_all_locked(c));
+ c->cansleep ? '$' : ' ',
+ state, c->refcnt, div, rate);
+ if (c->parent && !list_empty(&c->parent->shared_bus_list))
+ seq_printf(s, " (%lu)", c->u.shared_bus_user.rate);
+ seq_printf(s, "\n");
+
+ if (c->dvfs)
+ dvfs_show_one(s, c->dvfs, level + 1);
list_for_each_entry(child, &clocks, node) {
if (child->parent != c)
@@ -529,8 +1015,8 @@ static void clock_tree_show_one(struct seq_file *s, struct clk *c, int level)
static int clock_tree_show(struct seq_file *s, void *data)
{
struct clk *c;
- seq_printf(s, " clock state ref div rate\n");
- seq_printf(s, "--------------------------------------------------------------\n");
+ seq_printf(s, " clock state ref div rate (shared rate)\n");
+ seq_printf(s, "------------------------------------------------------------------------------\n");
mutex_lock(&clock_list_lock);
@@ -558,6 +1044,61 @@ static const struct file_operations clock_tree_fops = {
.release = single_release,
};
+static void syncevent_one(struct clk *c)
+{
+ struct clk *child;
+
+ if (c->state == ON)
+ trace_clock_enable(c->name, 1, smp_processor_id());
+ else
+ trace_clock_disable(c->name, 0, smp_processor_id());
+
+ trace_clock_set_rate(c->name, clk_get_rate_all_locked(c),
+ smp_processor_id());
+
+ list_for_each_entry(child, &clocks, node) {
+ if (child->parent != c)
+ continue;
+
+ syncevent_one(child);
+ }
+}
+
+static int syncevent_write(struct file *file, const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct clk *c;
+ char buffer[40];
+ int buf_size;
+
+ memset(buffer, 0, sizeof(buffer));
+ buf_size = min(count, (sizeof(buffer)-1));
+
+ if (copy_from_user(buffer, user_buf, buf_size))
+ return -EFAULT;
+
+ if (!strnicmp("all", buffer, 3)) {
+ mutex_lock(&clock_list_lock);
+
+ clk_lock_all();
+
+ list_for_each_entry(c, &clocks, node) {
+ if (c->parent == NULL)
+ syncevent_one(c);
+ }
+
+ clk_unlock_all();
+
+ mutex_unlock(&clock_list_lock);
+ }
+
+ return count;
+}
+
+static const struct file_operations syncevent_fops = {
+ .write = syncevent_write,
+};
+
static int possible_parents_show(struct seq_file *s, void *data)
{
struct clk *c = s->private;
@@ -583,6 +1124,124 @@ static const struct file_operations possible_parents_fops = {
.release = single_release,
};
+static int parent_show(struct seq_file *s, void *data)
+{
+ struct clk *c = s->private;
+ struct clk *p = clk_get_parent(c);
+
+ seq_printf(s, "%s\n", p ? p->name : "clk_root");
+ return 0;
+}
+
+static int parent_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, parent_show, inode->i_private);
+}
+
+static int rate_get(void *data, u64 *val)
+{
+ struct clk *c = (struct clk *)data;
+ *val = (u64)clk_get_rate(c);
+ return 0;
+}
+
+static int state_get(void *data, u64 *val)
+{
+ struct clk *c = (struct clk *)data;
+ *val = (u64)((c->state == ON) ? 1 : 0);
+ return 0;
+}
+
+#ifdef CONFIG_TEGRA_CLOCK_DEBUG_WRITE
+
+static const mode_t parent_rate_mode = S_IRUGO | S_IWUSR;
+
+static ssize_t parent_write(struct file *file,
+ const char __user *userbuf, size_t count, loff_t *ppos)
+{
+ struct seq_file *s = file->private_data;
+ struct clk *c = s->private;
+ struct clk *p = NULL;
+ char buf[32];
+
+ if (sizeof(buf) <= count)
+ return -EINVAL;
+
+ if (copy_from_user(buf, userbuf, count))
+ return -EFAULT;
+
+ /* terminate buffer and trim - white spaces may be appended
+ * at the end when invoked from shell command line */
+ buf[count]='\0';
+ strim(buf);
+
+ p = tegra_get_clock_by_name(buf);
+ if (!p)
+ return -EINVAL;
+
+ if (clk_set_parent(c, p))
+ return -EINVAL;
+
+ return count;
+}
+
+static const struct file_operations parent_fops = {
+ .open = parent_open,
+ .read = seq_read,
+ .write = parent_write,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int rate_set(void *data, u64 val)
+{
+ struct clk *c = (struct clk *)data;
+ return clk_set_rate(c, (unsigned long)val);
+}
+DEFINE_SIMPLE_ATTRIBUTE(rate_fops, rate_get, rate_set, "%llu\n");
+
+static int state_set(void *data, u64 val)
+{
+ struct clk *c = (struct clk *)data;
+
+ if (val)
+ return clk_enable(c);
+ else {
+ clk_disable(c);
+ return 0;
+ }
+}
+DEFINE_SIMPLE_ATTRIBUTE(state_fops, state_get, state_set, "%llu\n");
+
+#else
+
+static const mode_t parent_rate_mode = S_IRUGO;
+
+static const struct file_operations parent_fops = {
+ .open = parent_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+DEFINE_SIMPLE_ATTRIBUTE(rate_fops, rate_get, NULL, "%llu\n");
+DEFINE_SIMPLE_ATTRIBUTE(state_fops, state_get, NULL, "%llu\n");
+#endif
+
+static int time_on_get(void *data, u64 *val)
+{
+ unsigned long flags;
+ struct clk *c = (struct clk *)data;
+
+ clk_lock_save(c, &flags);
+ clk_stats_update(c);
+ *val = cputime64_to_clock_t(c->stats.time_on);
+ clk_unlock_restore(c, &flags);
+
+ return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(time_on_fops, time_on_get, NULL, "%llu\n");
+
static int clk_debugfs_register_one(struct clk *c)
{
struct dentry *d;
@@ -596,11 +1255,31 @@ static int clk_debugfs_register_one(struct clk *c)
if (!d)
goto err_out;
- d = debugfs_create_u32("rate", S_IRUGO, c->dent, (u32 *)&c->rate);
+ d = debugfs_create_x32("flags", S_IRUGO, c->dent, (u32 *)&c->flags);
if (!d)
goto err_out;
- d = debugfs_create_x32("flags", S_IRUGO, c->dent, (u32 *)&c->flags);
+ d = debugfs_create_u32("max", S_IRUGO, c->dent, (u32 *)&c->max_rate);
+ if (!d)
+ goto err_out;
+
+ d = debugfs_create_file(
+ "parent", parent_rate_mode, c->dent, c, &parent_fops);
+ if (!d)
+ goto err_out;
+
+ d = debugfs_create_file(
+ "rate", parent_rate_mode, c->dent, c, &rate_fops);
+ if (!d)
+ goto err_out;
+
+ d = debugfs_create_file(
+ "state", parent_rate_mode, c->dent, c, &state_fops);
+ if (!d)
+ goto err_out;
+
+ d = debugfs_create_file(
+ "time_on", S_IRUGO, c->dent, c, &time_on_fops);
if (!d)
goto err_out;
@@ -653,6 +1332,12 @@ static int __init clk_debugfs_init(void)
if (!d)
goto err_out;
+ d = debugfs_create_file("syncevents", S_IRUGO|S_IWUSR, clk_debugfs_root, NULL,
+ &syncevent_fops);
+
+ if (dvfs_debugfs_init(clk_debugfs_root))
+ goto err_out;
+
list_for_each_entry(c, &clocks, node) {
err = clk_debugfs_register(c);
if (err)
diff --git a/arch/arm/mach-tegra/clock.h b/arch/arm/mach-tegra/clock.h
index 688316abc64e..53f3e96b8785 100644
--- a/arch/arm/mach-tegra/clock.h
+++ b/arch/arm/mach-tegra/clock.h
@@ -6,6 +6,8 @@
* Author:
* Colin Cross <ccross@google.com>
*
+ * Copyright (C) 2010-2011, NVIDIA Corporation.
+ *
* 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.
@@ -20,9 +22,13 @@
#ifndef __MACH_TEGRA_CLOCK_H
#define __MACH_TEGRA_CLOCK_H
-#include <linux/clkdev.h>
-#include <linux/list.h>
-#include <linux/spinlock.h>
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+#define USE_PLL_LOCK_BITS 0 /* Never use lock bits on Tegra2 */
+#else
+/* !!!FIXME!!! PLL lock bits should work on Tegra3 */
+#define USE_PLL_LOCK_BITS 0 /* Use lock bits for PLL stabiliation */
+#define USE_PLLE_SS 1 /* Use spread spectrum coefficients for PLLE */
+#endif
#define DIV_BUS (1 << 0)
#define DIV_U71 (1 << 1)
@@ -39,7 +45,28 @@
#define PERIPH_MANUAL_RESET (1 << 12)
#define PLL_ALT_MISC_REG (1 << 13)
#define PLLU (1 << 14)
+#define PLLX (1 << 15)
+#define MUX_PWM (1 << 16)
+#define MUX8 (1 << 17)
+#define DIV_U71_UART (1 << 18)
+#define MUX_CLK_OUT (1 << 19)
+#define PLLM (1 << 20)
+#define DIV_U71_INT (1 << 21)
+#define DIV_U71_IDLE (1 << 22)
#define ENABLE_ON_INIT (1 << 28)
+#define PERIPH_ON_APB (1 << 29)
+#define PERIPH_ON_CBUS (1 << 30)
+
+#ifndef __ASSEMBLY__
+
+#include <linux/clkdev.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+#include <asm/cputime.h>
+
+#include <mach/clk.h>
+#define MAX_SAME_LIMIT_SKU_IDS 16
struct clk;
@@ -64,7 +91,26 @@ struct clk_ops {
int (*set_parent)(struct clk *, struct clk *);
int (*set_rate)(struct clk *, unsigned long);
long (*round_rate)(struct clk *, unsigned long);
+ int (*clk_cfg_ex)(struct clk *, enum tegra_clk_ex_param, u32);
void (*reset)(struct clk *, bool);
+ int (*shared_bus_update)(struct clk *);
+};
+
+struct clk_stats {
+ cputime64_t time_on;
+ u64 last_update;
+};
+
+enum cpu_mode {
+ MODE_G = 0,
+ MODE_LP,
+};
+
+enum shared_bus_users_mode {
+ SHARED_FLOOR = 0,
+ SHARED_BW,
+ SHARED_CEILING,
+ SHARED_AUTO,
};
enum clk_state {
@@ -76,6 +122,7 @@ enum clk_state {
struct clk {
/* node for master clocks list */
struct list_head node; /* node for list of all clocks */
+ struct dvfs *dvfs;
struct clk_lookup lookup;
#ifdef CONFIG_DEBUG_FS
@@ -83,9 +130,12 @@ struct clk {
#endif
bool set;
struct clk_ops *ops;
+ unsigned long dvfs_rate;
unsigned long rate;
unsigned long max_rate;
unsigned long min_rate;
+ bool auto_dvfs;
+ bool cansleep;
u32 flags;
const char *name;
@@ -94,12 +144,14 @@ struct clk {
struct clk *parent;
u32 div;
u32 mul;
+ struct clk_stats stats;
const struct clk_mux_sel *inputs;
u32 reg;
u32 reg_shift;
struct list_head shared_bus_list;
+ struct clk_mux_sel shared_bus_backup;
union {
struct {
@@ -114,6 +166,7 @@ struct clk {
unsigned long vco_max;
const struct clk_pll_freq_table *freq_table;
int lock_delay;
+ unsigned long fixed_rate;
} pll;
struct {
u32 sel;
@@ -122,14 +175,29 @@ struct clk {
struct {
struct clk *main;
struct clk *backup;
+ enum cpu_mode mode;
} cpu;
struct {
+ struct clk *pclk;
+ struct clk *hclk;
+ struct clk *sclk_low;
+ struct clk *sclk_high;
+ unsigned long threshold;
+ } system;
+ struct {
struct list_head node;
bool enabled;
unsigned long rate;
+ const char *client_id;
+ struct clk *client;
+ u32 client_div;
+ enum shared_bus_users_mode mode;
} shared_bus_user;
} u;
+ struct raw_notifier_head *rate_change_nh;
+
+ struct mutex mutex;
spinlock_t spinlock;
};
@@ -145,16 +213,83 @@ struct tegra_clk_init_table {
bool enabled;
};
-void tegra2_init_clocks(void);
-void tegra2_periph_reset_deassert(struct clk *c);
-void tegra2_periph_reset_assert(struct clk *c);
+struct tegra_sku_rate_limit {
+ const char *clk_name;
+ unsigned long max_rate;
+ int sku_ids[MAX_SAME_LIMIT_SKU_IDS];
+};
+
+void tegra_soc_init_clocks(void);
+void tegra_init_max_rate(struct clk *c, unsigned long max_rate);
void clk_init(struct clk *clk);
struct clk *tegra_get_clock_by_name(const char *name);
unsigned long clk_measure_input_freq(void);
int clk_reparent(struct clk *c, struct clk *parent);
void tegra_clk_init_from_table(struct tegra_clk_init_table *table);
+void clk_set_cansleep(struct clk *c);
+unsigned long clk_get_max_rate(struct clk *c);
+unsigned long clk_get_min_rate(struct clk *c);
unsigned long clk_get_rate_locked(struct clk *c);
int clk_set_rate_locked(struct clk *c, unsigned long rate);
+int clk_set_parent_locked(struct clk *c, struct clk *parent);
+int tegra_clk_shared_bus_update(struct clk *c);
void tegra2_sdmmc_tap_delay(struct clk *c, int delay);
+int tegra_emc_set_rate(unsigned long rate);
+long tegra_emc_round_rate(unsigned long rate);
+struct clk *tegra_emc_predict_parent(unsigned long rate, u32 *div_value);
+void tegra_emc_timing_invalidate(void);
+
+static inline bool clk_is_auto_dvfs(struct clk *c)
+{
+ return c->auto_dvfs;
+}
+
+static inline bool clk_is_dvfs(struct clk *c)
+{
+ return (c->dvfs != NULL);
+}
+
+static inline bool clk_cansleep(struct clk *c)
+{
+ return c->cansleep;
+}
+static inline void clk_lock_save(struct clk *c, unsigned long *flags)
+{
+ if (clk_cansleep(c)) {
+ *flags = 0;
+ mutex_lock(&c->mutex);
+ } else {
+ spin_lock_irqsave(&c->spinlock, *flags);
+ }
+}
+
+static inline void clk_unlock_restore(struct clk *c, unsigned long *flags)
+{
+ if (clk_cansleep(c))
+ mutex_unlock(&c->mutex);
+ else
+ spin_unlock_irqrestore(&c->spinlock, *flags);
+}
+
+static inline void clk_lock_init(struct clk *c)
+{
+ mutex_init(&c->mutex);
+ spin_lock_init(&c->spinlock);
+}
+
+#ifdef CONFIG_CPU_FREQ
+struct cpufreq_frequency_table;
+
+struct tegra_cpufreq_table_data {
+ struct cpufreq_frequency_table *freq_table;
+ int throttle_lowest_index;
+ int throttle_highest_index;
+ int suspend_index;
+};
+struct tegra_cpufreq_table_data *tegra_cpufreq_table_get(void);
+unsigned long tegra_emc_to_cpu_ratio(unsigned long cpu_rate);
+#endif
+
+#endif
#endif
diff --git a/arch/arm/mach-tegra/common-t2.c b/arch/arm/mach-tegra/common-t2.c
new file mode 100644
index 000000000000..6f9b177892ce
--- /dev/null
+++ b/arch/arm/mach-tegra/common-t2.c
@@ -0,0 +1,192 @@
+/*
+ * arch/arm/mach-tegra/common-t2.c
+ *
+ * Tegra 2 SoC-specific initialization (memory controller, etc.)
+ *
+ * Copyright (c) 2009-2011, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+
+#include <mach/iomap.h>
+#include <mach/irqs.h>
+
+#define MC_INT_STATUS 0x0
+#define MC_INT_MASK 0x4
+#define MC_INT_DECERR_EMEM_OTHERS (1<<6)
+#define MC_INT_INVALID_GART_PAGE (1<<7)
+#define MC_INT_SECURITY_VIOLATION (1<<8)
+
+#define MC_GART_ERROR_STATUS 0x30
+#define MC_GART_ERROR_ADDRESS 0x34
+
+#define MC_DECERR_EMEM_OTHERS_STATUS 0x58
+#define MC_DECERR_EMEM_OTHERS_ADDRESS 0x5c
+
+#define MC_SECURITY_VIOLATION_STATUS 0x74
+#define MC_SECURITY_VIOLATION_ADDRESS 0x78
+
+struct mc_client {
+ bool write;
+ const char *name;
+};
+
+#define client(_name,_write) \
+ { \
+ .write = _write, \
+ .name = _name, \
+ }
+
+static const struct mc_client mc_clients[] = {
+ client("display0_wina", false), client("display1_wina", false),
+ client("display0_winb", false), client("display1_winb", false),
+ client("display0_winc", false), client("display1_winc", false),
+ client("display0_winb_vfilter", false),
+ client("display1_winb_vfilter", false),
+ client("epp", false), client("gr2d_pat", false),
+ client("gr2d_src", false), client("mpe_unified", false),
+ client("vi_chroma_filter", false), client("cop", false),
+ client("display0_cursor", false), client("display1_cursor", false),
+ client("gr3d_fdc", false), client("gr2d_dst", false),
+ client("host1x_dma", false), client("host1x_generic", false),
+ client("gr3d_idx", false), client("cpu_uncached", false),
+ client("mpe_intrapred", false), client("mpe_mpea", false),
+ client("mpe_mpec", false), client("ahb_dma", false),
+ client("ahb_slave", false), client("gr3d_tex", false),
+ client("vde_bsev", false), client("vde_mbe", false),
+ client("vde_mce", false), client("vde_tpe", false),
+ client("epp_u", true), client("epp_v", true),
+ client("epp_y", true), client("mpe_unified", true),
+ client("vi_sb", true), client("vi_u", true),
+ client("vi_v", true), client("vi_y", true),
+ client("gr2d_dst", true), client("gr3d_fdc", true),
+ client("host1x", true), client("isp", true),
+ client("cpu_uncached", true), client("mpe_mpec", true),
+ client("ahb_dma", true), client("ahb_slave", true),
+ client("avp_bsev", true), client("avp_mbe", true),
+ client("avp_tpm", true),
+};
+
+static DEFINE_SPINLOCK(mc_lock);
+static unsigned long error_count = 0;
+#define MAX_PRINTS 5
+
+static void unthrottle_prints(struct work_struct *work)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&mc_lock, flags);
+ error_count = 0;
+ spin_unlock_irqrestore(&mc_lock, flags);
+}
+
+static DECLARE_DELAYED_WORK(unthrottle_prints_work, unthrottle_prints);
+
+static irqreturn_t tegra_mc_error_isr(int irq, void *data)
+{
+ void __iomem *mc = IO_ADDRESS(TEGRA_MC_BASE);
+ unsigned long count;
+ u32 stat;
+
+ stat = readl(mc + MC_INT_STATUS);
+ stat &= (MC_INT_SECURITY_VIOLATION |
+ MC_INT_INVALID_GART_PAGE |
+ MC_INT_DECERR_EMEM_OTHERS);
+
+ __cancel_delayed_work(&unthrottle_prints_work);
+
+ spin_lock(&mc_lock);
+ count = ++error_count;
+ spin_unlock(&mc_lock);
+
+ if (count >= MAX_PRINTS) {
+ if (count == MAX_PRINTS)
+ pr_err("Too many MC errors; throttling prints\n");
+ schedule_delayed_work(&unthrottle_prints_work, HZ/2);
+ goto out;
+ }
+
+ if (stat & MC_INT_DECERR_EMEM_OTHERS) {
+ const struct mc_client *client = NULL;
+ u32 addr, req;
+
+ req = readl(mc + MC_DECERR_EMEM_OTHERS_STATUS);
+ addr = readl(mc + MC_DECERR_EMEM_OTHERS_ADDRESS);
+ req &= 0x3f;
+ if (req < ARRAY_SIZE(mc_clients))
+ client = &mc_clients[req];
+
+ pr_err("MC_DECERR: %p %s (%s)\n", (void*)addr,
+ (client) ? client->name : "unknown",
+ (client && client->write) ? "write" : "read");
+ }
+
+ if (stat & MC_INT_INVALID_GART_PAGE) {
+ const struct mc_client *client = NULL;
+ u32 addr, req;
+
+ req = readl(mc + MC_GART_ERROR_STATUS);
+ addr = readl(mc + MC_GART_ERROR_ADDRESS);
+ req = (req >> 1) & 0x3f;
+
+ if (req < ARRAY_SIZE(mc_clients))
+ client = &mc_clients[req];
+
+ pr_err("MC_GART_ERR: %p %s (%s)\n", (void*)addr,
+ (client) ? client->name : "unknown",
+ (client && client->write) ? "write" : "read");
+ }
+
+ if (stat & MC_INT_SECURITY_VIOLATION) {
+ const struct mc_client *client = NULL;
+ const char *type = NULL;
+ u32 addr, req;
+
+ req = readl(mc + MC_SECURITY_VIOLATION_STATUS);
+ addr = readl(mc + MC_SECURITY_VIOLATION_ADDRESS);
+
+ type = (req & (1<<30)) ? "carveout" : "trustzone";
+
+ req &= 0x3f;
+ if (req < ARRAY_SIZE(mc_clients))
+ client = &mc_clients[req];
+
+ pr_err("MC_SECURITY_ERR (%s): %p %s (%s)\n", type, (void*)addr,
+ (client) ? client->name : "unknown",
+ (client && client->write) ? "write" : "read");
+ }
+out:
+ writel(stat, mc + MC_INT_STATUS);
+ return IRQ_HANDLED;
+}
+
+void __init tegra_mc_init(void)
+{
+ if (request_irq(INT_MC_GENERAL, tegra_mc_error_isr, 0,
+ "mc_status", NULL)) {
+ pr_err("%s: unable to register MC error interrupt\n", __func__);
+ } else {
+ void __iomem *mc = IO_ADDRESS(TEGRA_MC_BASE);
+ u32 reg = MC_INT_SECURITY_VIOLATION | MC_INT_INVALID_GART_PAGE |
+ MC_INT_DECERR_EMEM_OTHERS;
+ writel(reg, mc + MC_INT_MASK);
+ }
+}
+arch_initcall(tegra_mc_init);
diff --git a/arch/arm/mach-tegra/common-t3.c b/arch/arm/mach-tegra/common-t3.c
new file mode 100644
index 000000000000..d65e44779f14
--- /dev/null
+++ b/arch/arm/mach-tegra/common-t3.c
@@ -0,0 +1,268 @@
+/*
+ * arch/arm/mach-tegra/common-t3.c
+ *
+ * Tegra 3 SoC-specific initialization (memory controller, etc.)
+ *
+ * Copyright (c) 2010-2011, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+
+#include <mach/iomap.h>
+#include <mach/irqs.h>
+
+#include "tegra3_emc.h"
+
+#define MC_INT_STATUS 0x0
+#define MC_INT_MASK 0x4
+#define MC_INT_DECERR_EMEM (1<<6)
+#define MC_INT_SECURITY_VIOLATION (1<<8)
+#define MC_INT_ARBITRATION_EMEM (1<<9)
+#define MC_INT_INVALID_SMMU_PAGE (1<<10)
+
+#define MC_ERROR_STATUS 0x8
+#define MC_ERROR_ADDRESS 0xC
+
+#define MC_TIMING_REG_NUM1 \
+ ((MC_EMEM_ARB_TIMING_W2R - MC_EMEM_ARB_CFG) / 4 + 1)
+#define MC_TIMING_REG_NUM2 \
+ ((MC_EMEM_ARB_MISC1 - MC_EMEM_ARB_DA_TURNS) / 4 + 1)
+
+struct mc_client {
+ const char *name;
+};
+
+#define client(_name) \
+ { \
+ .name = _name, \
+ }
+
+
+static void __iomem *mc = IO_ADDRESS(TEGRA_MC_BASE);
+
+
+#ifdef CONFIG_PM_SLEEP
+static u32 mc_boot_timing[MC_TIMING_REG_NUM1 + MC_TIMING_REG_NUM2 + 4];
+
+static void tegra_mc_timing_save(void)
+{
+ u32 off;
+ u32 *ctx = mc_boot_timing;
+
+ for (off = MC_EMEM_ARB_CFG; off <= MC_EMEM_ARB_TIMING_W2R; off += 4)
+ *ctx++ = readl((u32)mc + off);
+
+ for (off = MC_EMEM_ARB_DA_TURNS; off <= MC_EMEM_ARB_MISC1; off += 4)
+ *ctx++ = readl((u32)mc + off);
+
+ *ctx++ = readl((u32)mc + MC_EMEM_ARB_RING3_THROTTLE);
+ *ctx++ = readl((u32)mc + MC_EMEM_ARB_OVERRIDE);
+ *ctx++ = readl((u32)mc + MC_RESERVED_RSV);
+
+ *ctx++ = readl((u32)mc + MC_INT_MASK);
+}
+
+void tegra_mc_timing_restore(void)
+{
+ u32 off;
+ u32 *ctx = mc_boot_timing;
+
+ for (off = MC_EMEM_ARB_CFG; off <= MC_EMEM_ARB_TIMING_W2R; off += 4)
+ __raw_writel(*ctx++, (u32)mc + off);
+
+ for (off = MC_EMEM_ARB_DA_TURNS; off <= MC_EMEM_ARB_MISC1; off += 4)
+ __raw_writel(*ctx++, (u32)mc + off);
+
+ __raw_writel(*ctx++, (u32)mc + MC_EMEM_ARB_RING3_THROTTLE);
+ __raw_writel(*ctx++, (u32)mc + MC_EMEM_ARB_OVERRIDE);
+ __raw_writel(*ctx++, (u32)mc + MC_RESERVED_RSV);
+
+ writel(*ctx++, (u32)mc + MC_INT_MASK);
+ off = readl((u32)mc + MC_INT_MASK);
+
+ writel(0x1, (u32)mc + MC_TIMING_CONTROL);
+ off = readl((u32)mc + MC_TIMING_CONTROL);
+}
+#else
+#define tegra_mc_timing_save()
+#endif
+
+
+static const struct mc_client mc_clients[] = {
+ client("ptc"),
+ client("display0_wina"), client("display1_wina"),
+ client("display0_winb"), client("display1_winb"),
+ client("display0_winc"), client("display1_winc"),
+ client("display0_winb_vfilter"),
+ client("display1_winb_vfilter"),
+ client("epp"), client("gr2d_pat"),
+ client("gr2d_src"), client("mpe_unified"),
+ client("vi_chroma_filter"), client("pcie"),
+ client("avp"),
+ client("display0_cursor"), client("display1_cursor"),
+ client("gr3d0_fdc"), client("gr3d1_fdc"),
+ client("gr2d_dst"), client("hda"),
+ client("host1x_dma"), client("host1x_generic"),
+ client("gr3d0_idx"), client("gr3d1_idx"),
+ client("mpe_intrapred"), client("mpe_mpea"),
+ client("mpe_mpec"), client("ahb_dma"),
+ client("ahb_slave"), client("sata"),
+ client("gr3d0_tex"), client("gr3d1_tex"),
+ client("vde_bsev"), client("vde_mbe"),
+ client("vde_mce"), client("vde_tpe"),
+ client("cpu_lp"), client("cpu"),
+ client("epp_u"), client("epp_v"),
+ client("epp_y"), client("mpe_unified"),
+ client("vi_sb"), client("vi_u"),
+ client("vi_v"), client("vi_y"),
+ client("gr2d_dst"), client("pcie"),
+ client("avp"), client("gr3d0_fdc"),
+ client("gr3d1_fdc"), client("hda"),
+ client("host1x"), client("isp"),
+ client("cpu_lp"), client("cpu"),
+ client("mpe_mpec"), client("ahb_dma"),
+ client("ahb_slave"), client("sata"),
+ client("vde_bsev"), client("vde_dbg"),
+ client("vde_mbe"), client("vde_tpm"),
+};
+
+static const char *smmu_page_attrib[] = {
+ "SMMU: nr-nw-s",
+ "SMMU: nr-nw-ns",
+ "SMMU: nr-wr-s",
+ "SMMU: nr-wr-ns",
+ "SMMU: rd-nw-s",
+ "SMMU: rd-nw-ns",
+ "SMMU: rd-wr-s",
+ "SMMU: rd-wr-ns"
+};
+
+static DEFINE_SPINLOCK(mc_lock);
+static unsigned long error_count = 0;
+#define MAX_PRINTS 5
+
+static void unthrottle_prints(struct work_struct *work)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&mc_lock, flags);
+ error_count = 0;
+ spin_unlock_irqrestore(&mc_lock, flags);
+}
+
+static DECLARE_DELAYED_WORK(unthrottle_prints_work, unthrottle_prints);
+
+static irqreturn_t tegra_mc_error_isr(int irq, void *data)
+{
+ const struct mc_client *client = NULL;
+ const char *mc_err;
+ const char *mc_err_info;
+ unsigned long count;
+ u32 stat;
+ u32 addr;
+ u32 err;
+ u32 type;
+ u32 is_write;
+ u32 is_secure;
+ u32 client_id;
+
+ stat = readl(mc + MC_INT_STATUS);
+ stat &= (MC_INT_DECERR_EMEM |
+ MC_INT_SECURITY_VIOLATION |
+ MC_INT_INVALID_SMMU_PAGE);
+
+ __cancel_delayed_work(&unthrottle_prints_work);
+
+ spin_lock(&mc_lock);
+ count = ++error_count;
+ spin_unlock(&mc_lock);
+
+ if (count >= MAX_PRINTS) {
+ if (count == MAX_PRINTS)
+ pr_err("Too many MC errors; throttling prints\n");
+ schedule_delayed_work(&unthrottle_prints_work, HZ/2);
+ goto out;
+ }
+
+ err = readl(mc + MC_ERROR_STATUS);
+ addr = readl(mc + MC_ERROR_ADDRESS);
+ is_write = err & (1<<16);
+ is_secure = err & (1<<17);
+ type = (err >> 28) & 7;
+ client_id = err & 0x7f;
+ if (client_id < ARRAY_SIZE(mc_clients))
+ client = &mc_clients[client_id];
+
+ if (stat & MC_INT_DECERR_EMEM)
+ mc_err = "MC_DECERR";
+ else if (stat & MC_INT_SECURITY_VIOLATION)
+ mc_err = "MC_SECURITY_ERR";
+ else if (stat & MC_INT_INVALID_SMMU_PAGE)
+ mc_err = "MC_SMMU_ERR";
+ else
+ mc_err = "unknown";
+
+ mc_err_info = "";
+ if (type == 3) {
+ mc_err_info = "SECURITY_TRUSTZONE";
+ } else if (type == 4) {
+ mc_err_info = "SECURITY_CARVEOUT";
+ } else if (type == 6) {
+ u32 attrib = (err >> 25) & 7;
+ mc_err_info = smmu_page_attrib[attrib];
+ }
+
+ pr_err("%s (0x%08X): %p %s (%s %s %s)\n", mc_err, err, (void*)addr,
+ (client) ? client->name : "unknown",
+ (is_secure)? "secure" : "non-secure",
+ (is_write) ? "write" : "read",
+ mc_err_info);
+
+out:
+ writel(stat, mc + MC_INT_STATUS);
+ return IRQ_HANDLED;
+}
+
+int __init tegra_mc_init(void)
+{
+ u32 reg;
+ int ret = 0;
+
+ reg = 0x0A7F1010;
+ writel(reg, mc + MC_RESERVED_RSV);
+
+ reg = readl(mc + MC_EMEM_ARB_OVERRIDE);
+ reg |= 3;
+ writel(reg, mc + MC_EMEM_ARB_OVERRIDE);
+
+ if (request_irq(INT_MC_GENERAL, tegra_mc_error_isr, 0,
+ "mc_status", NULL)) {
+ pr_err("%s: unable to register MC error interrupt\n", __func__);
+ ret = -ENXIO;
+ } else {
+ reg = MC_INT_DECERR_EMEM | MC_INT_SECURITY_VIOLATION |
+ MC_INT_INVALID_SMMU_PAGE;
+ writel(reg, mc + MC_INT_MASK);
+ }
+ tegra_mc_timing_save();
+
+ return ret;
+}
+arch_initcall(tegra_mc_init);
diff --git a/arch/arm/mach-tegra/common.c b/arch/arm/mach-tegra/common.c
index d5e3f89b05af..21ffbe44ed76 100644
--- a/arch/arm/mach-tegra/common.c
+++ b/arch/arm/mach-tegra/common.c
@@ -1,7 +1,8 @@
/*
- * arch/arm/mach-tegra/board-harmony.c
+ * arch/arm/mach-tegra/common.c
*
* Copyright (C) 2010 Google, Inc.
+ * Copyright (C) 2010-2011 NVIDIA Corporation
*
* Author:
* Colin Cross <ccross@android.com>
@@ -17,67 +18,974 @@
*
*/
+#include <linux/platform_device.h>
+#include <linux/console.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/clk.h>
#include <linux/delay.h>
+#include <linux/highmem.h>
+#include <linux/memblock.h>
+#include <linux/bitops.h>
+#include <linux/sched.h>
#include <asm/hardware/cache-l2x0.h>
+#include <asm/system.h>
+#include <mach/gpio.h>
#include <mach/iomap.h>
+#include <mach/pinmux.h>
+#include <mach/powergate.h>
#include <mach/system.h>
+#include "apbio.h"
#include "board.h"
#include "clock.h"
#include "fuse.h"
+#include "pm.h"
+#include "reset.h"
+#include "tegra_smmu.h"
+
+#define MC_SECURITY_CFG2 0x7c
+
+#define AHB_ARBITRATION_PRIORITY_CTRL 0x4
+#define AHB_PRIORITY_WEIGHT(x) (((x) & 0x7) << 29)
+#define PRIORITY_SELECT_USB BIT(6)
+#define PRIORITY_SELECT_USB2 BIT(18)
+#define PRIORITY_SELECT_USB3 BIT(17)
+
+#define AHB_GIZMO_AHB_MEM 0xc
+#define ENB_FAST_REARBITRATE BIT(2)
+#define DONT_SPLIT_AHB_WR BIT(7)
+
+#define AHB_GIZMO_USB 0x1c
+#define AHB_GIZMO_USB2 0x78
+#define AHB_GIZMO_USB3 0x7c
+#define IMMEDIATE BIT(18)
+
+#define AHB_MEM_PREFETCH_CFG3 0xe0
+#define AHB_MEM_PREFETCH_CFG4 0xe4
+#define AHB_MEM_PREFETCH_CFG1 0xec
+#define AHB_MEM_PREFETCH_CFG2 0xf0
+#define PREFETCH_ENB BIT(31)
+#define MST_ID(x) (((x) & 0x1f) << 26)
+#define AHBDMA_MST_ID MST_ID(5)
+#define USB_MST_ID MST_ID(6)
+#define USB2_MST_ID MST_ID(18)
+#define USB3_MST_ID MST_ID(17)
+#define ADDR_BNDRY(x) (((x) & 0xf) << 21)
+#define INACTIVITY_TIMEOUT(x) (((x) & 0xffff) << 0)
+
+unsigned long tegra_bootloader_fb_start;
+unsigned long tegra_bootloader_fb_size;
+unsigned long tegra_fb_start;
+unsigned long tegra_fb_size;
+unsigned long tegra_fb2_start;
+unsigned long tegra_fb2_size;
+unsigned long tegra_carveout_start;
+unsigned long tegra_carveout_size;
+unsigned long tegra_vpr_start;
+unsigned long tegra_vpr_size;
+unsigned long tegra_lp0_vec_start;
+unsigned long tegra_lp0_vec_size;
+bool tegra_lp0_vec_relocate;
+unsigned long tegra_grhost_aperture = ~0ul;
+static bool is_tegra_debug_uart_hsport;
+static struct board_info pmu_board_info;
+static struct board_info display_board_info;
+
+static int pmu_core_edp = 1200; /* default 1.2V EDP limit */
+static int board_panel_type;
+static enum power_supply_type pow_supply_type = POWER_SUPPLY_TYPE_MAINS;
void (*arch_reset)(char mode, const char *cmd) = tegra_assert_system_reset;
+#define NEVER_RESET 0
+
void tegra_assert_system_reset(char mode, const char *cmd)
{
- void __iomem *reset = IO_ADDRESS(TEGRA_CLK_RESET_BASE + 0x04);
+#if defined(CONFIG_TEGRA_FPGA_PLATFORM) || NEVER_RESET
+ printk("tegra_assert_system_reset() ignored.....");
+ do { } while (1);
+#else
+ void __iomem *reset = IO_ADDRESS(TEGRA_PMC_BASE + 0x00);
u32 reg;
/* use *_related to avoid spinlock since caches are off */
reg = readl_relaxed(reset);
- reg |= 0x04;
+ reg |= 0x10;
writel_relaxed(reg, reset);
+#endif
}
+static int modem_id;
+static int debug_uart_port_id;
+static enum audio_codec_type audio_codec_name;
+static int max_cpu_current;
+/* WARNING: There is implicit client of pllp_out3 like i2c, uart, dsi
+ * and so this clock (pllp_out3) should never be disabled.
+ */
static __initdata struct tegra_clk_init_table common_clk_init_table[] = {
/* name parent rate enabled */
{ "clk_m", NULL, 0, true },
- { "pll_p", "clk_m", 216000000, true },
+#ifdef CONFIG_TEGRA_SILICON_PLATFORM
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+ { "pll_p", NULL, 216000000, true },
{ "pll_p_out1", "pll_p", 28800000, true },
{ "pll_p_out2", "pll_p", 48000000, true },
{ "pll_p_out3", "pll_p", 72000000, true },
{ "pll_p_out4", "pll_p", 108000000, true },
+ { "pll_m", "clk_m", 0, true },
+ { "pll_m_out1", "pll_m", 120000000, true },
+ { "sclk", "pll_c_out1", 40000000, true },
+ { "hclk", "sclk", 40000000, true },
+ { "pclk", "hclk", 40000000, true },
+ { "mpe", "pll_c", 0, false },
+ { "epp", "pll_c", 0, false },
+ { "vi_sensor", "pll_c", 0, false },
+ { "vi", "pll_c", 0, false },
+ { "2d", "pll_c", 0, false },
+ { "3d", "pll_c", 0, false },
+#else
+ { "pll_p", NULL, 408000000, true },
+ { "pll_p_out1", "pll_p", 9600000, true },
+ { "pll_p_out2", "pll_p", 48000000, true },
+ { "pll_p_out3", "pll_p", 102000000, true },
+ { "pll_m_out1", "pll_m", 275000000, false },
+ { "pll_p_out4", "pll_p", 102000000, true },
+ { "sclk", "pll_p_out4", 102000000, true },
+ { "hclk", "sclk", 102000000, true },
+ { "pclk", "hclk", 51000000, true },
+#endif
+#else
+ { "pll_p", NULL, 216000000, true },
+ { "pll_p_out1", "pll_p", 28800000, true },
+ { "pll_p_out2", "pll_p", 48000000, true },
+ { "pll_p_out3", "pll_p", 72000000, true },
+ { "pll_m_out1", "pll_m", 275000000, true },
+ { "pll_c", NULL, ULONG_MAX, false },
+ { "pll_c_out1", "pll_c", 208000000, false },
+ { "pll_p_out4", "pll_p", 108000000, true },
{ "sclk", "pll_p_out4", 108000000, true },
{ "hclk", "sclk", 108000000, true },
{ "pclk", "hclk", 54000000, true },
+#endif
{ "csite", NULL, 0, true },
{ "emc", NULL, 0, true },
{ "cpu", NULL, 0, true },
+ { "kfuse", NULL, 0, true },
+ { "fuse", NULL, 0, true },
+ { "pll_u", NULL, 480000000, false },
+ { "sdmmc1", "pll_p", 48000000, false},
+ { "sdmmc3", "pll_p", 48000000, false},
+ { "sdmmc4", "pll_p", 48000000, false},
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+ { "cbus", "pll_c", 416000000, false },
+ { "pll_c_out1", "pll_c", 208000000, false },
+#endif
{ NULL, NULL, 0, 0},
};
-void __init tegra_init_cache(void)
+#if defined(CONFIG_TRUSTED_FOUNDATIONS) && defined(CONFIG_CACHE_L2X0)
+static void tegra_cache_smc(bool enable, u32 arg)
+{
+ void __iomem *p = IO_ADDRESS(TEGRA_ARM_PERIF_BASE) + 0x3000;
+ bool need_affinity_switch;
+ bool can_switch_affinity;
+ bool l2x0_enabled;
+ cpumask_t local_cpu_mask;
+ cpumask_t saved_cpu_mask;
+ unsigned long flags;
+ long ret;
+
+ /*
+ * ISSUE : Some registers of PL310 controler must be written
+ * from Secure context (and from CPU0)!
+ *
+ * When called form Normal we obtain an abort or do nothing.
+ * Instructions that must be called in Secure:
+ * - Write to Control register (L2X0_CTRL==0x100)
+ * - Write in Auxiliary controler (L2X0_AUX_CTRL==0x104)
+ * - Invalidate all entries (L2X0_INV_WAY==0x77C),
+ * mandatory at boot time.
+ * - Tag and Data RAM Latency Control Registers
+ * (0x108 & 0x10C) must be written in Secure.
+ */
+ need_affinity_switch = (smp_processor_id() != 0);
+ can_switch_affinity = !irqs_disabled();
+
+ WARN_ON(need_affinity_switch && !can_switch_affinity);
+ if (need_affinity_switch && can_switch_affinity) {
+ cpu_set(0, local_cpu_mask);
+ sched_getaffinity(0, &saved_cpu_mask);
+ ret = sched_setaffinity(0, &local_cpu_mask);
+ WARN_ON(ret != 0);
+ }
+
+ local_irq_save(flags);
+ l2x0_enabled = readl_relaxed(p + L2X0_CTRL) & 1;
+ if (enable && !l2x0_enabled)
+ tegra_generic_smc(0xFFFFF100, 0x00000001, arg);
+ else if (!enable && l2x0_enabled)
+ tegra_generic_smc(0xFFFFF100, 0x00000002, arg);
+ local_irq_restore(flags);
+
+ if (need_affinity_switch && can_switch_affinity) {
+ ret = sched_setaffinity(0, &saved_cpu_mask);
+ WARN_ON(ret != 0);
+ }
+}
+
+static void tegra_l2x0_disable(void)
+{
+ unsigned long flags;
+ static u32 l2x0_way_mask;
+
+ if (!l2x0_way_mask) {
+ void __iomem *p = IO_ADDRESS(TEGRA_ARM_PERIF_BASE) + 0x3000;
+ u32 aux_ctrl;
+ u32 ways;
+
+ aux_ctrl = readl_relaxed(p + L2X0_AUX_CTRL);
+ ways = (aux_ctrl & (1 << 16)) ? 16 : 8;
+ l2x0_way_mask = (1 << ways) - 1;
+ }
+
+ local_irq_save(flags);
+ tegra_cache_smc(false, l2x0_way_mask);
+ local_irq_restore(flags);
+}
+#endif /* CONFIG_TRUSTED_FOUNDATIONS && defined(CONFIG_CACHE_L2X0) */
+
+void tegra_init_cache(bool init)
{
#ifdef CONFIG_CACHE_L2X0
void __iomem *p = IO_ADDRESS(TEGRA_ARM_PERIF_BASE) + 0x3000;
+ u32 aux_ctrl;
+ u32 speedo;
+
+#ifdef CONFIG_TRUSTED_FOUNDATIONS
+ /* issue the SMC to enable the L2 */
+ aux_ctrl = readl_relaxed(p + L2X0_AUX_CTRL);
+ tegra_cache_smc(true, aux_ctrl);
+
+ /* after init, reread aux_ctrl and register handlers */
+ aux_ctrl = readl_relaxed(p + L2X0_AUX_CTRL);
+ l2x0_init(p, aux_ctrl, 0xFFFFFFFF);
+ /* override outer_disable() with our disable */
+ outer_cache.disable = tegra_l2x0_disable;
+#else
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
writel_relaxed(0x331, p + L2X0_TAG_LATENCY_CTRL);
writel_relaxed(0x441, p + L2X0_DATA_LATENCY_CTRL);
- l2x0_init(p, 0x6C080001, 0x8200c3fe);
+#elif defined(CONFIG_ARCH_TEGRA_3x_SOC)
+#ifdef CONFIG_TEGRA_SILICON_PLATFORM
+ /* PL310 RAM latency is CPU dependent. NOTE: Changes here
+ must also be reflected in __cortex_a9_l2x0_restart */
+
+ if (is_lp_cluster()) {
+ writel(0x221, p + L2X0_TAG_LATENCY_CTRL);
+ writel(0x221, p + L2X0_DATA_LATENCY_CTRL);
+ } else {
+ /* relax l2-cache latency for speedos 4,5,6 (T33's chips) */
+ speedo = tegra_cpu_speedo_id();
+ if (speedo == 4 || speedo == 5 || speedo == 6) {
+ writel(0x442, p + L2X0_TAG_LATENCY_CTRL);
+ writel(0x552, p + L2X0_DATA_LATENCY_CTRL);
+ } else {
+ writel(0x441, p + L2X0_TAG_LATENCY_CTRL);
+ writel(0x551, p + L2X0_DATA_LATENCY_CTRL);
+ }
+ }
+#else
+ writel(0x770, p + L2X0_TAG_LATENCY_CTRL);
+ writel(0x770, p + L2X0_DATA_LATENCY_CTRL);
+#endif
+#endif
+ if (init) {
+ aux_ctrl = readl(p + L2X0_CACHE_TYPE);
+ aux_ctrl = (aux_ctrl & 0x700) << (17-8);
+ aux_ctrl |= 0x7C000001;
+ l2x0_init(p, aux_ctrl, 0x8200c3fe);
+ }
+ l2x0_enable();
+#endif
#endif
+}
+
+static void __init tegra_init_power(void)
+{
+#ifdef CONFIG_ARCH_TEGRA_HAS_SATA
+ tegra_powergate_partition_with_clk_off(TEGRA_POWERGATE_SATA);
+#endif
+#ifdef CONFIG_ARCH_TEGRA_HAS_PCIE
+ tegra_powergate_partition_with_clk_off(TEGRA_POWERGATE_PCIE);
+#endif
+}
+
+static inline unsigned long gizmo_readl(unsigned long offset)
+{
+ return readl(IO_TO_VIRT(TEGRA_AHB_GIZMO_BASE + offset));
+}
+
+static inline void gizmo_writel(unsigned long value, unsigned long offset)
+{
+ writel(value, IO_TO_VIRT(TEGRA_AHB_GIZMO_BASE + offset));
+}
+
+static void __init tegra_init_ahb_gizmo_settings(void)
+{
+ unsigned long val;
+
+ val = gizmo_readl(AHB_GIZMO_AHB_MEM);
+ val |= ENB_FAST_REARBITRATE | IMMEDIATE | DONT_SPLIT_AHB_WR;
+ gizmo_writel(val, AHB_GIZMO_AHB_MEM);
+
+ val = gizmo_readl(AHB_GIZMO_USB);
+ val |= IMMEDIATE;
+ gizmo_writel(val, AHB_GIZMO_USB);
+ val = gizmo_readl(AHB_GIZMO_USB2);
+ val |= IMMEDIATE;
+ gizmo_writel(val, AHB_GIZMO_USB2);
+
+ val = gizmo_readl(AHB_GIZMO_USB3);
+ val |= IMMEDIATE;
+ gizmo_writel(val, AHB_GIZMO_USB3);
+
+ val = gizmo_readl(AHB_ARBITRATION_PRIORITY_CTRL);
+ val |= PRIORITY_SELECT_USB | PRIORITY_SELECT_USB2 | PRIORITY_SELECT_USB3
+ | AHB_PRIORITY_WEIGHT(7);
+ gizmo_writel(val, AHB_ARBITRATION_PRIORITY_CTRL);
+
+ val = gizmo_readl(AHB_MEM_PREFETCH_CFG1);
+ val &= ~MST_ID(~0);
+ val |= PREFETCH_ENB | AHBDMA_MST_ID | ADDR_BNDRY(0xc) | INACTIVITY_TIMEOUT(0x1000);
+ gizmo_writel(val, AHB_MEM_PREFETCH_CFG1);
+
+ val = gizmo_readl(AHB_MEM_PREFETCH_CFG2);
+ val &= ~MST_ID(~0);
+ val |= PREFETCH_ENB | USB_MST_ID | ADDR_BNDRY(0xc) | INACTIVITY_TIMEOUT(0x1000);
+ gizmo_writel(val, AHB_MEM_PREFETCH_CFG2);
+
+ val = gizmo_readl(AHB_MEM_PREFETCH_CFG3);
+ val &= ~MST_ID(~0);
+ val |= PREFETCH_ENB | USB3_MST_ID | ADDR_BNDRY(0xc) | INACTIVITY_TIMEOUT(0x1000);
+ gizmo_writel(val, AHB_MEM_PREFETCH_CFG3);
+
+ val = gizmo_readl(AHB_MEM_PREFETCH_CFG4);
+ val &= ~MST_ID(~0);
+ val |= PREFETCH_ENB | USB2_MST_ID | ADDR_BNDRY(0xc) | INACTIVITY_TIMEOUT(0x1000);
+ gizmo_writel(val, AHB_MEM_PREFETCH_CFG4);
+}
+
+static bool console_flushed;
+
+static void tegra_pm_flush_console(void)
+{
+ if (console_flushed)
+ return;
+ console_flushed = true;
+
+ pr_emerg("Restarting %s\n", linux_banner);
+ if (console_trylock()) {
+ console_unlock();
+ return;
+ }
+
+ mdelay(50);
+
+ local_irq_disable();
+ if (!console_trylock())
+ pr_emerg("%s: Console was locked! Busting\n", __func__);
+ else
+ pr_emerg("%s: Console was locked!\n", __func__);
+ console_unlock();
+}
+
+static void tegra_pm_restart(char mode, const char *cmd)
+{
+ tegra_pm_flush_console();
+ arm_machine_restart(mode, cmd);
}
void __init tegra_init_early(void)
{
+ arm_pm_restart = tegra_pm_restart;
+#ifndef CONFIG_SMP
+ /* For SMP system, initializing the reset handler here is too
+ late. For non-SMP systems, the function that calls the reset
+ handler initializer is not called, so do it here for non-SMP. */
+ tegra_cpu_reset_handler_init();
+#endif
tegra_init_fuse();
+ tegra_gpio_resume_init();
tegra_init_clock();
+ tegra_init_pinmux();
tegra_clk_init_from_table(common_clk_init_table);
- tegra_init_cache();
+ tegra_init_power();
+ tegra_init_cache(true);
+ tegra_init_ahb_gizmo_settings();
+}
+
+static int __init tegra_lp0_vec_arg(char *options)
+{
+ char *p = options;
+
+ tegra_lp0_vec_size = memparse(p, &p);
+ if (*p == '@')
+ tegra_lp0_vec_start = memparse(p+1, &p);
+ if (!tegra_lp0_vec_size || !tegra_lp0_vec_start) {
+ tegra_lp0_vec_size = 0;
+ tegra_lp0_vec_start = 0;
+ }
+
+ return 0;
+}
+early_param("lp0_vec", tegra_lp0_vec_arg);
+
+static int __init tegra_bootloader_fb_arg(char *options)
+{
+ char *p = options;
+
+ tegra_bootloader_fb_size = memparse(p, &p);
+ if (*p == '@')
+ tegra_bootloader_fb_start = memparse(p+1, &p);
+
+ pr_info("Found tegra_fbmem: %08lx@%08lx\n",
+ tegra_bootloader_fb_size, tegra_bootloader_fb_start);
+
+ return 0;
+}
+early_param("tegra_fbmem", tegra_bootloader_fb_arg);
+
+static int __init tegra_vpr_arg(char *options)
+{
+ char *p = options;
+
+ tegra_vpr_size = memparse(p, &p);
+ if (*p == '@')
+ tegra_vpr_start = memparse(p+1, &p);
+ pr_info("Found vpr, start=0x%lx size=%lx",
+ tegra_vpr_start, tegra_vpr_size);
+ return 0;
+}
+early_param("vpr", tegra_vpr_arg);
+
+enum panel_type get_panel_type(void)
+{
+ return board_panel_type;
+}
+static int __init tegra_board_panel_type(char *options)
+{
+ if (!strcmp(options, "lvds"))
+ board_panel_type = panel_type_lvds;
+ else if (!strcmp(options, "dsi"))
+ board_panel_type = panel_type_dsi;
+ else
+ return 0;
+ return 1;
+}
+__setup("panel=", tegra_board_panel_type);
+
+enum power_supply_type get_power_supply_type(void)
+{
+ return pow_supply_type;
+}
+static int __init tegra_board_power_supply_type(char *options)
+{
+ if (!strcmp(options, "Adapter"))
+ pow_supply_type = POWER_SUPPLY_TYPE_MAINS;
+ if (!strcmp(options, "Mains"))
+ pow_supply_type = POWER_SUPPLY_TYPE_MAINS;
+ else if (!strcmp(options, "Battery"))
+ pow_supply_type = POWER_SUPPLY_TYPE_BATTERY;
+ else
+ return 0;
+ return 1;
+}
+__setup("power_supply=", tegra_board_power_supply_type);
+
+int get_core_edp(void)
+{
+ return pmu_core_edp;
+}
+static int __init tegra_pmu_core_edp(char *options)
+{
+ char *p = options;
+ int core_edp = memparse(p, &p);
+ if (core_edp != 0)
+ pmu_core_edp = core_edp;
+ return 0;
+}
+early_param("core_edp_mv", tegra_pmu_core_edp);
+
+int get_maximum_cpu_current_supported(void)
+{
+ return max_cpu_current;
+}
+static int __init tegra_max_cpu_current(char *options)
+{
+ char *p = options;
+ max_cpu_current = memparse(p, &p);
+ return 1;
+}
+__setup("max_cpu_cur_ma=", tegra_max_cpu_current);
+
+static int __init tegra_debug_uartport(char *info)
+{
+ char *p = info;
+ unsigned long long port_id;
+ if (!strncmp(p, "hsport", 6))
+ is_tegra_debug_uart_hsport = true;
+ else if (!strncmp(p, "lsport", 6))
+ is_tegra_debug_uart_hsport = false;
+
+ if (p[6] == ',') {
+ if (p[7] == '-') {
+ debug_uart_port_id = -1;
+ } else {
+ port_id = memparse(p + 7, &p);
+ debug_uart_port_id = (int) port_id;
+ }
+ } else {
+ debug_uart_port_id = -1;
+ }
+
+ return 1;
+}
+
+bool is_tegra_debug_uartport_hs(void)
+{
+ return is_tegra_debug_uart_hsport;
+}
+
+int get_tegra_uart_debug_port_id(void)
+{
+ return debug_uart_port_id;
+}
+__setup("debug_uartport=", tegra_debug_uartport);
+
+static int __init tegra_audio_codec_type(char *info)
+{
+ char *p = info;
+ if (!strncmp(p, "wm8903", 6))
+ audio_codec_name = audio_codec_wm8903;
+ else
+ audio_codec_name = audio_codec_none;
+
+ return 1;
+}
+
+enum audio_codec_type get_audio_codec_type(void)
+{
+ return audio_codec_name;
+}
+__setup("audio_codec=", tegra_audio_codec_type);
+
+
+void tegra_get_board_info(struct board_info *bi)
+{
+ bi->board_id = (system_serial_high >> 16) & 0xFFFF;
+ bi->sku = (system_serial_high) & 0xFFFF;
+ bi->fab = (system_serial_low >> 24) & 0xFF;
+ bi->major_revision = (system_serial_low >> 16) & 0xFF;
+ bi->minor_revision = (system_serial_low >> 8) & 0xFF;
+}
+
+static int __init tegra_pmu_board_info(char *info)
+{
+ char *p = info;
+ pmu_board_info.board_id = memparse(p, &p);
+ pmu_board_info.sku = memparse(p+1, &p);
+ pmu_board_info.fab = memparse(p+1, &p);
+ pmu_board_info.major_revision = memparse(p+1, &p);
+ pmu_board_info.minor_revision = memparse(p+1, &p);
+ return 1;
+}
+
+void tegra_get_pmu_board_info(struct board_info *bi)
+{
+ memcpy(bi, &pmu_board_info, sizeof(struct board_info));
+}
+
+__setup("pmuboard=", tegra_pmu_board_info);
+
+static int __init tegra_display_board_info(char *info)
+{
+ char *p = info;
+ display_board_info.board_id = memparse(p, &p);
+ display_board_info.sku = memparse(p+1, &p);
+ display_board_info.fab = memparse(p+1, &p);
+ display_board_info.major_revision = memparse(p+1, &p);
+ display_board_info.minor_revision = memparse(p+1, &p);
+ return 1;
+}
+
+void tegra_get_display_board_info(struct board_info *bi)
+{
+ memcpy(bi, &display_board_info, sizeof(struct board_info));
+}
+
+__setup("displayboard=", tegra_display_board_info);
+
+static int __init tegra_modem_id(char *id)
+{
+ char *p = id;
+
+ modem_id = memparse(p, &p);
+ return 1;
+}
+
+int tegra_get_modem_id(void)
+{
+ return modem_id;
+}
+
+__setup("modem_id=", tegra_modem_id);
+
+/*
+ * Tegra has a protected aperture that prevents access by most non-CPU
+ * memory masters to addresses above the aperture value. Enabling it
+ * secures the CPU's memory from the GPU, except through the GART.
+ */
+void __init tegra_protected_aperture_init(unsigned long aperture)
+{
+#ifndef CONFIG_NVMAP_ALLOW_SYSMEM
+ void __iomem *mc_base = IO_ADDRESS(TEGRA_MC_BASE);
+ pr_info("Enabling Tegra protected aperture at 0x%08lx\n", aperture);
+ writel(aperture, mc_base + MC_SECURITY_CFG2);
+#else
+ pr_err("Tegra protected aperture disabled because nvmap is using "
+ "system memory\n");
+#endif
+}
+
+/*
+ * Due to conflicting restrictions on the placement of the framebuffer,
+ * the bootloader is likely to leave the framebuffer pointed at a location
+ * in memory that is outside the grhost aperture. This function will move
+ * the framebuffer contents from a physical address that is anywher (lowmem,
+ * highmem, or outside the memory map) to a physical address that is outside
+ * the memory map.
+ */
+void tegra_move_framebuffer(unsigned long to, unsigned long from,
+ unsigned long size)
+{
+ struct page *page;
+ void __iomem *to_io;
+ void *from_virt;
+ unsigned long i;
+
+ BUG_ON(PAGE_ALIGN((unsigned long)to) != (unsigned long)to);
+ BUG_ON(PAGE_ALIGN(from) != from);
+ BUG_ON(PAGE_ALIGN(size) != size);
+
+ to_io = ioremap(to, size);
+ if (!to_io) {
+ pr_err("%s: Failed to map target framebuffer\n", __func__);
+ return;
+ }
+
+ if (pfn_valid(page_to_pfn(phys_to_page(from)))) {
+ for (i = 0 ; i < size; i += PAGE_SIZE) {
+ page = phys_to_page(from + i);
+ from_virt = kmap(page);
+ memcpy(to_io + i, from_virt, PAGE_SIZE);
+ kunmap(page);
+ }
+ } else {
+ void __iomem *from_io = ioremap(from, size);
+ if (!from_io) {
+ pr_err("%s: Failed to map source framebuffer\n",
+ __func__);
+ goto out;
+ }
+
+ for (i = 0; i < size; i += 4)
+ writel(readl(from_io + i), to_io + i);
+
+ iounmap(from_io);
+ }
+out:
+ iounmap(to_io);
+}
+
+#ifdef CONFIG_TEGRA_IOVMM_SMMU
+/* Support for Tegra3 A01 chip mask that needs to have SMMU IOVA reside in
+ * the upper half of 4GB IOVA space. A02 and after use the bottom 1GB and
+ * do not need to reserve memory.
+ */
+#define SUPPORT_TEGRA_3_IOVMM_SMMU_A01
+#endif
+
+void __init tegra_reserve(unsigned long carveout_size, unsigned long fb_size,
+ unsigned long fb2_size)
+{
+#ifdef SUPPORT_TEGRA_3_IOVMM_SMMU_A01
+ int smmu_reserved = 0;
+ struct tegra_smmu_window *smmu_window = tegra_smmu_window(0);
+#endif
+
+ if (carveout_size) {
+ tegra_carveout_start = memblock_end_of_DRAM() - carveout_size;
+ if (memblock_remove(tegra_carveout_start, carveout_size)) {
+ pr_err("Failed to remove carveout %08lx@%08lx "
+ "from memory map\n",
+ carveout_size, tegra_carveout_start);
+ tegra_carveout_start = 0;
+ tegra_carveout_size = 0;
+ } else
+ tegra_carveout_size = carveout_size;
+ }
+
+ if (fb2_size) {
+ tegra_fb2_start = memblock_end_of_DRAM() - fb2_size;
+ if (memblock_remove(tegra_fb2_start, fb2_size)) {
+ pr_err("Failed to remove second framebuffer "
+ "%08lx@%08lx from memory map\n",
+ fb2_size, tegra_fb2_start);
+ tegra_fb2_start = 0;
+ tegra_fb2_size = 0;
+ } else
+ tegra_fb2_size = fb2_size;
+ }
+
+ if (fb_size) {
+ tegra_fb_start = memblock_end_of_DRAM() - fb_size;
+ if (memblock_remove(tegra_fb_start, fb_size)) {
+ pr_err("Failed to remove framebuffer %08lx@%08lx "
+ "from memory map\n",
+ fb_size, tegra_fb_start);
+ tegra_fb_start = 0;
+ tegra_fb_size = 0;
+ } else
+ tegra_fb_size = fb_size;
+ }
+
+ if (tegra_fb_size)
+ tegra_grhost_aperture = tegra_fb_start;
+
+ if (tegra_fb2_size && tegra_fb2_start < tegra_grhost_aperture)
+ tegra_grhost_aperture = tegra_fb2_start;
+
+ if (tegra_carveout_size && tegra_carveout_start < tegra_grhost_aperture)
+ tegra_grhost_aperture = tegra_carveout_start;
+
+#ifdef SUPPORT_TEGRA_3_IOVMM_SMMU_A01
+ if (!smmu_window) {
+ pr_err("No SMMU resource\n");
+ } else {
+ size_t smmu_window_size;
+
+ if (tegra_get_revision() == TEGRA_REVISION_A01) {
+ smmu_window->start = TEGRA_SMMU_BASE_A01;
+ smmu_window->end = TEGRA_SMMU_BASE_A01 +
+ TEGRA_SMMU_SIZE_A01 - 1;
+ }
+ smmu_window_size = smmu_window->end + 1 - smmu_window->start;
+ if (smmu_window->start >= 0x80000000) {
+ if (memblock_reserve(smmu_window->start,
+ smmu_window_size))
+ pr_err(
+ "Failed to reserve SMMU I/O VA window %08lx@%08lx\n",
+ (unsigned long)smmu_window_size,
+ (unsigned long)smmu_window->start);
+ else
+ smmu_reserved = 1;
+ }
+ }
+#endif
+
+ if (tegra_lp0_vec_size &&
+ (tegra_lp0_vec_start < memblock_end_of_DRAM())) {
+ if (memblock_reserve(tegra_lp0_vec_start, tegra_lp0_vec_size)) {
+ pr_err("Failed to reserve lp0_vec %08lx@%08lx\n",
+ tegra_lp0_vec_size, tegra_lp0_vec_start);
+ tegra_lp0_vec_start = 0;
+ tegra_lp0_vec_size = 0;
+ }
+ tegra_lp0_vec_relocate = false;
+ } else
+ tegra_lp0_vec_relocate = true;
+
+ /*
+ * We copy the bootloader's framebuffer to the framebuffer allocated
+ * above, and then free this one.
+ * */
+ if (tegra_bootloader_fb_size) {
+ tegra_bootloader_fb_size = PAGE_ALIGN(tegra_bootloader_fb_size);
+ if (memblock_reserve(tegra_bootloader_fb_start,
+ tegra_bootloader_fb_size)) {
+ pr_err("Failed to reserve bootloader frame buffer "
+ "%08lx@%08lx\n", tegra_bootloader_fb_size,
+ tegra_bootloader_fb_start);
+ tegra_bootloader_fb_start = 0;
+ tegra_bootloader_fb_size = 0;
+ }
+ }
+
+ pr_info("Tegra reserved memory:\n"
+ "LP0: %08lx - %08lx\n"
+ "Bootloader framebuffer: %08lx - %08lx\n"
+ "Framebuffer: %08lx - %08lx\n"
+ "2nd Framebuffer: %08lx - %08lx\n"
+ "Carveout: %08lx - %08lx\n"
+ "Vpr: %08lx - %08lx\n",
+ tegra_lp0_vec_start,
+ tegra_lp0_vec_size ?
+ tegra_lp0_vec_start + tegra_lp0_vec_size - 1 : 0,
+ tegra_bootloader_fb_start,
+ tegra_bootloader_fb_size ?
+ tegra_bootloader_fb_start + tegra_bootloader_fb_size - 1 : 0,
+ tegra_fb_start,
+ tegra_fb_size ?
+ tegra_fb_start + tegra_fb_size - 1 : 0,
+ tegra_fb2_start,
+ tegra_fb2_size ?
+ tegra_fb2_start + tegra_fb2_size - 1 : 0,
+ tegra_carveout_start,
+ tegra_carveout_size ?
+ tegra_carveout_start + tegra_carveout_size - 1 : 0,
+ tegra_vpr_start,
+ tegra_vpr_size ?
+ tegra_vpr_start + tegra_vpr_size - 1 : 0);
+
+#ifdef SUPPORT_TEGRA_3_IOVMM_SMMU_A01
+ if (smmu_reserved)
+ pr_info("SMMU: %08lx - %08lx\n",
+ smmu_window->start, smmu_window->end);
+#endif
+}
+
+void __init tegra_release_bootloader_fb(void)
+{
+ /* Since bootloader fb is reserved in common.c, it is freed here. */
+ if (tegra_bootloader_fb_size)
+ if (memblock_free(tegra_bootloader_fb_start,
+ tegra_bootloader_fb_size))
+ pr_err("Failed to free bootloader fb.\n");
+}
+
+#ifdef CONFIG_TEGRA_CONVSERVATIVE_GOV_ON_EARLYSUPSEND
+static char cpufreq_gov_default[32];
+static char *cpufreq_gov_conservative = "conservative";
+static char *cpufreq_sysfs_place_holder="/sys/devices/system/cpu/cpu%i/cpufreq/scaling_governor";
+static char *cpufreq_gov_conservative_param="/sys/devices/system/cpu/cpufreq/conservative/%s";
+
+static void cpufreq_set_governor(char *governor)
+{
+ struct file *scaling_gov = NULL;
+ char buf[128];
+ int i;
+ loff_t offset = 0;
+
+ if (governor == NULL)
+ return;
+
+ for_each_online_cpu(i) {
+ sprintf(buf, cpufreq_sysfs_place_holder, i);
+ scaling_gov = filp_open(buf, O_RDWR, 0);
+ if (scaling_gov != NULL) {
+ if (scaling_gov->f_op != NULL &&
+ scaling_gov->f_op->write != NULL)
+ scaling_gov->f_op->write(scaling_gov,
+ governor,
+ strlen(governor),
+ &offset);
+ else
+ pr_err("f_op might be null\n");
+
+ filp_close(scaling_gov, NULL);
+ } else {
+ pr_err("%s. Can't open %s\n", __func__, buf);
+ }
+ }
+}
+
+void cpufreq_save_default_governor(void)
+{
+ struct file *scaling_gov = NULL;
+ char buf[128];
+ loff_t offset = 0;
+
+ buf[127] = 0;
+ sprintf(buf, cpufreq_sysfs_place_holder,0);
+ scaling_gov = filp_open(buf, O_RDONLY, 0);
+ if (scaling_gov != NULL) {
+ if (scaling_gov->f_op != NULL &&
+ scaling_gov->f_op->read != NULL)
+ scaling_gov->f_op->read(scaling_gov,
+ cpufreq_gov_default,
+ 32,
+ &offset);
+ else
+ pr_err("f_op might be null\n");
+
+ filp_close(scaling_gov, NULL);
+ } else {
+ pr_err("%s. Can't open %s\n", __func__, buf);
+ }
+}
+
+void cpufreq_restore_default_governor(void)
+{
+ cpufreq_set_governor(cpufreq_gov_default);
+}
+
+void cpufreq_set_conservative_governor_param(int up_th, int down_th)
+{
+ struct file *gov_param = NULL;
+ static char buf[128],parm[8];
+ loff_t offset = 0;
+
+ if (up_th <= down_th) {
+ printk(KERN_ERR "%s: up_th(%d) is lesser than down_th(%d)\n",
+ __func__, up_th, down_th);
+ return;
+ }
+
+ sprintf(parm, "%d", up_th);
+ sprintf(buf, cpufreq_gov_conservative_param ,"up_threshold");
+ gov_param = filp_open(buf, O_RDONLY, 0);
+ if (gov_param != NULL) {
+ if (gov_param->f_op != NULL &&
+ gov_param->f_op->write != NULL)
+ gov_param->f_op->write(gov_param,
+ parm,
+ strlen(parm),
+ &offset);
+ else
+ pr_err("f_op might be null\n");
+
+ filp_close(gov_param, NULL);
+ } else {
+ pr_err("%s. Can't open %s\n", __func__, buf);
+ }
+
+ sprintf(parm, "%d", down_th);
+ sprintf(buf, cpufreq_gov_conservative_param ,"down_threshold");
+ gov_param = filp_open(buf, O_RDONLY, 0);
+ if (gov_param != NULL) {
+ if (gov_param->f_op != NULL &&
+ gov_param->f_op->write != NULL)
+ gov_param->f_op->write(gov_param,
+ parm,
+ strlen(parm),
+ &offset);
+ else
+ pr_err("f_op might be null\n");
+
+ filp_close(gov_param, NULL);
+ } else {
+ pr_err("%s. Can't open %s\n", __func__, buf);
+ }
+}
+
+void cpufreq_set_conservative_governor(void)
+{
+ cpufreq_set_governor(cpufreq_gov_conservative);
}
+#endif /* CONFIG_TEGRA_CONVSERVATIVE_GOV_ON_EARLYSUPSEND */
diff --git a/arch/arm/mach-tegra/cpu-tegra.c b/arch/arm/mach-tegra/cpu-tegra.c
index 0e0fd4d889bd..5599c298ac5b 100644
--- a/arch/arm/mach-tegra/cpu-tegra.c
+++ b/arch/arm/mach-tegra/cpu-tegra.c
@@ -7,6 +7,8 @@
* Colin Cross <ccross@google.com>
* Based on arch/arm/plat-omap/cpu-omap.c, (C) 2005 Nokia Corporation
*
+ * Copyright (C) 2010-2011 NVIDIA Corporation
+ *
* 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.
@@ -29,32 +31,408 @@
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/suspend.h>
+#include <linux/debugfs.h>
+#include <linux/cpu.h>
#include <asm/system.h>
#include <mach/clk.h>
+#include <mach/edp.h>
-/* Frequency table index must be sequential starting at 0 */
-static struct cpufreq_frequency_table freq_table[] = {
- { 0, 216000 },
- { 1, 312000 },
- { 2, 456000 },
- { 3, 608000 },
- { 4, 760000 },
- { 5, 816000 },
- { 6, 912000 },
- { 7, 1000000 },
- { 8, CPUFREQ_TABLE_END },
-};
+#include "clock.h"
+#include "cpu-tegra.h"
-#define NUM_CPUS 2
+/* tegra throttling and edp governors require frequencies in the table
+ to be in ascending order */
+static struct cpufreq_frequency_table *freq_table;
static struct clk *cpu_clk;
static struct clk *emc_clk;
-static unsigned long target_cpu_speed[NUM_CPUS];
+static unsigned long policy_max_speed[CONFIG_NR_CPUS];
+static unsigned long target_cpu_speed[CONFIG_NR_CPUS];
static DEFINE_MUTEX(tegra_cpu_lock);
static bool is_suspended;
+static int suspend_index;
+
+static bool force_policy_max;
+
+static int force_policy_max_set(const char *arg, const struct kernel_param *kp)
+{
+ int ret;
+ bool old_policy = force_policy_max;
+
+ mutex_lock(&tegra_cpu_lock);
+
+ ret = param_set_bool(arg, kp);
+ if ((ret == 0) && (old_policy != force_policy_max))
+ tegra_cpu_set_speed_cap(NULL);
+
+ mutex_unlock(&tegra_cpu_lock);
+ return ret;
+}
+
+static int force_policy_max_get(char *buffer, const struct kernel_param *kp)
+{
+ return param_get_bool(buffer, kp);
+}
+
+static struct kernel_param_ops policy_ops = {
+ .set = force_policy_max_set,
+ .get = force_policy_max_get,
+};
+module_param_cb(force_policy_max, &policy_ops, &force_policy_max, 0644);
+
+
+static unsigned int cpu_user_cap;
+
+static int cpu_user_cap_set(const char *arg, const struct kernel_param *kp)
+{
+ int ret;
+
+ mutex_lock(&tegra_cpu_lock);
+
+ ret = param_set_uint(arg, kp);
+ if (ret == 0) {
+#ifndef CONFIG_TEGRA_CPU_CAP_EXACT_FREQ
+ if (cpu_user_cap != 0) {
+ int i;
+ for (i = 0; freq_table[i].frequency !=
+ CPUFREQ_TABLE_END; i++) {
+ if (freq_table[i].frequency > cpu_user_cap)
+ break;
+ }
+ i = (i == 0) ? 0 : i - 1;
+ cpu_user_cap = freq_table[i].frequency;
+ }
+#endif
+ tegra_cpu_set_speed_cap(NULL);
+ }
+
+ mutex_unlock(&tegra_cpu_lock);
+ return ret;
+}
+
+static int cpu_user_cap_get(char *buffer, const struct kernel_param *kp)
+{
+ return param_get_uint(buffer, kp);
+}
+
+static struct kernel_param_ops cap_ops = {
+ .set = cpu_user_cap_set,
+ .get = cpu_user_cap_get,
+};
+module_param_cb(cpu_user_cap, &cap_ops, &cpu_user_cap, 0644);
+
+static unsigned int user_cap_speed(unsigned int requested_speed)
+{
+ if ((cpu_user_cap) && (requested_speed > cpu_user_cap))
+ return cpu_user_cap;
+ return requested_speed;
+}
+
+#ifdef CONFIG_TEGRA_THERMAL_THROTTLE
+
+static ssize_t show_throttle(struct cpufreq_policy *policy, char *buf)
+{
+ return sprintf(buf, "%u\n", tegra_is_throttling());
+}
+
+cpufreq_freq_attr_ro(throttle);
+#endif /* CONFIG_TEGRA_THERMAL_THROTTLE */
+
+#ifdef CONFIG_TEGRA_EDP_LIMITS
+
+static const struct tegra_edp_limits *cpu_edp_limits;
+static int cpu_edp_limits_size;
+
+static const unsigned int *system_edp_limits;
+static bool system_edp_alarm;
+
+static int edp_thermal_index;
+static cpumask_t edp_cpumask;
+static unsigned int edp_limit;
+
+unsigned int tegra_get_edp_limit(void)
+{
+ return edp_limit;
+}
+
+static unsigned int edp_predict_limit(unsigned int cpus)
+{
+ unsigned int limit = 0;
+
+ BUG_ON(cpus == 0);
+ if (cpu_edp_limits) {
+ BUG_ON(edp_thermal_index >= cpu_edp_limits_size);
+ limit = cpu_edp_limits[edp_thermal_index].freq_limits[cpus - 1];
+ }
+ if (system_edp_limits && system_edp_alarm)
+ limit = min(limit, system_edp_limits[cpus - 1]);
+
+ return limit;
+}
+
+static void edp_update_limit(void)
+{
+ unsigned int limit = edp_predict_limit(cpumask_weight(&edp_cpumask));
+
+#ifdef CONFIG_TEGRA_EDP_EXACT_FREQ
+ edp_limit = limit;
+#else
+ unsigned int i;
+ for (i = 0; freq_table[i].frequency != CPUFREQ_TABLE_END; i++) {
+ if (freq_table[i].frequency > limit) {
+ break;
+ }
+ }
+ BUG_ON(i == 0); /* min freq above the limit or table empty */
+ edp_limit = freq_table[i-1].frequency;
+#endif
+}
+
+static unsigned int edp_governor_speed(unsigned int requested_speed)
+{
+ if ((!edp_limit) || (requested_speed <= edp_limit))
+ return requested_speed;
+ else
+ return edp_limit;
+}
+
+int tegra_edp_update_thermal_zone(int temperature)
+{
+ int i;
+ int ret = 0;
+ int nlimits = cpu_edp_limits_size;
+ int index;
+
+ if (!cpu_edp_limits)
+ return -EINVAL;
+
+ index = nlimits - 1;
+
+ if (temperature < cpu_edp_limits[0].temperature) {
+ index = 0;
+ } else {
+ for (i = 0; i < (nlimits - 1); i++) {
+ if (temperature >= cpu_edp_limits[i].temperature &&
+ temperature < cpu_edp_limits[i + 1].temperature) {
+ index = i + 1;
+ break;
+ }
+ }
+ }
+
+ mutex_lock(&tegra_cpu_lock);
+ edp_thermal_index = index;
+
+ /* Update cpu rate if cpufreq (at least on cpu0) is already started */
+ if (target_cpu_speed[0]) {
+ edp_update_limit();
+ tegra_cpu_set_speed_cap(NULL);
+ }
+ mutex_unlock(&tegra_cpu_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(tegra_edp_update_thermal_zone);
+
+int tegra_system_edp_alarm(bool alarm)
+{
+ int ret = -ENODEV;
+
+ mutex_lock(&tegra_cpu_lock);
+ system_edp_alarm = alarm;
+
+ /* Update cpu rate if cpufreq (at least on cpu0) is already started
+ and cancel emergency throttling after edp limit is applied */
+ if (target_cpu_speed[0]) {
+ edp_update_limit();
+ ret = tegra_cpu_set_speed_cap(NULL);
+ if (!ret && alarm)
+ tegra_edp_throttle_cpu_now(0);
+ }
+ mutex_unlock(&tegra_cpu_lock);
+
+ return ret;
+}
+
+bool tegra_cpu_edp_favor_up(unsigned int n, int mp_overhead)
+{
+ unsigned int current_limit, next_limit;
+
+ if (n == 0)
+ return true;
+
+ if (n >= ARRAY_SIZE(cpu_edp_limits->freq_limits))
+ return false;
+
+ current_limit = edp_predict_limit(n);
+ next_limit = edp_predict_limit(n + 1);
+
+ return ((next_limit * (n + 1)) >=
+ (current_limit * n * (100 + mp_overhead) / 100));
+}
+
+bool tegra_cpu_edp_favor_down(unsigned int n, int mp_overhead)
+{
+ unsigned int current_limit, next_limit;
+
+ if (n <= 1)
+ return false;
+
+ if (n > ARRAY_SIZE(cpu_edp_limits->freq_limits))
+ return true;
+
+ current_limit = edp_predict_limit(n);
+ next_limit = edp_predict_limit(n - 1);
+
+ return ((next_limit * (n - 1) * (100 + mp_overhead) / 100)) >
+ (current_limit * n);
+}
+
+static int tegra_cpu_edp_notify(
+ struct notifier_block *nb, unsigned long event, void *hcpu)
+{
+ int ret = 0;
+ unsigned int cpu_speed, new_speed;
+ int cpu = (long)hcpu;
+
+ switch (event) {
+ case CPU_UP_PREPARE:
+ mutex_lock(&tegra_cpu_lock);
+ cpu_set(cpu, edp_cpumask);
+ edp_update_limit();
+
+ cpu_speed = tegra_getspeed(0);
+ new_speed = edp_governor_speed(cpu_speed);
+ if (new_speed < cpu_speed) {
+ ret = tegra_cpu_set_speed_cap(NULL);
+ if (ret) {
+ cpu_clear(cpu, edp_cpumask);
+ edp_update_limit();
+ }
+
+ printk(KERN_DEBUG "tegra CPU:%sforce EDP limit %u kHz"
+ "\n", ret ? " failed to " : " ", new_speed);
+ }
+ mutex_unlock(&tegra_cpu_lock);
+ break;
+ case CPU_DEAD:
+ mutex_lock(&tegra_cpu_lock);
+ cpu_clear(cpu, edp_cpumask);
+ edp_update_limit();
+ tegra_cpu_set_speed_cap(NULL);
+ mutex_unlock(&tegra_cpu_lock);
+ break;
+ }
+ return notifier_from_errno(ret);
+}
+
+static struct notifier_block tegra_cpu_edp_notifier = {
+ .notifier_call = tegra_cpu_edp_notify,
+};
+
+static void tegra_cpu_edp_init(bool resume)
+{
+ tegra_get_system_edp_limits(&system_edp_limits);
+ tegra_get_cpu_edp_limits(&cpu_edp_limits, &cpu_edp_limits_size);
+
+ if (!(cpu_edp_limits || system_edp_limits)) {
+ if (!resume)
+ pr_info("cpu-tegra: no EDP table is provided\n");
+ return;
+ }
+
+ /* FIXME: use the highest temperature limits if sensor is not on-line?
+ * If thermal zone is not set yet by the sensor, edp_thermal_index = 0.
+ * Boot frequency allowed SoC to get here, should work till sensor is
+ * initialized.
+ */
+ edp_cpumask = *cpu_online_mask;
+ edp_update_limit();
+
+ if (!resume) {
+ register_hotcpu_notifier(&tegra_cpu_edp_notifier);
+ pr_info("cpu-tegra: init EDP limit: %u MHz\n", edp_limit/1000);
+ }
+}
+
+static void tegra_cpu_edp_exit(void)
+{
+ if (!(cpu_edp_limits || system_edp_limits))
+ return;
+
+ unregister_hotcpu_notifier(&tegra_cpu_edp_notifier);
+}
+
+#ifdef CONFIG_DEBUG_FS
+
+static int system_edp_alarm_get(void *data, u64 *val)
+{
+ *val = (u64)system_edp_alarm;
+ return 0;
+}
+static int system_edp_alarm_set(void *data, u64 val)
+{
+ if (val > 1) { /* emulate emergency throttling */
+ tegra_edp_throttle_cpu_now(val);
+ return 0;
+ }
+ return tegra_system_edp_alarm((bool)val);
+}
+DEFINE_SIMPLE_ATTRIBUTE(system_edp_alarm_fops,
+ system_edp_alarm_get, system_edp_alarm_set, "%llu\n");
+
+static int __init tegra_edp_debug_init(struct dentry *cpu_tegra_debugfs_root)
+{
+ if (!debugfs_create_file("edp_alarm", 0644, cpu_tegra_debugfs_root,
+ NULL, &system_edp_alarm_fops))
+ return -ENOMEM;
+
+ return 0;
+}
+#endif
+
+#else /* CONFIG_TEGRA_EDP_LIMITS */
+#define edp_governor_speed(requested_speed) (requested_speed)
+#define tegra_cpu_edp_init(resume)
+#define tegra_cpu_edp_exit()
+#define tegra_edp_debug_init(cpu_tegra_debugfs_root) (0)
+#endif /* CONFIG_TEGRA_EDP_LIMITS */
+
+#ifdef CONFIG_DEBUG_FS
+
+static struct dentry *cpu_tegra_debugfs_root;
+
+static int __init tegra_cpu_debug_init(void)
+{
+ cpu_tegra_debugfs_root = debugfs_create_dir("cpu-tegra", 0);
+
+ if (!cpu_tegra_debugfs_root)
+ return -ENOMEM;
+
+ if (tegra_throttle_debug_init(cpu_tegra_debugfs_root))
+ goto err_out;
+
+ if (tegra_edp_debug_init(cpu_tegra_debugfs_root))
+ goto err_out;
+
+ return 0;
+
+err_out:
+ debugfs_remove_recursive(cpu_tegra_debugfs_root);
+ return -ENOMEM;
+}
+
+static void __exit tegra_cpu_debug_exit(void)
+{
+ debugfs_remove_recursive(cpu_tegra_debugfs_root);
+}
+
+late_initcall(tegra_cpu_debug_init);
+module_exit(tegra_cpu_debug_exit);
+#endif /* CONFIG_DEBUG_FS */
int tegra_verify_speed(struct cpufreq_policy *policy)
{
@@ -65,7 +443,7 @@ unsigned int tegra_getspeed(unsigned int cpu)
{
unsigned long rate;
- if (cpu >= NUM_CPUS)
+ if (cpu >= CONFIG_NR_CPUS)
return 0;
rate = clk_get_rate(cpu_clk) / 1000;
@@ -80,6 +458,10 @@ static int tegra_update_cpu_speed(unsigned long rate)
freqs.old = tegra_getspeed(0);
freqs.new = rate;
+ rate = clk_round_rate(cpu_clk, rate * 1000);
+ if (!IS_ERR_VALUE(rate))
+ freqs.new = rate / 1000;
+
if (freqs.old == freqs.new)
return ret;
@@ -87,12 +469,8 @@ static int tegra_update_cpu_speed(unsigned long rate)
* Vote on memory bus frequency based on cpu frequency
* This sets the minimum frequency, display or avp may request higher
*/
- if (rate >= 816000)
- clk_set_rate(emc_clk, 600000000); /* cpu 816 MHz, emc max */
- else if (rate >= 456000)
- clk_set_rate(emc_clk, 300000000); /* cpu 456 MHz, emc 150Mhz */
- else
- clk_set_rate(emc_clk, 100000000); /* emc 50Mhz */
+ if (freqs.old < freqs.new)
+ clk_set_rate(emc_clk, tegra_emc_to_cpu_ratio(freqs.new));
for_each_online_cpu(freqs.cpu)
cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
@@ -112,48 +490,104 @@ static int tegra_update_cpu_speed(unsigned long rate)
for_each_online_cpu(freqs.cpu)
cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+ if (freqs.old > freqs.new)
+ clk_set_rate(emc_clk, tegra_emc_to_cpu_ratio(freqs.new));
+
return 0;
}
-static unsigned long tegra_cpu_highest_speed(void)
+unsigned int tegra_count_slow_cpus(unsigned long speed_limit)
{
- unsigned long rate = 0;
+ unsigned int cnt = 0;
int i;
for_each_online_cpu(i)
+ if (target_cpu_speed[i] <= speed_limit)
+ cnt++;
+ return cnt;
+}
+
+unsigned int tegra_get_slowest_cpu_n(void) {
+ unsigned int cpu = nr_cpu_ids;
+ unsigned long rate = ULONG_MAX;
+ int i;
+
+ for_each_online_cpu(i)
+ if ((i > 0) && (rate > target_cpu_speed[i])) {
+ cpu = i;
+ rate = target_cpu_speed[i];
+ }
+ return cpu;
+}
+
+unsigned long tegra_cpu_lowest_speed(void) {
+ unsigned long rate = ULONG_MAX;
+ int i;
+
+ for_each_online_cpu(i)
+ rate = min(rate, target_cpu_speed[i]);
+ return rate;
+}
+
+unsigned long tegra_cpu_highest_speed(void) {
+ unsigned long policy_max = ULONG_MAX;
+ unsigned long rate = 0;
+ int i;
+
+ for_each_online_cpu(i) {
+ if (force_policy_max)
+ policy_max = min(policy_max, policy_max_speed[i]);
rate = max(rate, target_cpu_speed[i]);
+ }
+ rate = min(rate, policy_max);
return rate;
}
+int tegra_cpu_set_speed_cap(unsigned int *speed_cap)
+{
+ int ret = 0;
+ unsigned int new_speed = tegra_cpu_highest_speed();
+
+ if (is_suspended)
+ return -EBUSY;
+
+ new_speed = tegra_throttle_governor_speed(new_speed);
+ new_speed = edp_governor_speed(new_speed);
+ new_speed = user_cap_speed(new_speed);
+ if (speed_cap)
+ *speed_cap = new_speed;
+
+ ret = tegra_update_cpu_speed(new_speed);
+ if (ret == 0)
+ tegra_auto_hotplug_governor(new_speed, false);
+ return ret;
+}
+
static int tegra_target(struct cpufreq_policy *policy,
unsigned int target_freq,
unsigned int relation)
{
int idx;
unsigned int freq;
+ unsigned int new_speed;
int ret = 0;
mutex_lock(&tegra_cpu_lock);
- if (is_suspended) {
- ret = -EBUSY;
- goto out;
- }
-
cpufreq_frequency_table_target(policy, freq_table, target_freq,
relation, &idx);
freq = freq_table[idx].frequency;
target_cpu_speed[policy->cpu] = freq;
+ ret = tegra_cpu_set_speed_cap(&new_speed);
- ret = tegra_update_cpu_speed(tegra_cpu_highest_speed());
-
-out:
mutex_unlock(&tegra_cpu_lock);
+
return ret;
}
+
static int tegra_pm_notify(struct notifier_block *nb, unsigned long event,
void *dummy)
{
@@ -161,10 +595,17 @@ static int tegra_pm_notify(struct notifier_block *nb, unsigned long event,
if (event == PM_SUSPEND_PREPARE) {
is_suspended = true;
pr_info("Tegra cpufreq suspend: setting frequency to %d kHz\n",
- freq_table[0].frequency);
- tegra_update_cpu_speed(freq_table[0].frequency);
+ freq_table[suspend_index].frequency);
+ tegra_update_cpu_speed(freq_table[suspend_index].frequency);
+ tegra_auto_hotplug_governor(
+ freq_table[suspend_index].frequency, true);
} else if (event == PM_POST_SUSPEND) {
+ unsigned int freq;
is_suspended = false;
+ tegra_cpu_edp_init(true);
+ tegra_cpu_set_speed_cap(&freq);
+ pr_info("Tegra cpufreq resume: restoring frequency to %d kHz\n",
+ freq);
}
mutex_unlock(&tegra_cpu_lock);
@@ -177,7 +618,7 @@ static struct notifier_block tegra_cpu_pm_notifier = {
static int tegra_cpu_init(struct cpufreq_policy *policy)
{
- if (policy->cpu >= NUM_CPUS)
+ if (policy->cpu >= CONFIG_NR_CPUS)
return -EINVAL;
cpu_clk = clk_get_sys(NULL, "cpu");
@@ -204,8 +645,9 @@ static int tegra_cpu_init(struct cpufreq_policy *policy)
policy->shared_type = CPUFREQ_SHARED_TYPE_ALL;
cpumask_copy(policy->related_cpus, cpu_possible_mask);
- if (policy->cpu == 0)
+ if (policy->cpu == 0) {
register_pm_notifier(&tegra_cpu_pm_notifier);
+ }
return 0;
}
@@ -219,8 +661,30 @@ static int tegra_cpu_exit(struct cpufreq_policy *policy)
return 0;
}
+static int tegra_cpufreq_policy_notifier(
+ struct notifier_block *nb, unsigned long event, void *data)
+{
+ int i, ret;
+ struct cpufreq_policy *policy = data;
+
+ if (event == CPUFREQ_NOTIFY) {
+ ret = cpufreq_frequency_table_target(policy, freq_table,
+ policy->max, CPUFREQ_RELATION_H, &i);
+ policy_max_speed[policy->cpu] =
+ ret ? policy->max : freq_table[i].frequency;
+ }
+ return NOTIFY_OK;
+}
+
+static struct notifier_block tegra_cpufreq_policy_nb = {
+ .notifier_call = tegra_cpufreq_policy_notifier,
+};
+
static struct freq_attr *tegra_cpufreq_attr[] = {
&cpufreq_freq_attr_scaling_available_freqs,
+#ifdef CONFIG_TEGRA_THERMAL_THROTTLE
+ &throttle,
+#endif
NULL,
};
@@ -236,12 +700,42 @@ static struct cpufreq_driver tegra_cpufreq_driver = {
static int __init tegra_cpufreq_init(void)
{
+ int ret = 0;
+
+ struct tegra_cpufreq_table_data *table_data =
+ tegra_cpufreq_table_get();
+ if (IS_ERR_OR_NULL(table_data))
+ return -EINVAL;
+
+ suspend_index = table_data->suspend_index;
+
+ ret = tegra_throttle_init(&tegra_cpu_lock);
+ if (ret)
+ return ret;
+
+ ret = tegra_auto_hotplug_init(&tegra_cpu_lock);
+ if (ret)
+ return ret;
+
+ freq_table = table_data->freq_table;
+ tegra_cpu_edp_init(false);
+
+ ret = cpufreq_register_notifier(
+ &tegra_cpufreq_policy_nb, CPUFREQ_POLICY_NOTIFIER);
+ if (ret)
+ return ret;
+
return cpufreq_register_driver(&tegra_cpufreq_driver);
}
static void __exit tegra_cpufreq_exit(void)
{
- cpufreq_unregister_driver(&tegra_cpufreq_driver);
+ tegra_throttle_exit();
+ tegra_cpu_edp_exit();
+ tegra_auto_hotplug_exit();
+ cpufreq_unregister_driver(&tegra_cpufreq_driver);
+ cpufreq_unregister_notifier(
+ &tegra_cpufreq_policy_nb, CPUFREQ_POLICY_NOTIFIER);
}
diff --git a/arch/arm/mach-tegra/cpu-tegra.h b/arch/arm/mach-tegra/cpu-tegra.h
new file mode 100644
index 000000000000..a89ccd32d463
--- /dev/null
+++ b/arch/arm/mach-tegra/cpu-tegra.h
@@ -0,0 +1,79 @@
+/*
+ * arch/arm/mach-tegra/cpu-tegra.h
+ *
+ * Copyright (c) 2011, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __MACH_TEGRA_CPU_TEGRA_H
+#define __MACH_TEGRA_CPU_TEGRA_H
+
+unsigned int tegra_getspeed(unsigned int cpu);
+int tegra_cpu_set_speed_cap(unsigned int *speed_cap);
+unsigned int tegra_count_slow_cpus(unsigned long speed_limit);
+unsigned int tegra_get_slowest_cpu_n(void);
+unsigned long tegra_cpu_lowest_speed(void);
+unsigned long tegra_cpu_highest_speed(void);
+
+#ifdef CONFIG_TEGRA_THERMAL_THROTTLE
+int tegra_throttle_init(struct mutex *cpu_lock);
+void tegra_throttle_exit(void);
+bool tegra_is_throttling(void);
+unsigned int tegra_throttle_governor_speed(unsigned int requested_speed);
+int tegra_throttle_debug_init(struct dentry *cpu_tegra_debugfs_root);
+void tegra_throttling_enable(bool enable);
+#else
+static inline int tegra_throttle_init(struct mutex *cpu_lock)
+{ return 0; }
+static inline void tegra_throttle_exit(void)
+{}
+static inline bool tegra_is_throttling(void)
+{ return false; }
+static inline unsigned int tegra_throttle_governor_speed(
+ unsigned int requested_speed)
+{ return requested_speed; }
+static inline int tegra_throttle_debug_init(
+ struct dentry *cpu_tegra_debugfs_root)
+{ return 0; }
+static inline void tegra_throttling_enable(bool enable)
+{}
+#endif /* CONFIG_TEGRA_THERMAL_THROTTLE */
+
+#if defined(CONFIG_TEGRA_AUTO_HOTPLUG) && !defined(CONFIG_ARCH_TEGRA_2x_SOC)
+int tegra_auto_hotplug_init(struct mutex *cpu_lock);
+void tegra_auto_hotplug_exit(void);
+void tegra_auto_hotplug_governor(unsigned int cpu_freq, bool suspend);
+#else
+static inline int tegra_auto_hotplug_init(struct mutex *cpu_lock)
+{ return 0; }
+static inline void tegra_auto_hotplug_exit(void)
+{ }
+static inline void tegra_auto_hotplug_governor(unsigned int cpu_freq,
+ bool suspend)
+{ }
+#endif
+
+#ifdef CONFIG_TEGRA_EDP_LIMITS
+bool tegra_cpu_edp_favor_up(unsigned int n, int mp_overhead);
+bool tegra_cpu_edp_favor_down(unsigned int n, int mp_overhead);
+#else
+static inline bool tegra_cpu_edp_favor_up(unsigned int n, int mp_overhead)
+{ return true; }
+static inline bool tegra_cpu_edp_favor_down(unsigned int n, int mp_overhead)
+{ return false; }
+#endif
+
+#endif /* __MACH_TEGRA_CPU_TEGRA_H */
diff --git a/arch/arm/mach-tegra/cpu-tegra3.c b/arch/arm/mach-tegra/cpu-tegra3.c
new file mode 100644
index 000000000000..05b2e70816e3
--- /dev/null
+++ b/arch/arm/mach-tegra/cpu-tegra3.c
@@ -0,0 +1,459 @@
+/*
+ * arch/arm/mach-tegra/cpu-tegra3.c
+ *
+ * CPU auto-hotplug for Tegra3 CPUs
+ *
+ * Copyright (c) 2011, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/cpufreq.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/cpu.h>
+#include <linux/clk.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+
+#include "pm.h"
+#include "cpu-tegra.h"
+#include "clock.h"
+
+#define INITIAL_STATE TEGRA_HP_DISABLED
+#define UP2G0_DELAY_MS 200
+#define UP2Gn_DELAY_MS 1000
+#define DOWN_DELAY_MS 2000
+
+static struct mutex *tegra3_cpu_lock;
+
+static struct workqueue_struct *hotplug_wq;
+static struct delayed_work hotplug_work;
+
+static bool no_lp;
+module_param(no_lp, bool, 0644);
+
+static unsigned long up2gn_delay;
+static unsigned long up2g0_delay;
+static unsigned long down_delay;
+module_param(up2gn_delay, ulong, 0644);
+module_param(up2g0_delay, ulong, 0644);
+module_param(down_delay, ulong, 0644);
+
+static unsigned int idle_top_freq;
+static unsigned int idle_bottom_freq;
+module_param(idle_top_freq, uint, 0644);
+module_param(idle_bottom_freq, uint, 0644);
+
+static int mp_overhead = 10;
+module_param(mp_overhead, int, 0644);
+
+static int balance_level = 75;
+module_param(balance_level, int, 0644);
+
+static struct clk *cpu_clk;
+static struct clk *cpu_g_clk;
+static struct clk *cpu_lp_clk;
+
+static struct {
+ cputime64_t time_up_total;
+ u64 last_update;
+ unsigned int up_down_count;
+} hp_stats[CONFIG_NR_CPUS + 1]; /* Append LP CPU entry at the end */
+
+static void hp_init_stats(void)
+{
+ int i;
+ u64 cur_jiffies = get_jiffies_64();
+
+ for (i = 0; i <= CONFIG_NR_CPUS; i++) {
+ hp_stats[i].time_up_total = 0;
+ hp_stats[i].last_update = cur_jiffies;
+
+ hp_stats[i].up_down_count = 0;
+ if (is_lp_cluster()) {
+ if (i == CONFIG_NR_CPUS)
+ hp_stats[i].up_down_count = 1;
+ } else {
+ if ((i < nr_cpu_ids) && cpu_online(i))
+ hp_stats[i].up_down_count = 1;
+ }
+ }
+
+}
+
+static void hp_stats_update(unsigned int cpu, bool up)
+{
+ u64 cur_jiffies = get_jiffies_64();
+ bool was_up = hp_stats[cpu].up_down_count & 0x1;
+
+ if (was_up)
+ hp_stats[cpu].time_up_total = cputime64_add(
+ hp_stats[cpu].time_up_total, cputime64_sub(
+ cur_jiffies, hp_stats[cpu].last_update));
+
+ if (was_up != up) {
+ hp_stats[cpu].up_down_count++;
+ if ((hp_stats[cpu].up_down_count & 0x1) != up) {
+ /* FIXME: sysfs user space CPU control breaks stats */
+ pr_err("tegra hotplug stats out of sync with %s CPU%d",
+ (cpu < CONFIG_NR_CPUS) ? "G" : "LP",
+ (cpu < CONFIG_NR_CPUS) ? cpu : 0);
+ hp_stats[cpu].up_down_count ^= 0x1;
+ }
+ }
+ hp_stats[cpu].last_update = cur_jiffies;
+}
+
+
+enum {
+ TEGRA_HP_DISABLED = 0,
+ TEGRA_HP_IDLE,
+ TEGRA_HP_DOWN,
+ TEGRA_HP_UP,
+};
+static int hp_state;
+
+static int hp_state_set(const char *arg, const struct kernel_param *kp)
+{
+ int ret = 0;
+ int old_state;
+
+ if (!tegra3_cpu_lock)
+ return ret;
+
+ mutex_lock(tegra3_cpu_lock);
+
+ old_state = hp_state;
+ ret = param_set_int(arg, kp);
+
+ if (ret == 0) {
+ switch (hp_state) {
+ case TEGRA_HP_DISABLED:
+ if (old_state != TEGRA_HP_DISABLED)
+ pr_info("Tegra auto-hotplug disabled\n");
+ break;
+ case TEGRA_HP_IDLE:
+ case TEGRA_HP_DOWN:
+ case TEGRA_HP_UP:
+ if (old_state == TEGRA_HP_DISABLED) {
+ hp_init_stats();
+ queue_delayed_work(
+ hotplug_wq, &hotplug_work, down_delay);
+ pr_info("Tegra auto-hotplug enabled\n");
+ }
+ break;
+ default:
+ pr_warn("%s: unable to set tegra hotplug state %d\n",
+ __func__, hp_state);
+ hp_state = old_state;
+ }
+ }
+ mutex_unlock(tegra3_cpu_lock);
+ return ret;
+}
+
+static int hp_state_get(char *buffer, const struct kernel_param *kp)
+{
+ return param_get_int(buffer, kp);
+}
+
+static struct kernel_param_ops tegra_hp_state_ops = {
+ .set = hp_state_set,
+ .get = hp_state_get,
+};
+module_param_cb(auto_hotplug, &tegra_hp_state_ops, &hp_state, 0644);
+
+
+enum {
+ TEGRA_CPU_SPEED_BALANCED,
+ TEGRA_CPU_SPEED_BIASED,
+ TEGRA_CPU_SPEED_SKEWED,
+};
+
+static noinline int tegra_cpu_speed_balance(void)
+{
+ unsigned long highest_speed = tegra_cpu_highest_speed();
+ unsigned long balanced_speed = highest_speed * balance_level / 100;
+ unsigned long skewed_speed = balanced_speed / 2;
+ unsigned int nr_cpus = num_online_cpus();
+
+ /* balanced: freq targets for all CPUs are above 50% of highest speed
+ biased: freq target for at least one CPU is below 50% threshold
+ skewed: freq targets for at least 2 CPUs are below 25% threshold */
+ if ((tegra_count_slow_cpus(skewed_speed) >= 2) ||
+ tegra_cpu_edp_favor_down(nr_cpus, mp_overhead))
+ return TEGRA_CPU_SPEED_SKEWED;
+ else if ((tegra_count_slow_cpus(balanced_speed) >= 1) ||
+ (!tegra_cpu_edp_favor_up(nr_cpus, mp_overhead)))
+ return TEGRA_CPU_SPEED_BIASED;
+ return TEGRA_CPU_SPEED_BALANCED;
+}
+
+static void tegra_auto_hotplug_work_func(struct work_struct *work)
+{
+ bool up = false;
+ unsigned int cpu = nr_cpu_ids;
+
+ mutex_lock(tegra3_cpu_lock);
+
+ switch (hp_state) {
+ case TEGRA_HP_DISABLED:
+ case TEGRA_HP_IDLE:
+ break;
+ case TEGRA_HP_DOWN:
+ cpu = tegra_get_slowest_cpu_n();
+ if (cpu < nr_cpu_ids) {
+ up = false;
+ queue_delayed_work(
+ hotplug_wq, &hotplug_work, down_delay);
+ hp_stats_update(cpu, false);
+ } else if (!is_lp_cluster() && !no_lp) {
+ if(!clk_set_parent(cpu_clk, cpu_lp_clk)) {
+ hp_stats_update(CONFIG_NR_CPUS, true);
+ hp_stats_update(0, false);
+ /* catch-up with governor target speed */
+ tegra_cpu_set_speed_cap(NULL);
+ } else
+ queue_delayed_work(
+ hotplug_wq, &hotplug_work, down_delay);
+ }
+ break;
+ case TEGRA_HP_UP:
+ if (is_lp_cluster() && !no_lp) {
+ if(!clk_set_parent(cpu_clk, cpu_g_clk)) {
+ hp_stats_update(CONFIG_NR_CPUS, false);
+ hp_stats_update(0, true);
+ /* catch-up with governor target speed */
+ tegra_cpu_set_speed_cap(NULL);
+ }
+ } else {
+ switch (tegra_cpu_speed_balance()) {
+ /* cpu speed is up and balanced - one more on-line */
+ case TEGRA_CPU_SPEED_BALANCED:
+ cpu = cpumask_next_zero(0, cpu_online_mask);
+ if (cpu < nr_cpu_ids) {
+ up = true;
+ hp_stats_update(cpu, true);
+ }
+ break;
+ /* cpu speed is up, but skewed - remove one core */
+ case TEGRA_CPU_SPEED_SKEWED:
+ cpu = tegra_get_slowest_cpu_n();
+ if (cpu < nr_cpu_ids) {
+ up = false;
+ hp_stats_update(cpu, false);
+ }
+ break;
+ /* cpu speed is up, but under-utilized - do nothing */
+ case TEGRA_CPU_SPEED_BIASED:
+ default:
+ break;
+ }
+ }
+ queue_delayed_work(
+ hotplug_wq, &hotplug_work, up2gn_delay);
+ break;
+ default:
+ pr_err("%s: invalid tegra hotplug state %d\n",
+ __func__, hp_state);
+ }
+ mutex_unlock(tegra3_cpu_lock);
+
+ if (cpu < nr_cpu_ids) {
+ if (up)
+ cpu_up(cpu);
+ else
+ cpu_down(cpu);
+ }
+}
+
+void tegra_auto_hotplug_governor(unsigned int cpu_freq, bool suspend)
+{
+ unsigned long up_delay;
+
+ if (!is_g_cluster_present())
+ return;
+
+ if (suspend && (hp_state != TEGRA_HP_DISABLED)) {
+ hp_state = TEGRA_HP_IDLE;
+ return;
+ }
+
+ up_delay = is_lp_cluster() ? up2g0_delay : up2gn_delay;
+
+ switch (hp_state) {
+ case TEGRA_HP_DISABLED:
+ break;
+ case TEGRA_HP_IDLE:
+ if (cpu_freq > idle_top_freq) {
+ hp_state = TEGRA_HP_UP;
+ queue_delayed_work(
+ hotplug_wq, &hotplug_work, up_delay);
+ } else if (cpu_freq <= idle_bottom_freq) {
+ hp_state = TEGRA_HP_DOWN;
+ queue_delayed_work(
+ hotplug_wq, &hotplug_work, down_delay);
+ }
+ break;
+ case TEGRA_HP_DOWN:
+ if (cpu_freq > idle_top_freq) {
+ hp_state = TEGRA_HP_UP;
+ queue_delayed_work(
+ hotplug_wq, &hotplug_work, up_delay);
+ } else if (cpu_freq > idle_bottom_freq) {
+ hp_state = TEGRA_HP_IDLE;
+ }
+ break;
+ case TEGRA_HP_UP:
+ if (cpu_freq <= idle_bottom_freq) {
+ hp_state = TEGRA_HP_DOWN;
+ queue_delayed_work(
+ hotplug_wq, &hotplug_work, down_delay);
+ } else if (cpu_freq <= idle_top_freq) {
+ hp_state = TEGRA_HP_IDLE;
+ }
+ break;
+ default:
+ pr_err("%s: invalid tegra hotplug state %d\n",
+ __func__, hp_state);
+ BUG();
+ }
+}
+
+int tegra_auto_hotplug_init(struct mutex *cpu_lock)
+{
+ /*
+ * Not bound to the issuer CPU (=> high-priority), has rescue worker
+ * task, single-threaded, freezable.
+ */
+ hotplug_wq = alloc_workqueue(
+ "cpu-tegra3", WQ_UNBOUND | WQ_RESCUER | WQ_FREEZABLE, 1);
+ if (!hotplug_wq)
+ return -ENOMEM;
+ INIT_DELAYED_WORK(&hotplug_work, tegra_auto_hotplug_work_func);
+
+ cpu_clk = clk_get_sys(NULL, "cpu");
+ cpu_g_clk = clk_get_sys(NULL, "cpu_g");
+ cpu_lp_clk = clk_get_sys(NULL, "cpu_lp");
+ if (IS_ERR(cpu_clk) || IS_ERR(cpu_g_clk) || IS_ERR(cpu_lp_clk))
+ return -ENOENT;
+
+ idle_top_freq = clk_get_max_rate(cpu_lp_clk) / 1000;
+ idle_bottom_freq = clk_get_min_rate(cpu_g_clk) / 1000;
+
+ up2g0_delay = msecs_to_jiffies(UP2G0_DELAY_MS);
+ up2gn_delay = msecs_to_jiffies(UP2Gn_DELAY_MS);
+ down_delay = msecs_to_jiffies(DOWN_DELAY_MS);
+
+ tegra3_cpu_lock = cpu_lock;
+ hp_state = INITIAL_STATE;
+ hp_init_stats();
+ pr_info("Tegra auto-hotplug initialized: %s\n",
+ (hp_state == TEGRA_HP_DISABLED) ? "disabled" : "enabled");
+
+ return 0;
+}
+
+#ifdef CONFIG_DEBUG_FS
+
+static struct dentry *hp_debugfs_root;
+
+static int hp_stats_show(struct seq_file *s, void *data)
+{
+ int i;
+ u64 cur_jiffies = get_jiffies_64();
+
+ mutex_lock(tegra3_cpu_lock);
+ if (hp_state != TEGRA_HP_DISABLED) {
+ for (i = 0; i <= CONFIG_NR_CPUS; i++) {
+ bool was_up = (hp_stats[i].up_down_count & 0x1);
+ hp_stats_update(i, was_up);
+ }
+ }
+ mutex_unlock(tegra3_cpu_lock);
+
+ seq_printf(s, "%-15s ", "cpu:");
+ for (i = 0; i < CONFIG_NR_CPUS; i++) {
+ seq_printf(s, "G%-9d ", i);
+ }
+ seq_printf(s, "LP\n");
+
+ seq_printf(s, "%-15s ", "transitions:");
+ for (i = 0; i <= CONFIG_NR_CPUS; i++) {
+ seq_printf(s, "%-10u ", hp_stats[i].up_down_count);
+ }
+ seq_printf(s, "\n");
+
+ seq_printf(s, "%-15s ", "time plugged:");
+ for (i = 0; i <= CONFIG_NR_CPUS; i++) {
+ seq_printf(s, "%-10llu ",
+ cputime64_to_clock_t(hp_stats[i].time_up_total));
+ }
+ seq_printf(s, "\n");
+
+ seq_printf(s, "%-15s %llu\n", "time-stamp:",
+ cputime64_to_clock_t(cur_jiffies));
+
+ return 0;
+}
+
+static int hp_stats_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, hp_stats_show, inode->i_private);
+}
+
+static const struct file_operations hp_stats_fops = {
+ .open = hp_stats_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int __init tegra_auto_hotplug_debug_init(void)
+{
+ if (!tegra3_cpu_lock)
+ return -ENOENT;
+
+ hp_debugfs_root = debugfs_create_dir("tegra_hotplug", NULL);
+ if (!hp_debugfs_root)
+ return -ENOMEM;
+
+ if (!debugfs_create_file(
+ "stats", S_IRUGO, hp_debugfs_root, NULL, &hp_stats_fops))
+ goto err_out;
+
+ return 0;
+
+err_out:
+ debugfs_remove_recursive(hp_debugfs_root);
+ return -ENOMEM;
+}
+
+late_initcall(tegra_auto_hotplug_debug_init);
+#endif
+
+void tegra_auto_hotplug_exit(void)
+{
+ destroy_workqueue(hotplug_wq);
+#ifdef CONFIG_DEBUG_FS
+ debugfs_remove_recursive(hp_debugfs_root);
+#endif
+}
diff --git a/arch/arm/mach-tegra/cpuidle-t2.c b/arch/arm/mach-tegra/cpuidle-t2.c
new file mode 100644
index 000000000000..8428bc4cfd3e
--- /dev/null
+++ b/arch/arm/mach-tegra/cpuidle-t2.c
@@ -0,0 +1,403 @@
+/*
+ * arch/arm/mach-tegra/cpuidle-t2.c
+ *
+ * CPU idle driver for Tegra2 CPUs
+ *
+ * Copyright (c) 2010-2011, NVIDIA Corporation.
+ * Copyright (c) 2011 Google, Inc.
+ * Author: Colin Cross <ccross@android.com>
+ * Gary King <gking@nvidia.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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/cpu.h>
+#include <linux/cpuidle.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+#include <linux/sched.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/smp.h>
+#include <linux/suspend.h>
+#include <linux/tick.h>
+
+#include <asm/cpu_pm.h>
+
+#include <mach/iomap.h>
+#include <mach/irqs.h>
+
+#include "cpuidle.h"
+#include "gic.h"
+#include "pm.h"
+#include "sleep.h"
+#include "timer.h"
+
+static struct {
+ unsigned int cpu_ready_count[2];
+ unsigned long long cpu_wants_lp2_time[2];
+ unsigned long long in_lp2_time;
+ unsigned int both_idle_count;
+ unsigned int tear_down_count;
+ unsigned int lp2_count;
+ unsigned int lp2_completed_count;
+ unsigned int lp2_count_bin[32];
+ unsigned int lp2_completed_count_bin[32];
+ unsigned int lp2_int_count[NR_IRQS];
+ unsigned int last_lp2_int_count[NR_IRQS];
+} idle_stats;
+
+static inline unsigned int time_to_bin(unsigned int time)
+{
+ return fls(time);
+}
+
+#ifdef CONFIG_SMP
+
+#define CLK_RST_CONTROLLER_CLK_CPU_CMPLX 0x4C
+#define CLK_RST_CONTROLLER_RST_CPU_CMPLX_CLR 0x344
+
+static void __iomem *clk_rst = IO_ADDRESS(TEGRA_CLK_RESET_BASE);
+static void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE);
+static s64 tegra_cpu1_wake_by_time = LLONG_MAX;
+
+static int tegra2_reset_sleeping_cpu(int cpu)
+{
+ int ret = 0;
+
+ BUG_ON(cpu == 0);
+ BUG_ON(cpu == smp_processor_id());
+ tegra_pen_lock();
+
+ if (readl(pmc + PMC_SCRATCH41) == CPU_RESETTABLE)
+ tegra2_cpu_reset(cpu);
+ else
+ ret = -EINVAL;
+
+ tegra_pen_unlock();
+
+ return ret;
+}
+
+static void tegra2_wake_reset_cpu(int cpu)
+{
+ u32 reg;
+
+ BUG_ON(cpu == 0);
+ BUG_ON(cpu == smp_processor_id());
+
+ tegra_pen_lock();
+
+ tegra2_cpu_clear_resettable();
+
+ /* enable cpu clock on cpu */
+ reg = readl(clk_rst + 0x4c);
+ writel(reg & ~(1 << (8 + cpu)),
+ clk_rst + CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
+
+ /* take the CPU out of reset */
+ reg = 0x1111 << cpu;
+ writel(reg, clk_rst +
+ CLK_RST_CONTROLLER_RST_CPU_CMPLX_CLR);
+
+ /* unhalt the cpu */
+ flowctrl_writel(0, FLOW_CTRL_HALT_CPU(1));
+
+ tegra_pen_unlock();
+}
+
+static int tegra2_reset_other_cpus(int cpu)
+{
+ int i;
+ int ret = 0;
+
+ BUG_ON(cpu != 0);
+
+ for_each_online_cpu(i) {
+ if (i != cpu) {
+ if (tegra2_reset_sleeping_cpu(i)) {
+ ret = -EBUSY;
+ break;
+ }
+ }
+ }
+
+ if (ret) {
+ for_each_online_cpu(i) {
+ if (i != cpu)
+ tegra2_wake_reset_cpu(i);
+ }
+ return ret;
+ }
+
+ return 0;
+}
+#else
+static void tegra2_wake_reset_cpu(int cpu)
+{
+}
+
+static int tegra2_reset_other_cpus(int cpu)
+{
+ return 0;
+}
+#endif
+
+bool tegra2_lp2_is_allowed(struct cpuidle_device *dev,
+ struct cpuidle_state *state)
+{
+ s64 request = ktime_to_us(tick_nohz_get_sleep_length());
+
+ if (request < state->target_residency) {
+ /* Not enough time left to enter LP2 */
+ return false;
+ }
+
+ return true;
+}
+
+static int tegra2_idle_lp2_cpu_0(struct cpuidle_device *dev,
+ struct cpuidle_state *state, s64 request)
+{
+ ktime_t entry_time;
+ ktime_t exit_time;
+ s64 wake_time;
+ bool sleep_completed = false;
+ int bin;
+ int i;
+
+ while (tegra2_cpu_is_resettable_soon())
+ cpu_relax();
+
+ if (tegra2_reset_other_cpus(dev->cpu))
+ return -EBUSY;
+
+ idle_stats.both_idle_count++;
+
+ if (request < state->target_residency) {
+ /* Not enough time left to enter LP2 */
+ tegra_cpu_wfi();
+ return -EBUSY;
+ }
+
+ /* LP2 entry time */
+ entry_time = ktime_get();
+
+ /* LP2 initial targeted wake time */
+ wake_time = ktime_to_us(entry_time) + request;
+
+ /* CPU0 must wake up before CPU1. */
+ smp_rmb();
+ wake_time = min_t(s64, wake_time, tegra_cpu1_wake_by_time);
+
+ /* LP2 actual targeted wake time */
+ request = wake_time - ktime_to_us(entry_time);
+ BUG_ON(wake_time < 0LL);
+
+ idle_stats.tear_down_count++;
+ entry_time = ktime_get();
+
+ if (request > state->target_residency) {
+ s64 sleep_time = request - tegra_lp2_exit_latency;
+
+ bin = time_to_bin((u32)request / 1000);
+ idle_stats.lp2_count++;
+ idle_stats.lp2_count_bin[bin]++;
+
+ clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &dev->cpu);
+
+ if (tegra_idle_lp2_last(sleep_time, 0) == 0)
+ sleep_completed = true;
+ else {
+ int irq = tegra_gic_pending_interrupt();
+ idle_stats.lp2_int_count[irq]++;
+ }
+
+ clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &dev->cpu);
+ }
+
+ for_each_online_cpu(i) {
+ if (i != dev->cpu)
+ tegra2_wake_reset_cpu(i);
+ }
+
+ exit_time = ktime_get();
+ if (sleep_completed) {
+ /*
+ * Stayed in LP2 for the full time until the next tick,
+ * adjust the exit latency based on measurement
+ */
+ s64 actual_time = ktime_to_us(ktime_sub(exit_time, entry_time));
+ long offset = (long)(actual_time - request);
+ int latency = tegra_lp2_exit_latency + offset / 16;
+ latency = clamp(latency, 0, 10000);
+ tegra_lp2_exit_latency = latency;
+ smp_wmb();
+
+ idle_stats.lp2_completed_count++;
+ idle_stats.lp2_completed_count_bin[bin]++;
+ idle_stats.in_lp2_time += actual_time;
+
+ pr_debug("%lld %lld %ld %d\n", request, actual_time,
+ offset, bin);
+ }
+
+ return 0;
+}
+
+static void tegra2_idle_lp2_cpu_1(struct cpuidle_device *dev,
+ struct cpuidle_state *state, s64 request)
+{
+#ifdef CONFIG_SMP
+ struct tegra_twd_context twd_context;
+
+ if (request < tegra_lp2_exit_latency) {
+ /*
+ * Not enough time left to enter LP2
+ */
+ tegra2_cpu_clear_resettable();
+ tegra_cpu_wfi();
+ return;
+ }
+
+ /* Save time this CPU must be awakened by. */
+ tegra_cpu1_wake_by_time = ktime_to_us(ktime_get()) + request;
+ smp_wmb();
+
+ tegra_twd_suspend(&twd_context);
+
+ tegra2_sleep_wfi(PLAT_PHYS_OFFSET - PAGE_OFFSET);
+
+ tegra2_cpu_clear_resettable();
+
+ tegra_cpu1_wake_by_time = LLONG_MAX;
+
+ tegra_twd_resume(&twd_context);
+#endif
+}
+
+void tegra2_idle_lp2(struct cpuidle_device *dev,
+ struct cpuidle_state *state)
+{
+ s64 request = ktime_to_us(tick_nohz_get_sleep_length());
+ bool last_cpu = tegra_set_cpu_in_lp2(dev->cpu);
+
+ cpu_pm_enter();
+
+ if (dev->cpu == 0) {
+ if (last_cpu) {
+ if (tegra2_idle_lp2_cpu_0(dev, state, request) < 0) {
+ int i;
+ for_each_online_cpu(i) {
+ if (i != dev->cpu)
+ tegra2_wake_reset_cpu(i);
+ }
+ }
+ } else
+ tegra_cpu_wfi();
+ } else {
+ BUG_ON(last_cpu);
+ tegra2_idle_lp2_cpu_1(dev, state, request);
+ }
+
+ cpu_pm_exit();
+ tegra_clear_cpu_in_lp2(dev->cpu);
+}
+
+void tegra2_cpu_idle_stats_lp2_ready(unsigned int cpu)
+{
+ idle_stats.cpu_ready_count[cpu]++;
+}
+
+void tegra2_cpu_idle_stats_lp2_time(unsigned int cpu, s64 us)
+{
+ idle_stats.cpu_wants_lp2_time[cpu] += us;
+}
+
+#ifdef CONFIG_DEBUG_FS
+int tegra2_lp2_debug_show(struct seq_file *s, void *data)
+{
+ int bin;
+ int i;
+ seq_printf(s, " cpu0 cpu1\n");
+ seq_printf(s, "-------------------------------------------------\n");
+ seq_printf(s, "cpu ready: %8u %8u\n",
+ idle_stats.cpu_ready_count[0],
+ idle_stats.cpu_ready_count[1]);
+ seq_printf(s, "both idle: %8u %7u%% %7u%%\n",
+ idle_stats.both_idle_count,
+ idle_stats.both_idle_count * 100 /
+ (idle_stats.cpu_ready_count[0] ?: 1),
+ idle_stats.both_idle_count * 100 /
+ (idle_stats.cpu_ready_count[1] ?: 1));
+ seq_printf(s, "tear down: %8u %7u%%\n", idle_stats.tear_down_count,
+ idle_stats.tear_down_count * 100 /
+ (idle_stats.both_idle_count ?: 1));
+ seq_printf(s, "lp2: %8u %7u%%\n", idle_stats.lp2_count,
+ idle_stats.lp2_count * 100 /
+ (idle_stats.both_idle_count ?: 1));
+ seq_printf(s, "lp2 completed: %8u %7u%%\n",
+ idle_stats.lp2_completed_count,
+ idle_stats.lp2_completed_count * 100 /
+ (idle_stats.lp2_count ?: 1));
+
+ seq_printf(s, "\n");
+ seq_printf(s, "cpu ready time: %8llu %8llu ms\n",
+ div64_u64(idle_stats.cpu_wants_lp2_time[0], 1000),
+ div64_u64(idle_stats.cpu_wants_lp2_time[1], 1000));
+ seq_printf(s, "lp2 time: %8llu ms %7d%% %7d%%\n",
+ div64_u64(idle_stats.in_lp2_time, 1000),
+ (int)div64_u64(idle_stats.in_lp2_time * 100,
+ idle_stats.cpu_wants_lp2_time[0] ?: 1),
+ (int)div64_u64(idle_stats.in_lp2_time * 100,
+ idle_stats.cpu_wants_lp2_time[1] ?: 1));
+
+ seq_printf(s, "\n");
+ seq_printf(s, "%19s %8s %8s %8s\n", "", "lp2", "comp", "%");
+ seq_printf(s, "-------------------------------------------------\n");
+ for (bin = 0; bin < 32; bin++) {
+ if (idle_stats.lp2_count_bin[bin] == 0)
+ continue;
+ seq_printf(s, "%6u - %6u ms: %8u %8u %7u%%\n",
+ 1 << (bin - 1), 1 << bin,
+ idle_stats.lp2_count_bin[bin],
+ idle_stats.lp2_completed_count_bin[bin],
+ idle_stats.lp2_completed_count_bin[bin] * 100 /
+ idle_stats.lp2_count_bin[bin]);
+ }
+
+ seq_printf(s, "\n");
+ seq_printf(s, "%3s %20s %6s %10s\n",
+ "int", "name", "count", "last count");
+ seq_printf(s, "--------------------------------------------\n");
+ for (i = 0; i < NR_IRQS; i++) {
+ if (idle_stats.lp2_int_count[i] == 0)
+ continue;
+ seq_printf(s, "%3d %20s %6d %10d\n",
+ i, irq_to_desc(i)->action ?
+ irq_to_desc(i)->action->name ?: "???" : "???",
+ idle_stats.lp2_int_count[i],
+ idle_stats.lp2_int_count[i] -
+ idle_stats.last_lp2_int_count[i]);
+ idle_stats.last_lp2_int_count[i] = idle_stats.lp2_int_count[i];
+ };
+ return 0;
+}
+#endif
diff --git a/arch/arm/mach-tegra/cpuidle-t3.c b/arch/arm/mach-tegra/cpuidle-t3.c
new file mode 100644
index 000000000000..e326a9b99875
--- /dev/null
+++ b/arch/arm/mach-tegra/cpuidle-t3.c
@@ -0,0 +1,439 @@
+/*
+ * arch/arm/mach-tegra/cpuidle-t3.c
+ *
+ * CPU idle driver for Tegra3 CPUs
+ *
+ * Copyright (c) 2010-2011, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/cpu.h>
+#include <linux/cpuidle.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/hrtimer.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+#include <linux/ratelimit.h>
+#include <linux/sched.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/smp.h>
+#include <linux/suspend.h>
+#include <linux/tick.h>
+#include <linux/clk.h>
+
+#include <asm/cacheflush.h>
+#include <asm/cpu_pm.h>
+#include <asm/hardware/gic.h>
+#include <asm/localtimer.h>
+
+#include <mach/iomap.h>
+#include <mach/irqs.h>
+
+#include <trace/events/power.h>
+
+#include "clock.h"
+#include "cpuidle.h"
+#include "dvfs.h"
+#include "fuse.h"
+#include "gic.h"
+#include "pm.h"
+#include "reset.h"
+#include "sleep.h"
+#include "timer.h"
+
+#define CLK_RST_CONTROLLER_CPU_CMPLX_STATUS \
+ (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x470)
+
+#ifdef CONFIG_SMP
+static s64 tegra_cpu_wake_by_time[4] = {
+ LLONG_MAX, LLONG_MAX, LLONG_MAX, LLONG_MAX };
+#endif
+
+static bool lp2_0_in_idle = true;
+module_param(lp2_0_in_idle, bool, 0644);
+
+static bool lp2_n_in_idle = true;
+module_param(lp2_n_in_idle, bool, 0644);
+
+static struct clk *cpu_clk_for_dvfs;
+static struct clk *twd_clk;
+
+static struct {
+ unsigned int cpu_ready_count[5];
+ unsigned int tear_down_count[5];
+ unsigned long long cpu_wants_lp2_time[5];
+ unsigned long long in_lp2_time[5];
+ unsigned int lp2_count;
+ unsigned int lp2_completed_count;
+ unsigned int lp2_count_bin[32];
+ unsigned int lp2_completed_count_bin[32];
+ unsigned int lp2_int_count[NR_IRQS];
+ unsigned int last_lp2_int_count[NR_IRQS];
+} idle_stats;
+
+static inline unsigned int time_to_bin(unsigned int time)
+{
+ return fls(time);
+}
+
+static inline void tegra_irq_unmask(int irq)
+{
+ struct irq_data *data = irq_get_irq_data(irq);
+ data->chip->irq_unmask(data);
+}
+
+static inline unsigned int cpu_number(unsigned int n)
+{
+ return is_lp_cluster() ? 4 : n;
+}
+
+void tegra3_cpu_idle_stats_lp2_ready(unsigned int cpu)
+{
+ idle_stats.cpu_ready_count[cpu_number(cpu)]++;
+}
+
+void tegra3_cpu_idle_stats_lp2_time(unsigned int cpu, s64 us)
+{
+ idle_stats.cpu_wants_lp2_time[cpu_number(cpu)] += us;
+}
+
+bool tegra3_lp2_is_allowed(struct cpuidle_device *dev,
+ struct cpuidle_state *state)
+{
+ s64 request;
+
+ if (!tegra_all_cpus_booted)
+ return false;
+
+ if ((!lp2_0_in_idle && !dev->cpu) || (!lp2_n_in_idle && dev->cpu))
+ return false;
+
+ /* On A01, LP2 on slave CPU's cause ranhdom CPU hangs.
+ * Refer to Bug 804085.
+ */
+ if ((tegra_get_revision() == TEGRA_REVISION_A01) &&
+ num_online_cpus() > 1)
+ return false;
+
+ /* FIXME: All CPU's entering LP2 is not working.
+ * Don't let CPU0 enter LP2 when any secondary CPU is online.
+ */
+ if ((dev->cpu == 0) && (num_online_cpus() > 1))
+ return false;
+
+ if (dev->cpu == 0) {
+ u32 reg = readl(CLK_RST_CONTROLLER_CPU_CMPLX_STATUS);
+ if ((reg & 0xE) != 0xE)
+ return false;
+
+ if (tegra_dvfs_rail_updating(cpu_clk_for_dvfs))
+ return false;
+ }
+
+ request = ktime_to_us(tick_nohz_get_sleep_length());
+ if (request < state->target_residency) {
+ /* Not enough time left to enter LP2 */
+ return false;
+ }
+
+ return true;
+}
+
+static inline void tegra3_lp3_fall_back(struct cpuidle_device *dev)
+{
+ tegra_cpu_wfi();
+ /* fall back here from LP2 path - tell cpuidle governor */
+ dev->last_state = &dev->states[0];
+}
+
+static void tegra3_idle_enter_lp2_cpu_0(struct cpuidle_device *dev,
+ struct cpuidle_state *state, s64 request)
+{
+ ktime_t entry_time;
+ ktime_t exit_time;
+ bool sleep_completed = false;
+ int bin;
+
+ /* LP2 entry time */
+ entry_time = ktime_get();
+
+ if (request < state->target_residency) {
+ /* Not enough time left to enter LP2 */
+ tegra3_lp3_fall_back(dev);
+ return;
+ }
+
+#ifdef CONFIG_SMP
+ if (!is_lp_cluster() && (num_online_cpus() > 1)) {
+ s64 wake_time;
+ unsigned int i;
+
+ /* Disable the distributor -- this is the only way to
+ prevent the other CPUs from responding to interrupts
+ and potentially fiddling with the distributor
+ registers while we're fiddling with them. */
+ tegra_gic_dist_disable();
+
+ /* Did an interrupt come in for another CPU before we
+ could disable the distributor? */
+ if (!tegra3_lp2_is_allowed(dev, state)) {
+ /* Yes, re-enable the distributor and LP3. */
+ tegra_gic_dist_enable();
+ tegra3_lp3_fall_back(dev);
+ return;
+ }
+
+ /* Save and disable the affinity setting for the other
+ CPUs and route all interrupts to CPU0. */
+ tegra_gic_disable_affinity();
+
+ /* Re-enable the distributor. */
+ tegra_gic_dist_enable();
+
+ /* LP2 initial targeted wake time */
+ wake_time = ktime_to_us(entry_time) + request;
+
+ /* CPU0 must wake up before any of the other CPUs. */
+ smp_rmb();
+ for (i = 1; i < CONFIG_NR_CPUS; i++)
+ wake_time = min_t(s64, wake_time,
+ tegra_cpu_wake_by_time[i]);
+
+ /* LP2 actual targeted wake time */
+ request = wake_time - ktime_to_us(entry_time);
+ BUG_ON(wake_time < 0LL);
+ }
+#endif
+
+ if (request > state->target_residency) {
+ s64 sleep_time = request - tegra_lp2_exit_latency;
+
+ bin = time_to_bin((u32)request / 1000);
+ idle_stats.tear_down_count[cpu_number(dev->cpu)]++;
+ idle_stats.lp2_count++;
+ idle_stats.lp2_count_bin[bin]++;
+
+ trace_power_start(POWER_CSTATE, 2, dev->cpu);
+ clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &dev->cpu);
+
+ if (tegra_idle_lp2_last(sleep_time, 0) == 0)
+ sleep_completed = true;
+ else {
+ int irq = tegra_gic_pending_interrupt();
+ idle_stats.lp2_int_count[irq]++;
+ }
+
+ clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &dev->cpu);
+ }
+
+#ifdef CONFIG_SMP
+ if (!is_lp_cluster() && (num_online_cpus() > 1)) {
+
+ /* Disable the distributor. */
+ tegra_gic_dist_disable();
+
+ /* Restore the other CPU's interrupt affinity. */
+ tegra_gic_restore_affinity();
+
+ /* Re-enable the distributor. */
+ tegra_gic_dist_enable();
+ }
+#endif
+
+ exit_time = ktime_get();
+ if (sleep_completed) {
+ /*
+ * Stayed in LP2 for the full time until the next tick,
+ * adjust the exit latency based on measurement
+ */
+ int offset = ktime_to_us(ktime_sub(exit_time, entry_time))
+ - request;
+ int latency = tegra_lp2_exit_latency + offset / 16;
+ latency = clamp(latency, 0, 10000);
+ tegra_lp2_exit_latency = latency;
+ smp_wmb();
+
+ idle_stats.lp2_completed_count++;
+ idle_stats.lp2_completed_count_bin[bin]++;
+ idle_stats.in_lp2_time[cpu_number(dev->cpu)] +=
+ ktime_to_us(ktime_sub(exit_time, entry_time));
+
+ pr_debug("%lld %lld %d %d\n", request,
+ ktime_to_us(ktime_sub(exit_time, entry_time)),
+ offset, bin);
+ }
+}
+
+static void tegra3_idle_enter_lp2_cpu_n(struct cpuidle_device *dev,
+ struct cpuidle_state *state, s64 request)
+{
+#ifdef CONFIG_SMP
+ ktime_t entery_time;
+ u32 twd_cnt;
+ u32 twd_ctrl = readl(twd_base + TWD_TIMER_CONTROL);
+ unsigned long twd_rate = clk_get_rate(twd_clk);
+
+ if ((twd_ctrl & TWD_TIMER_CONTROL_ENABLE) &&
+ (twd_ctrl & TWD_TIMER_CONTROL_IT_ENABLE)) {
+ twd_cnt = readl(twd_base + TWD_TIMER_COUNTER);
+ request = div_u64((u64)twd_cnt * 1000000, twd_rate);
+ }
+
+ if (request < tegra_lp2_exit_latency) {
+ /*
+ * Not enough time left to enter LP2
+ */
+ tegra3_lp3_fall_back(dev);
+ return;
+ }
+
+ idle_stats.tear_down_count[cpu_number(dev->cpu)]++;
+
+ trace_power_start(POWER_CSTATE, 2, dev->cpu);
+
+ entery_time = ktime_get();
+
+ /* Save time this CPU must be awakened by. */
+ tegra_cpu_wake_by_time[dev->cpu] = ktime_to_us(ktime_get()) + request;
+ smp_wmb();
+
+ tegra3_sleep_cpu_secondary(PLAT_PHYS_OFFSET - PAGE_OFFSET);
+
+ tegra_cpu_wake_by_time[dev->cpu] = LLONG_MAX;
+
+ idle_stats.in_lp2_time[cpu_number(dev->cpu)] +=
+ ktime_to_us(ktime_sub(ktime_get(), entery_time));
+#endif
+}
+
+void tegra3_idle_lp2(struct cpuidle_device *dev,
+ struct cpuidle_state *state)
+{
+ s64 request = ktime_to_us(tick_nohz_get_sleep_length());
+ bool last_cpu = tegra_set_cpu_in_lp2(dev->cpu);
+
+ cpu_pm_enter();
+
+ if (last_cpu && (dev->cpu == 0))
+ tegra3_idle_enter_lp2_cpu_0(dev, state, request);
+ else
+ tegra3_idle_enter_lp2_cpu_n(dev, state, request);
+
+ cpu_pm_exit();
+ tegra_clear_cpu_in_lp2(dev->cpu);
+}
+
+int tegra3_cpudile_init_soc(void)
+{
+ cpu_clk_for_dvfs = tegra_get_clock_by_name("cpu_g");
+ twd_clk = tegra_get_clock_by_name("twd");
+ return 0;
+}
+
+#ifdef CONFIG_DEBUG_FS
+int tegra3_lp2_debug_show(struct seq_file *s, void *data)
+{
+ int bin;
+ int i;
+ seq_printf(s, " cpu0 cpu1 cpu2 cpu3 cpulp\n");
+ seq_printf(s, "-----------------------------------------------------------------------------\n");
+ seq_printf(s, "cpu ready: %8u %8u %8u %8u %8u\n",
+ idle_stats.cpu_ready_count[0],
+ idle_stats.cpu_ready_count[1],
+ idle_stats.cpu_ready_count[2],
+ idle_stats.cpu_ready_count[3],
+ idle_stats.cpu_ready_count[4]);
+ seq_printf(s, "tear down: %8u %8u %8u %8u %8u\n",
+ idle_stats.tear_down_count[0],
+ idle_stats.tear_down_count[1],
+ idle_stats.tear_down_count[2],
+ idle_stats.tear_down_count[3],
+ idle_stats.tear_down_count[4]);
+ seq_printf(s, "lp2: %8u\n", idle_stats.lp2_count);
+ seq_printf(s, "lp2 completed: %8u %7u%%\n",
+ idle_stats.lp2_completed_count,
+ idle_stats.lp2_completed_count * 100 /
+ (idle_stats.lp2_count ?: 1));
+
+ seq_printf(s, "\n");
+ seq_printf(s, "cpu ready time: %8llu %8llu %8llu %8llu %8llu ms\n",
+ div64_u64(idle_stats.cpu_wants_lp2_time[0], 1000),
+ div64_u64(idle_stats.cpu_wants_lp2_time[1], 1000),
+ div64_u64(idle_stats.cpu_wants_lp2_time[2], 1000),
+ div64_u64(idle_stats.cpu_wants_lp2_time[3], 1000),
+ div64_u64(idle_stats.cpu_wants_lp2_time[4], 1000));
+
+ seq_printf(s, "lp2 time: %8llu %8llu %8llu %8llu %8llu ms\n",
+ div64_u64(idle_stats.in_lp2_time[0], 1000),
+ div64_u64(idle_stats.in_lp2_time[1], 1000),
+ div64_u64(idle_stats.in_lp2_time[2], 1000),
+ div64_u64(idle_stats.in_lp2_time[3], 1000),
+ div64_u64(idle_stats.in_lp2_time[4], 1000));
+
+ seq_printf(s, "lp2 %%: %7d%% %7d%% %7d%% %7d%% %7d%%\n",
+ (int)(idle_stats.cpu_wants_lp2_time[0] ?
+ div64_u64(idle_stats.in_lp2_time[0] * 100,
+ idle_stats.cpu_wants_lp2_time[0]) : 0),
+ (int)(idle_stats.cpu_wants_lp2_time[1] ?
+ div64_u64(idle_stats.in_lp2_time[1] * 100,
+ idle_stats.cpu_wants_lp2_time[1]) : 0),
+ (int)(idle_stats.cpu_wants_lp2_time[2] ?
+ div64_u64(idle_stats.in_lp2_time[2] * 100,
+ idle_stats.cpu_wants_lp2_time[2]) : 0),
+ (int)(idle_stats.cpu_wants_lp2_time[3] ?
+ div64_u64(idle_stats.in_lp2_time[3] * 100,
+ idle_stats.cpu_wants_lp2_time[3]) : 0),
+ (int)(idle_stats.cpu_wants_lp2_time[4] ?
+ div64_u64(idle_stats.in_lp2_time[4] * 100,
+ idle_stats.cpu_wants_lp2_time[4]) : 0));
+ seq_printf(s, "\n");
+
+ seq_printf(s, "%19s %8s %8s %8s\n", "", "lp2", "comp", "%");
+ seq_printf(s, "-------------------------------------------------\n");
+ for (bin = 0; bin < 32; bin++) {
+ if (idle_stats.lp2_count_bin[bin] == 0)
+ continue;
+ seq_printf(s, "%6u - %6u ms: %8u %8u %7u%%\n",
+ 1 << (bin - 1), 1 << bin,
+ idle_stats.lp2_count_bin[bin],
+ idle_stats.lp2_completed_count_bin[bin],
+ idle_stats.lp2_completed_count_bin[bin] * 100 /
+ idle_stats.lp2_count_bin[bin]);
+ }
+
+ seq_printf(s, "\n");
+ seq_printf(s, "%3s %20s %6s %10s\n",
+ "int", "name", "count", "last count");
+ seq_printf(s, "--------------------------------------------\n");
+ for (i = 0; i < NR_IRQS; i++) {
+ if (idle_stats.lp2_int_count[i] == 0)
+ continue;
+ seq_printf(s, "%3d %20s %6d %10d\n",
+ i, irq_to_desc(i)->action ?
+ irq_to_desc(i)->action->name ?: "???" : "???",
+ idle_stats.lp2_int_count[i],
+ idle_stats.lp2_int_count[i] -
+ idle_stats.last_lp2_int_count[i]);
+ idle_stats.last_lp2_int_count[i] = idle_stats.lp2_int_count[i];
+ };
+ return 0;
+}
+#endif
diff --git a/arch/arm/mach-tegra/cpuidle.c b/arch/arm/mach-tegra/cpuidle.c
new file mode 100644
index 000000000000..bdf45f81f9da
--- /dev/null
+++ b/arch/arm/mach-tegra/cpuidle.c
@@ -0,0 +1,315 @@
+/*
+ * arch/arm/mach-tegra/cpuidle.c
+ *
+ * CPU idle driver for Tegra CPUs
+ *
+ * Copyright (c) 2010-2011, NVIDIA Corporation.
+ * Copyright (c) 2011 Google, Inc.
+ * Author: Colin Cross <ccross@android.com>
+ * Gary King <gking@nvidia.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/kernel.h>
+#include <linux/cpu.h>
+#include <linux/cpuidle.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+#include <linux/sched.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/smp.h>
+#include <linux/suspend.h>
+#include <linux/tick.h>
+
+#include <asm/cpu_pm.h>
+
+#include <mach/iomap.h>
+#include <mach/irqs.h>
+
+#include <trace/events/power.h>
+
+#include "cpuidle.h"
+#include "pm.h"
+#include "sleep.h"
+
+int tegra_lp2_exit_latency;
+static int tegra_lp2_power_off_time;
+static unsigned int tegra_lp2_min_residency;
+
+struct cpuidle_driver tegra_idle = {
+ .name = "tegra_idle",
+ .owner = THIS_MODULE,
+};
+
+static DEFINE_PER_CPU(struct cpuidle_device *, idle_devices);
+
+static int tegra_idle_enter_lp3(struct cpuidle_device *dev,
+ struct cpuidle_state *state)
+{
+ ktime_t enter, exit;
+ s64 us;
+
+ trace_power_start(POWER_CSTATE, 1, dev->cpu);
+
+ local_irq_disable();
+ local_fiq_disable();
+
+ enter = ktime_get();
+
+ tegra_cpu_wfi();
+
+ exit = ktime_sub(ktime_get(), enter);
+ us = ktime_to_us(exit);
+
+ local_fiq_enable();
+ local_irq_enable();
+ return (int)us;
+}
+
+static bool lp2_in_idle __read_mostly = false;
+
+#ifdef CONFIG_PM_SLEEP
+static bool lp2_in_idle_modifiable __read_mostly = true;
+static bool lp2_disabled_by_suspend;
+
+void tegra_lp2_in_idle(bool enable)
+{
+ /* If LP2 in idle is permanently disabled it can't be re-enabled. */
+ if (lp2_in_idle_modifiable) {
+ lp2_in_idle = enable;
+ lp2_in_idle_modifiable = enable;
+ if (!enable)
+ pr_warn("LP2 in idle disabled\n");
+ }
+}
+
+static int tegra_idle_enter_lp2(struct cpuidle_device *dev,
+ struct cpuidle_state *state)
+{
+ ktime_t enter, exit;
+ s64 us;
+
+ if (!lp2_in_idle || lp2_disabled_by_suspend ||
+ !tegra_lp2_is_allowed(dev, state))
+ return tegra_idle_enter_lp3(dev, state);
+
+ local_irq_disable();
+ enter = ktime_get();
+
+ tegra_cpu_idle_stats_lp2_ready(dev->cpu);
+ tegra_idle_lp2(dev, state);
+
+ exit = ktime_sub(ktime_get(), enter);
+ us = ktime_to_us(exit);
+
+ local_irq_enable();
+
+ /* cpu clockevents may have been reset by powerdown */
+ hrtimer_peek_ahead_timers();
+
+ smp_rmb();
+
+ /* Update LP2 latency provided no fall back to LP3 */
+ if (state == dev->last_state) {
+ state->exit_latency = tegra_lp2_exit_latency;
+ state->target_residency = tegra_lp2_exit_latency +
+ tegra_lp2_power_off_time;
+ if (state->target_residency < tegra_lp2_min_residency)
+ state->target_residency = tegra_lp2_min_residency;
+ }
+ tegra_cpu_idle_stats_lp2_time(dev->cpu, us);
+
+ return (int)us;
+}
+#endif
+
+static int tegra_idle_prepare(struct cpuidle_device *dev)
+{
+#ifdef CONFIG_PM_SLEEP
+ if (lp2_in_idle)
+ dev->states[1].flags &= ~CPUIDLE_FLAG_IGNORE;
+ else
+ dev->states[1].flags |= CPUIDLE_FLAG_IGNORE;
+#endif
+
+ return 0;
+}
+
+static int tegra_cpuidle_register_device(unsigned int cpu)
+{
+ struct cpuidle_device *dev;
+ struct cpuidle_state *state;
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ dev->state_count = 0;
+ dev->cpu = cpu;
+
+ state = &dev->states[0];
+ snprintf(state->name, CPUIDLE_NAME_LEN, "LP3");
+ snprintf(state->desc, CPUIDLE_DESC_LEN, "CPU flow-controlled");
+ state->exit_latency = 10;
+ state->target_residency = 10;
+ state->power_usage = 600;
+ state->flags = CPUIDLE_FLAG_TIME_VALID;
+ state->enter = tegra_idle_enter_lp3;
+ dev->safe_state = state;
+ dev->state_count++;
+
+#ifdef CONFIG_PM_SLEEP
+ state = &dev->states[1];
+ snprintf(state->name, CPUIDLE_NAME_LEN, "LP2");
+ snprintf(state->desc, CPUIDLE_DESC_LEN, "CPU power-gate");
+ state->exit_latency = tegra_cpu_power_good_time();
+
+ state->target_residency = tegra_cpu_power_off_time() +
+ tegra_cpu_power_good_time();
+ if (state->target_residency < tegra_lp2_min_residency)
+ state->target_residency = tegra_lp2_min_residency;
+ state->power_usage = 0;
+ state->flags = CPUIDLE_FLAG_TIME_VALID;
+ state->enter = tegra_idle_enter_lp2;
+
+ dev->power_specified = 1;
+ dev->safe_state = state;
+ dev->state_count++;
+#endif
+
+ dev->prepare = tegra_idle_prepare;
+
+ if (cpuidle_register_device(dev)) {
+ pr_err("CPU%u: failed to register idle device\n", cpu);
+ kfree(dev);
+ return -EIO;
+ }
+ per_cpu(idle_devices, cpu) = dev;
+ return 0;
+}
+
+static int tegra_cpuidle_pm_notify(struct notifier_block *nb,
+ unsigned long event, void *dummy)
+{
+#ifdef CONFIG_PM_SLEEP
+ if (event == PM_SUSPEND_PREPARE)
+ lp2_disabled_by_suspend = true;
+ else if (event == PM_POST_SUSPEND)
+ lp2_disabled_by_suspend = false;
+#endif
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block tegra_cpuidle_pm_notifier = {
+ .notifier_call = tegra_cpuidle_pm_notify,
+};
+
+static int __init tegra_cpuidle_init(void)
+{
+ unsigned int cpu;
+ int ret;
+
+ ret = cpuidle_register_driver(&tegra_idle);
+ if (ret)
+ return ret;
+
+#ifdef CONFIG_PM_SLEEP
+ tegra_lp2_min_residency = tegra_cpu_lp2_min_residency();
+ tegra_lp2_exit_latency = tegra_cpu_power_good_time();
+ tegra_lp2_power_off_time = tegra_cpu_power_off_time();
+
+ ret = tegra_cpudile_init_soc();
+ if (ret)
+ return ret;
+#endif
+
+ for_each_possible_cpu(cpu) {
+ if (tegra_cpuidle_register_device(cpu))
+ pr_err("CPU%u: error initializing idle loop\n", cpu);
+ }
+
+ register_pm_notifier(&tegra_cpuidle_pm_notifier);
+ return 0;
+}
+
+static void __exit tegra_cpuidle_exit(void)
+{
+ unregister_pm_notifier(&tegra_cpuidle_pm_notifier);
+ cpuidle_unregister_driver(&tegra_idle);
+}
+
+module_init(tegra_cpuidle_init);
+module_exit(tegra_cpuidle_exit);
+
+static int lp2_in_idle_set(const char *arg, const struct kernel_param *kp)
+{
+#ifdef CONFIG_PM_SLEEP
+ int ret;
+
+ /* If LP2 in idle is permanently disabled it can't be re-enabled. */
+ if (lp2_in_idle_modifiable) {
+ ret = param_set_bool(arg, kp);
+ return ret;
+ }
+#endif
+ return -ENODEV;
+}
+
+static int lp2_in_idle_get(char *buffer, const struct kernel_param *kp)
+{
+ return param_get_bool(buffer, kp);
+}
+
+static struct kernel_param_ops lp2_in_idle_ops = {
+ .set = lp2_in_idle_set,
+ .get = lp2_in_idle_get,
+};
+module_param_cb(lp2_in_idle, &lp2_in_idle_ops, &lp2_in_idle, 0644);
+
+#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_PM_SLEEP)
+static int tegra_lp2_debug_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, tegra_lp2_debug_show, inode->i_private);
+}
+
+static const struct file_operations tegra_lp2_debug_ops = {
+ .open = tegra_lp2_debug_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int __init tegra_cpuidle_debug_init(void)
+{
+ struct dentry *dir;
+ struct dentry *d;
+
+ dir = debugfs_create_dir("cpuidle", NULL);
+ if (!dir)
+ return -ENOMEM;
+
+ d = debugfs_create_file("lp2", S_IRUGO, dir, NULL,
+ &tegra_lp2_debug_ops);
+ if (!d)
+ return -ENOMEM;
+
+ return 0;
+}
+
+late_initcall(tegra_cpuidle_debug_init);
+#endif
diff --git a/arch/arm/mach-tegra/cpuidle.h b/arch/arm/mach-tegra/cpuidle.h
new file mode 100644
index 000000000000..12a29ff2e236
--- /dev/null
+++ b/arch/arm/mach-tegra/cpuidle.h
@@ -0,0 +1,120 @@
+/*
+ * arch/arm/mach-tegra/cpuidle.h
+ *
+ * Declarations for power state transition code
+ *
+ * Copyright (c) 2011, NVIDIA Corporation.
+ *
+ * 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 __MACH_TEGRA_CPUIDLE_H
+#define __MACH_TEGRA_CPUIDLE_H
+
+#include <linux/cpuidle.h>
+
+#ifdef CONFIG_PM_SLEEP
+
+extern int tegra_lp2_exit_latency;
+
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+void tegra2_idle_lp2(struct cpuidle_device *dev, struct cpuidle_state *state);
+void tegra2_cpu_idle_stats_lp2_ready(unsigned int cpu);
+void tegra2_cpu_idle_stats_lp2_time(unsigned int cpu, s64 us);
+bool tegra2_lp2_is_allowed(struct cpuidle_device *dev,
+ struct cpuidle_state *state);
+#ifdef CONFIG_DEBUG_FS
+int tegra2_lp2_debug_show(struct seq_file *s, void *data);
+#endif
+#endif
+#ifdef CONFIG_ARCH_TEGRA_3x_SOC
+void tegra3_idle_lp2(struct cpuidle_device *dev, struct cpuidle_state *state);
+void tegra3_cpu_idle_stats_lp2_ready(unsigned int cpu);
+void tegra3_cpu_idle_stats_lp2_time(unsigned int cpu, s64 us);
+bool tegra3_lp2_is_allowed(struct cpuidle_device *dev,
+ struct cpuidle_state *state);
+int tegra3_cpudile_init_soc(void);
+#ifdef CONFIG_DEBUG_FS
+int tegra3_lp2_debug_show(struct seq_file *s, void *data);
+#endif
+#endif
+
+static inline int tegra_cpudile_init_soc(void)
+{
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+ return 0;
+#else
+ return tegra3_cpudile_init_soc();
+#endif
+}
+
+static inline void tegra_cpu_idle_stats_lp2_ready(unsigned int cpu)
+{
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+ tegra2_cpu_idle_stats_lp2_ready(cpu);
+#endif
+#ifdef CONFIG_ARCH_TEGRA_3x_SOC
+ tegra3_cpu_idle_stats_lp2_ready(cpu);
+#endif
+}
+
+static inline void tegra_cpu_idle_stats_lp2_time(unsigned int cpu, s64 us)
+{
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+ tegra2_cpu_idle_stats_lp2_time(cpu, us);
+#endif
+#ifdef CONFIG_ARCH_TEGRA_3x_SOC
+ tegra3_cpu_idle_stats_lp2_time(cpu, us);
+#endif
+}
+
+static inline void tegra_idle_lp2(struct cpuidle_device *dev,
+ struct cpuidle_state *state)
+{
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+ tegra2_idle_lp2(dev, state);
+#endif
+#ifdef CONFIG_ARCH_TEGRA_3x_SOC
+ tegra3_idle_lp2(dev, state);
+#endif
+}
+
+static inline bool tegra_lp2_is_allowed(struct cpuidle_device *dev,
+ struct cpuidle_state *state)
+{
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+ return tegra2_lp2_is_allowed(dev, state);
+#endif
+#ifdef CONFIG_ARCH_TEGRA_3x_SOC
+ return tegra3_lp2_is_allowed(dev, state);
+#endif
+}
+
+#ifdef CONFIG_DEBUG_FS
+static inline int tegra_lp2_debug_show(struct seq_file *s, void *data)
+{
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+ return tegra2_lp2_debug_show(s, data);
+#endif
+#ifdef CONFIG_ARCH_TEGRA_3x_SOC
+ return tegra3_lp2_debug_show(s, data);
+#endif
+}
+#endif
+#endif /* CONFIG_PM_SLEEP */
+
+#if defined(CONFIG_CPU_IDLE) && defined(CONFIG_PM_SLEEP)
+void tegra_lp2_in_idle(bool enable);
+#else
+static inline void tegra_lp2_in_idle(bool enable) {}
+#endif
+
+#endif
diff --git a/arch/arm/mach-tegra/csi.c b/arch/arm/mach-tegra/csi.c
new file mode 100644
index 000000000000..3b26c7ae2233
--- /dev/null
+++ b/arch/arm/mach-tegra/csi.c
@@ -0,0 +1,84 @@
+/*
+ * arch/arm/mach-tegra/csi.c
+ *
+ * Copyright (C) 2010-2011 NVIDIA Corporation.
+ *
+ * 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/string.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+
+#include <mach/iomap.h>
+#include <mach/csi.h>
+
+#include "clock.h"
+
+static struct clk *vi_clk;
+static struct clk *csi_clk;
+
+int tegra_vi_csi_writel(u32 val, u32 offset)
+{
+ if (vi_clk == NULL) {
+ vi_clk = tegra_get_clock_by_name("vi");
+ if (IS_ERR_OR_NULL(vi_clk)) {
+ pr_err("vi: can't get vi clock\n");
+ return -EINVAL;
+ }
+ }
+ clk_enable(vi_clk);
+
+ if (csi_clk == NULL) {
+ csi_clk = tegra_get_clock_by_name("csi");
+ if (IS_ERR_OR_NULL(csi_clk)) {
+ pr_err("csi: can't get csi clock\n");
+ return -EINVAL;
+ }
+ }
+ clk_enable(csi_clk);
+
+ writel(val, IO_TO_VIRT(TEGRA_VI_BASE) + offset * 4);
+
+ clk_disable(csi_clk);
+ clk_disable(vi_clk);
+ return 0;
+}
+
+int tegra_vi_csi_readl(u32 offset, u32 *val)
+{
+ if (vi_clk == NULL) {
+ vi_clk = tegra_get_clock_by_name("vi");
+ if (IS_ERR_OR_NULL(vi_clk)) {
+ pr_err("vi: can't get vi clock\n");
+ return -EINVAL;
+ }
+ }
+ clk_enable(vi_clk);
+
+ if (csi_clk == NULL) {
+ csi_clk = tegra_get_clock_by_name("csi");
+ if (IS_ERR_OR_NULL(csi_clk)) {
+ pr_err("csi: can't get csi clock\n");
+ return -EINVAL;
+ }
+ }
+ clk_enable(csi_clk);
+
+ *val = readl(IO_TO_VIRT(TEGRA_VI_BASE) + offset * 4);
+
+ clk_disable(csi_clk);
+ clk_disable(vi_clk);
+
+ return 0;
+}
diff --git a/arch/arm/mach-tegra/delay.S b/arch/arm/mach-tegra/delay.S
new file mode 100644
index 000000000000..76bfafa76ebe
--- /dev/null
+++ b/arch/arm/mach-tegra/delay.S
@@ -0,0 +1,52 @@
+/*
+ * arch/arm/mach-tegra/delay.S
+ *
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * Author:
+ * Colin Cross <ccross@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/linkage.h>
+#include <asm/assembler.h>
+#include <mach/iomap.h>
+#include <mach/io.h>
+
+#include "asm_macros.h"
+
+ .text
+
+ENTRY(__udelay)
+ENTRY(__const_udelay)
+ mov32 r3, (IO_PPSB_VIRT + TEGRA_TMRUS_BASE - IO_PPSB_PHYS)
+ ldr r1, [r3]
+
+/* r0 - usecs to wait
+ * r1 - initial value of the counter
+ */
+loop:
+ ldr r2, [r3]
+ sub r2, r2, r1
+ cmp r2, r0
+ bls loop
+ mov pc, lr
+ENDPROC(__const_udelay)
+ENDPROC(__udelay)
+
+
+@ Delay routine
+ENTRY(__delay)
+ subs r0, r0, #1
+ bhi __delay
+ mov pc, lr
+ENDPROC(__delay)
diff --git a/arch/arm/mach-tegra/devices.c b/arch/arm/mach-tegra/devices.c
index 57e35d20c24c..c38120d119f4 100644
--- a/arch/arm/mach-tegra/devices.c
+++ b/arch/arm/mach-tegra/devices.c
@@ -5,6 +5,8 @@
* Colin Cross <ccross@android.com>
* Erik Gilling <ccross@android.com>
*
+ * Copyright (C) 2010-2011 NVIDIA Corporation.
+ *
* 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.
@@ -24,12 +26,21 @@
#include <linux/serial_8250.h>
#include <linux/i2c-tegra.h>
#include <linux/platform_data/tegra_usb.h>
+#include <linux/tegra_avp.h>
+#include <linux/nvhost.h>
#include <asm/pmu.h>
#include <mach/irqs.h>
#include <mach/iomap.h>
#include <mach/dma.h>
#include <mach/usb_phy.h>
#include "gpio-names.h"
+#include "tegra_smmu.h"
+
+#if !defined(CONFIG_ARCH_TEGRA_2x_SOC)
+#define UART_SOURCE_RATE 408000000
+#else
+#define UART_SOURCE_RATE 216000000
+#endif
static struct resource i2c_resource1[] = {
[0] = {
@@ -70,6 +81,7 @@ static struct resource i2c_resource3[] = {
},
};
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
static struct resource i2c_resource4[] = {
[0] = {
.start = INT_DVC,
@@ -83,20 +95,48 @@ static struct resource i2c_resource4[] = {
},
};
+#else
+static struct resource i2c_resource4[] = {
+ [0] = {
+ .start = INT_I2C4,
+ .end = INT_I2C4,
+ .flags = IORESOURCE_IRQ,
+ },
+ [1] = {
+ .start = TEGRA_I2C4_BASE,
+ .end = TEGRA_I2C4_BASE + TEGRA_I2C4_SIZE-1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct resource i2c_resource5[] = {
+ [0] = {
+ .start = INT_I2C5,
+ .end = INT_I2C5,
+ .flags = IORESOURCE_IRQ,
+ },
+ [1] = {
+ .start = TEGRA_I2C5_BASE,
+ .end = TEGRA_I2C5_BASE + TEGRA_I2C5_SIZE-1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+#endif
+
static struct tegra_i2c_platform_data tegra_i2c1_platform_data = {
- .bus_clk_rate = 400000,
+ .bus_clk_rate = { 400000 },
};
static struct tegra_i2c_platform_data tegra_i2c2_platform_data = {
- .bus_clk_rate = 400000,
+ .bus_clk_rate = { 400000 },
};
static struct tegra_i2c_platform_data tegra_i2c3_platform_data = {
- .bus_clk_rate = 400000,
+ .bus_clk_rate = { 400000 },
};
static struct tegra_i2c_platform_data tegra_dvc_platform_data = {
- .bus_clk_rate = 400000,
+ .bus_clk_rate = { 400000 },
};
struct platform_device tegra_i2c_device1 = {
@@ -139,10 +179,22 @@ struct platform_device tegra_i2c_device4 = {
},
};
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+struct platform_device tegra_i2c_device5 = {
+ .name = "tegra-i2c",
+ .id = 4,
+ .resource = i2c_resource5,
+ .num_resources = ARRAY_SIZE(i2c_resource5),
+ .dev = {
+ .platform_data = 0,
+ },
+};
+#endif
+
static struct resource spi_resource1[] = {
[0] = {
- .start = INT_S_LINK1,
- .end = INT_S_LINK1,
+ .start = INT_SPI_1,
+ .end = INT_SPI_1,
.flags = IORESOURCE_IRQ,
},
[1] = {
@@ -190,6 +242,33 @@ static struct resource spi_resource4[] = {
.flags = IORESOURCE_MEM,
},
};
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+static struct resource spi_resource5[] = {
+ [0] = {
+ .start = INT_SPI_5,
+ .end = INT_SPI_5,
+ .flags = IORESOURCE_IRQ,
+ },
+ [1] = {
+ .start = TEGRA_SPI5_BASE,
+ .end = TEGRA_SPI5_BASE + TEGRA_SPI5_SIZE-1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct resource spi_resource6[] = {
+ [0] = {
+ .start = INT_SPI_6,
+ .end = INT_SPI_6,
+ .flags = IORESOURCE_IRQ,
+ },
+ [1] = {
+ .start = TEGRA_SPI6_BASE,
+ .end = TEGRA_SPI6_BASE + TEGRA_SPI6_SIZE-1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+#endif
struct platform_device tegra_spi_device1 = {
.name = "spi_tegra",
@@ -230,7 +309,118 @@ struct platform_device tegra_spi_device4 = {
.coherent_dma_mask = 0xffffffff,
},
};
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+struct platform_device tegra_spi_device5 = {
+ .name = "spi_tegra",
+ .id = 4,
+ .resource = spi_resource5,
+ .num_resources = ARRAY_SIZE(spi_resource5),
+ .dev = {
+ .coherent_dma_mask = 0xffffffff,
+ },
+};
+
+struct platform_device tegra_spi_device6 = {
+ .name = "spi_tegra",
+ .id = 5,
+ .resource = spi_resource6,
+ .num_resources = ARRAY_SIZE(spi_resource6),
+ .dev = {
+ .coherent_dma_mask = 0xffffffff,
+ },
+};
+#endif
+
+struct platform_device tegra_spi_slave_device1 = {
+ .name = "spi_slave_tegra",
+ .id = 0,
+ .resource = spi_resource1,
+ .num_resources = ARRAY_SIZE(spi_resource1),
+ .dev = {
+ .coherent_dma_mask = 0xffffffff,
+ },
+};
+
+struct platform_device tegra_spi_slave_device2 = {
+ .name = "spi_slave_tegra",
+ .id = 1,
+ .resource = spi_resource2,
+ .num_resources = ARRAY_SIZE(spi_resource2),
+ .dev = {
+ .coherent_dma_mask = 0xffffffff,
+ },
+};
+
+struct platform_device tegra_spi_slave_device3 = {
+ .name = "spi_slave_tegra",
+ .id = 2,
+ .resource = spi_resource3,
+ .num_resources = ARRAY_SIZE(spi_resource3),
+ .dev = {
+ .coherent_dma_mask = 0xffffffff,
+ },
+};
+
+struct platform_device tegra_spi_slave_device4 = {
+ .name = "spi_slave_tegra",
+ .id = 3,
+ .resource = spi_resource4,
+ .num_resources = ARRAY_SIZE(spi_resource4),
+ .dev = {
+ .coherent_dma_mask = 0xffffffff,
+ },
+};
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+struct platform_device tegra_spi_slave_device5 = {
+ .name = "spi_slave_tegra",
+ .id = 4,
+ .resource = spi_resource5,
+ .num_resources = ARRAY_SIZE(spi_resource5),
+ .dev = {
+ .coherent_dma_mask = 0xffffffff,
+ },
+};
+
+struct platform_device tegra_spi_slave_device6 = {
+ .name = "spi_slave_tegra",
+ .id = 5,
+ .resource = spi_resource6,
+ .num_resources = ARRAY_SIZE(spi_resource6),
+ .dev = {
+ .coherent_dma_mask = 0xffffffff,
+ },
+};
+#endif
+
+static struct resource resources_nor[] = {
+ [0] = {
+ .start = INT_SNOR,
+ .end = INT_SNOR,
+ .flags = IORESOURCE_IRQ,
+ },
+ [1] = {
+ /* Map SNOR Controller */
+ .start = TEGRA_SNOR_BASE,
+ .end = TEGRA_SNOR_BASE + TEGRA_SNOR_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [2] = {
+ /* Map the size of flash */
+ .start = TEGRA_NOR_FLASH_BASE,
+ .end = TEGRA_NOR_FLASH_BASE + TEGRA_NOR_FLASH_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ }
+};
+struct platform_device tegra_nor_device = {
+ .name = "tegra-nor",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(resources_nor),
+ .resource = resources_nor,
+ .dev = {
+ .coherent_dma_mask = 0xffffffff,
+ },
+};
static struct resource sdhci_resource1[] = {
[0] = {
@@ -425,6 +615,18 @@ static struct resource tegra_pmu_resources[] = {
.end = INT_CPU1_PMU_INTR,
.flags = IORESOURCE_IRQ,
},
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+ [2] = {
+ .start = INT_CPU2_PMU_INTR,
+ .end = INT_CPU2_PMU_INTR,
+ .flags = IORESOURCE_IRQ,
+ },
+ [3] = {
+ .start = INT_CPU3_PMU_INTR,
+ .end = INT_CPU3_PMU_INTR,
+ .flags = IORESOURCE_IRQ,
+ },
+#endif
};
struct platform_device tegra_pmu_device = {
@@ -549,6 +751,131 @@ struct platform_device tegra_uarte_device = {
},
};
+static struct plat_serial8250_port debug_uarta_platform_data[] = {
+ {
+ .membase = IO_ADDRESS(TEGRA_UARTA_BASE),
+ .mapbase = TEGRA_UARTA_BASE,
+ .irq = INT_UARTA,
+ .flags = UPF_BOOT_AUTOCONF | UPF_FIXED_TYPE,
+ .type = PORT_TEGRA,
+ .iotype = UPIO_MEM,
+ .regshift = 2,
+ .uartclk = UART_SOURCE_RATE,
+ },
+ {
+ .flags = 0,
+ },
+};
+
+static struct plat_serial8250_port debug_uartb_platform_data[] = {
+ {
+ .membase = IO_ADDRESS(TEGRA_UARTB_BASE),
+ .mapbase = TEGRA_UARTB_BASE,
+ .irq = INT_UARTB,
+ .flags = UPF_BOOT_AUTOCONF | UPF_FIXED_TYPE,
+ .type = PORT_TEGRA,
+ .iotype = UPIO_MEM,
+ .regshift = 2,
+ .uartclk = UART_SOURCE_RATE,
+ },
+ {
+ .flags = 0,
+ },
+};
+
+static struct plat_serial8250_port debug_uartc_platform_data[] = {
+ {
+ .membase = IO_ADDRESS(TEGRA_UARTC_BASE),
+ .mapbase = TEGRA_UARTC_BASE,
+ .irq = INT_UARTC,
+ .flags = UPF_BOOT_AUTOCONF | UPF_FIXED_TYPE,
+ .type = PORT_TEGRA,
+ .iotype = UPIO_MEM,
+ .regshift = 2,
+ .uartclk = UART_SOURCE_RATE,
+ },
+ {
+ .flags = 0,
+ },
+};
+
+static struct plat_serial8250_port debug_uartd_platform_data[] = {
+ {
+ .membase = IO_ADDRESS(TEGRA_UARTD_BASE),
+ .mapbase = TEGRA_UARTD_BASE,
+ .irq = INT_UARTD,
+ .flags = UPF_BOOT_AUTOCONF | UPF_FIXED_TYPE,
+ .type = PORT_TEGRA,
+ .iotype = UPIO_MEM,
+ .regshift = 2,
+ .uartclk = UART_SOURCE_RATE,
+ },
+ {
+ .flags = 0,
+ },
+};
+
+#if !defined(CONFIG_ARCH_TEGRA_2x_SOC)
+static struct plat_serial8250_port debug_uarte_platform_data[] = {
+ {
+ .membase = IO_ADDRESS(TEGRA_UARTE_BASE),
+ .mapbase = TEGRA_UARTE_BASE,
+ .irq = INT_UARTE,
+ .flags = UPF_BOOT_AUTOCONF | UPF_FIXED_TYPE,
+ .type = PORT_TEGRA,
+ .iotype = UPIO_MEM,
+ .regshift = 2,
+ .uartclk = UART_SOURCE_RATE,
+ },
+ {
+ .flags = 0,
+ },
+};
+#endif
+
+struct platform_device debug_uarta_device = {
+ .name = "serial8250",
+ .id = PLAT8250_DEV_PLATFORM,
+ .dev = {
+ .platform_data = debug_uarta_platform_data,
+ },
+};
+
+struct platform_device debug_uartb_device = {
+ .name = "serial8250",
+ .id = PLAT8250_DEV_PLATFORM,
+ .dev = {
+ .platform_data = debug_uartb_platform_data,
+ },
+};
+
+struct platform_device debug_uartc_device = {
+ .name = "serial8250",
+ .id = PLAT8250_DEV_PLATFORM,
+ .dev = {
+ .platform_data = debug_uartc_platform_data,
+ },
+};
+
+struct platform_device debug_uartd_device = {
+ .name = "serial8250",
+ .id = PLAT8250_DEV_PLATFORM,
+ .dev = {
+ .platform_data = debug_uartd_platform_data,
+ },
+};
+
+#if !defined(CONFIG_ARCH_TEGRA_2x_SOC)
+struct platform_device debug_uarte_device = {
+ .name = "serial8250",
+ .id = PLAT8250_DEV_PLATFORM,
+ .dev = {
+ .platform_data = debug_uarte_platform_data,
+ },
+};
+#endif
+
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
static struct resource i2s_resource1[] = {
[0] = {
.start = INT_I2S1,
@@ -567,6 +894,13 @@ static struct resource i2s_resource1[] = {
}
};
+struct platform_device tegra_i2s_device1 = {
+ .name = "tegra20-i2s",
+ .id = 0,
+ .resource = i2s_resource1,
+ .num_resources = ARRAY_SIZE(i2s_resource1),
+};
+
static struct resource i2s_resource2[] = {
[0] = {
.start = INT_I2S2,
@@ -585,36 +919,734 @@ static struct resource i2s_resource2[] = {
}
};
-struct platform_device tegra_i2s_device1 = {
- .name = "tegra-i2s",
+struct platform_device tegra_i2s_device2 = {
+ .name = "tegra20-i2s",
+ .id = 1,
+ .resource = i2s_resource2,
+ .num_resources = ARRAY_SIZE(i2s_resource2),
+};
+#else
+static struct resource i2s_resource0[] = {
+ [0] = {
+ .start = TEGRA_I2S0_BASE,
+ .end = TEGRA_I2S0_BASE + TEGRA_I2S0_SIZE - 1,
+ .flags = IORESOURCE_MEM
+ }
+};
+
+struct platform_device tegra_i2s_device0 = {
+ .name = "tegra30-i2s",
.id = 0,
+ .resource = i2s_resource0,
+ .num_resources = ARRAY_SIZE(i2s_resource0),
+};
+
+static struct resource i2s_resource1[] = {
+ [0] = {
+ .start = TEGRA_I2S1_BASE,
+ .end = TEGRA_I2S1_BASE + TEGRA_I2S1_SIZE - 1,
+ .flags = IORESOURCE_MEM
+ }
+};
+
+struct platform_device tegra_i2s_device1 = {
+ .name = "tegra30-i2s",
+ .id = 1,
.resource = i2s_resource1,
.num_resources = ARRAY_SIZE(i2s_resource1),
};
+static struct resource i2s_resource2[] = {
+ [0] = {
+ .start = TEGRA_I2S2_BASE,
+ .end = TEGRA_I2S2_BASE + TEGRA_I2S2_SIZE - 1,
+ .flags = IORESOURCE_MEM
+ }
+};
+
struct platform_device tegra_i2s_device2 = {
- .name = "tegra-i2s",
- .id = 1,
+ .name = "tegra30-i2s",
+ .id = 2,
.resource = i2s_resource2,
.num_resources = ARRAY_SIZE(i2s_resource2),
};
-static struct resource tegra_das_resources[] = {
+static struct resource i2s_resource3[] = {
[0] = {
- .start = TEGRA_APB_MISC_DAS_BASE,
- .end = TEGRA_APB_MISC_DAS_BASE + TEGRA_APB_MISC_DAS_SIZE - 1,
- .flags = IORESOURCE_MEM,
+ .start = TEGRA_I2S3_BASE,
+ .end = TEGRA_I2S3_BASE + TEGRA_I2S3_SIZE - 1,
+ .flags = IORESOURCE_MEM
+ }
+};
+
+struct platform_device tegra_i2s_device3 = {
+ .name = "tegra30-i2s",
+ .id = 3,
+ .resource = i2s_resource3,
+ .num_resources = ARRAY_SIZE(i2s_resource3),
+};
+
+static struct resource i2s_resource4[] = {
+ [0] = {
+ .start = TEGRA_I2S4_BASE,
+ .end = TEGRA_I2S4_BASE + TEGRA_I2S4_SIZE - 1,
+ .flags = IORESOURCE_MEM
+ }
+};
+
+struct platform_device tegra_i2s_device4 = {
+ .name = "tegra30-i2s",
+ .id = 4,
+ .resource = i2s_resource4,
+ .num_resources = ARRAY_SIZE(i2s_resource4),
+};
+#endif
+
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+static struct resource spdif_resource[] = {
+ [0] = {
+ .start = INT_SPDIF,
+ .end = INT_SPDIF,
+ .flags = IORESOURCE_IRQ
+ },
+ [1] = {
+ .start = TEGRA_DMA_REQ_SEL_SPD_I,
+ .end = TEGRA_DMA_REQ_SEL_SPD_I,
+ .flags = IORESOURCE_DMA
},
+ [2] = {
+ .start = TEGRA_SPDIF_BASE,
+ .end = TEGRA_SPDIF_BASE + TEGRA_SPDIF_SIZE - 1,
+ .flags = IORESOURCE_MEM
+ }
};
-struct platform_device tegra_das_device = {
- .name = "tegra-das",
+struct platform_device tegra_spdif_device = {
+ .name = "tegra20-spdif",
+ .id = -1,
+ .resource = spdif_resource,
+ .num_resources = ARRAY_SIZE(spdif_resource),
+};
+#else
+static struct resource spdif_resource[] = {
+ [0] = {
+ .start = TEGRA_SPDIF_BASE,
+ .end = TEGRA_SPDIF_BASE + TEGRA_SPDIF_SIZE - 1,
+ .flags = IORESOURCE_MEM
+ }
+};
+
+struct platform_device tegra_spdif_device = {
+ .name = "tegra30-spdif",
+ .id = -1,
+ .resource = spdif_resource,
+ .num_resources = ARRAY_SIZE(spdif_resource),
+};
+#endif
+
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+static struct resource ahub_resource[] = {
+ [0] = {
+ .start = TEGRA_APBIF0_BASE,
+ .end = TEGRA_APBIF3_BASE + TEGRA_APBIF3_SIZE - 1,
+ .flags = IORESOURCE_MEM
+ },
+ [1] = {
+ .start = TEGRA_AHUB_BASE,
+ .end = TEGRA_AHUB_BASE + TEGRA_AHUB_SIZE - 1,
+ .flags = IORESOURCE_MEM
+ }
+};
+
+struct platform_device tegra_ahub_device = {
+ .name = "tegra30-ahub",
+ .id = -1,
+ .resource = ahub_resource,
+ .num_resources = ARRAY_SIZE(ahub_resource),
+};
+
+static struct resource dam_resource0[] = {
+ [0] = {
+ .start = TEGRA_DAM0_BASE,
+ .end = TEGRA_DAM0_BASE + TEGRA_DAM0_SIZE - 1,
+ .flags = IORESOURCE_MEM
+ }
+};
+
+struct platform_device tegra_dam_device0 = {
+ .name = "tegra30-dam",
+ .id = 0,
+ .resource = dam_resource0,
+ .num_resources = ARRAY_SIZE(dam_resource0),
+};
+
+static struct resource dam_resource1[] = {
+ [0] = {
+ .start = TEGRA_DAM1_BASE,
+ .end = TEGRA_DAM1_BASE + TEGRA_DAM1_SIZE - 1,
+ .flags = IORESOURCE_MEM
+ }
+};
+
+struct platform_device tegra_dam_device1 = {
+ .name = "tegra30-dam",
+ .id = 1,
+ .resource = dam_resource1,
+ .num_resources = ARRAY_SIZE(dam_resource1),
+};
+
+static struct resource dam_resource2[] = {
+ [0] = {
+ .start = TEGRA_DAM2_BASE,
+ .end = TEGRA_DAM2_BASE + TEGRA_DAM2_SIZE - 1,
+ .flags = IORESOURCE_MEM
+ }
+};
+
+struct platform_device tegra_dam_device2 = {
+ .name = "tegra30-dam",
+ .id = 2,
+ .resource = dam_resource2,
+ .num_resources = ARRAY_SIZE(dam_resource2),
+};
+
+static u64 tegra_hda_dma_mask = DMA_BIT_MASK(32);
+static struct resource hda_platform_resources[] = {
+ [0] = {
+ .start = TEGRA_HDA_BASE,
+ .end = TEGRA_HDA_BASE + TEGRA_HDA_SIZE - 1,
+ .flags = IORESOURCE_MEM
+ },
+ [1] = {
+ .start = INT_HDA,
+ .end = INT_HDA,
+ .flags = IORESOURCE_IRQ
+ },
+};
+
+struct platform_device tegra_hda_device = {
+ .name = "tegra30-hda",
.id = -1,
- .num_resources = ARRAY_SIZE(tegra_das_resources),
- .resource = tegra_das_resources,
+ .dev = {
+ .coherent_dma_mask = DMA_BIT_MASK(32),
+ .dma_mask = &tegra_hda_dma_mask,
+ },
+ .resource = hda_platform_resources,
+ .num_resources = ARRAY_SIZE(hda_platform_resources),
+};
+#endif
+
+struct platform_device spdif_dit_device = {
+ .name = "spdif-dit",
+ .id = 0,
+};
+
+struct platform_device bluetooth_dit_device = {
+ .name = "spdif-dit",
+ .id = 1,
+};
+
+struct platform_device baseband_dit_device = {
+ .name = "spdif-dit",
+ .id = 2,
};
struct platform_device tegra_pcm_device = {
.name = "tegra-pcm-audio",
.id = -1,
};
+
+static struct resource w1_resources[] = {
+ [0] = {
+ .start = INT_OWR,
+ .end = INT_OWR,
+ .flags = IORESOURCE_IRQ
+ },
+ [1] = {
+ .start = TEGRA_OWR_BASE,
+ .end = TEGRA_OWR_BASE + TEGRA_OWR_SIZE - 1,
+ .flags = IORESOURCE_MEM
+ }
+};
+
+struct platform_device tegra_w1_device = {
+ .name = "tegra_w1",
+ .id = -1,
+ .resource = w1_resources,
+ .num_resources = ARRAY_SIZE(w1_resources),
+};
+
+static struct resource tegra_udc_resources[] = {
+ [0] = {
+ .start = TEGRA_USB_BASE,
+ .end = TEGRA_USB_BASE + TEGRA_USB_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = INT_USB,
+ .end = INT_USB,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static u64 tegra_udc_dmamask = DMA_BIT_MASK(32);
+
+static struct fsl_usb2_platform_data tegra_udc_pdata = {
+ .operating_mode = FSL_USB2_DR_DEVICE,
+ .phy_mode = FSL_USB2_PHY_UTMI,
+};
+
+struct platform_device tegra_udc_device = {
+ .name = "fsl-tegra-udc",
+ .id = -1,
+ .dev = {
+ .dma_mask = &tegra_udc_dmamask,
+ .coherent_dma_mask = DMA_BIT_MASK(32),
+ .platform_data = &tegra_udc_pdata,
+ },
+ .resource = tegra_udc_resources,
+ .num_resources = ARRAY_SIZE(tegra_udc_resources),
+};
+
+static struct resource tegra_otg_resources[] = {
+ [0] = {
+ .start = TEGRA_USB_BASE,
+ .end = TEGRA_USB_BASE + TEGRA_USB_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = INT_USB,
+ .end = INT_USB,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device tegra_otg_device = {
+ .name = "tegra-otg",
+ .id = -1,
+ .resource = tegra_otg_resources,
+ .num_resources = ARRAY_SIZE(tegra_otg_resources),
+};
+
+#ifdef CONFIG_SATA_AHCI_TEGRA
+static u64 tegra_sata_dma_mask = DMA_BIT_MASK(32);
+
+static struct resource tegra_sata_resources[] = {
+ [0] = {
+ .start = TEGRA_SATA_BAR5_BASE,
+ .end = TEGRA_SATA_BAR5_BASE + TEGRA_SATA_BAR5_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = TEGRA_SATA_CONFIG_BASE,
+ .end = TEGRA_SATA_CONFIG_BASE + TEGRA_SATA_CONFIG_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [2] = {
+ .start = INT_SATA_CTL,
+ .end = INT_SATA_CTL,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device tegra_sata_device = {
+ .name = "tegra-sata",
+ .id = 0,
+ .dev = {
+ .platform_data = 0,
+ .coherent_dma_mask = DMA_BIT_MASK(32),
+ .dma_mask = &tegra_sata_dma_mask,
+ },
+ .resource = tegra_sata_resources,
+ .num_resources = ARRAY_SIZE(tegra_sata_resources),
+};
+#endif
+
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+static struct resource das_resource[] = {
+ [0] = {
+ .start = TEGRA_APB_MISC_DAS_BASE,
+ .end = TEGRA_APB_MISC_DAS_BASE + TEGRA_APB_MISC_DAS_SIZE - 1,
+ .flags = IORESOURCE_MEM
+ }
+};
+
+struct platform_device tegra_das_device = {
+ .name = "tegra20-das",
+ .id = -1,
+ .resource = das_resource,
+ .num_resources = ARRAY_SIZE(das_resource),
+};
+#endif
+
+#if defined(CONFIG_TEGRA_IOVMM_GART)
+static struct resource tegra_gart_resources[] = {
+ [0] = {
+ .name = "mc",
+ .flags = IORESOURCE_MEM,
+ .start = TEGRA_MC_BASE,
+ .end = TEGRA_MC_BASE + TEGRA_MC_SIZE - 1,
+ },
+ [1] = {
+ .name = "gart",
+ .flags = IORESOURCE_MEM,
+ .start = TEGRA_GART_BASE,
+ .end = TEGRA_GART_BASE + TEGRA_GART_SIZE - 1,
+ }
+};
+
+struct platform_device tegra_gart_device = {
+ .name = "tegra_gart",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(tegra_gart_resources),
+ .resource = tegra_gart_resources
+};
+#endif
+
+#if defined(CONFIG_TEGRA_IOVMM_SMMU)
+static struct resource tegra_smmu_resources[] = {
+ [0] = {
+ .name = "mc",
+ .flags = IORESOURCE_MEM,
+ .start = TEGRA_MC_BASE,
+ .end = TEGRA_MC_BASE + TEGRA_MC_SIZE - 1,
+ },
+ [1] = {
+ .name = "ahbarb",
+ .flags = IORESOURCE_MEM,
+ .start = TEGRA_AHB_ARB_BASE,
+ .end = TEGRA_AHB_ARB_BASE + TEGRA_AHB_ARB_SIZE - 1,
+ }
+};
+
+struct platform_device tegra_smmu_device = {
+ .name = "tegra_smmu",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(tegra_smmu_resources),
+ .resource = tegra_smmu_resources
+};
+
+
+static struct tegra_smmu_window tegra_smmu[] = {
+ [0] = {
+ .start = TEGRA_SMMU_BASE,
+ .end = TEGRA_SMMU_BASE + TEGRA_SMMU_SIZE - 1,
+ },
+};
+
+struct tegra_smmu_window *tegra_smmu_window(int wnum)
+{
+ return &tegra_smmu[wnum];
+}
+
+int tegra_smmu_window_count(void)
+{
+ return ARRAY_SIZE(tegra_smmu);
+}
+#endif
+
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
+#define CLK_RESET_RST_SOURCE 0x0
+static struct resource tegra_wdt_resources[] = {
+ [0] = {
+ .start = TEGRA_CLK_RESET_BASE + CLK_RESET_RST_SOURCE,
+ .end = TEGRA_CLK_RESET_BASE + CLK_RESET_RST_SOURCE + 4 - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = TEGRA_TMR1_BASE,
+ .end = TEGRA_TMR1_BASE + TEGRA_TMR1_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [2] = {
+ .start = INT_TMR1,
+ .end = INT_TMR1,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+#else
+static struct resource tegra_wdt_resources[] = {
+ [0] = {
+ .start = TEGRA_WDT0_BASE,
+ .end = TEGRA_WDT0_BASE + TEGRA_WDT0_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = TEGRA_TMR10_BASE,
+ .end = TEGRA_TMR10_BASE + TEGRA_TMR10_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [2] = {
+ .start = INT_WDT_CPU,
+ .end = INT_WDT_CPU,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+#endif
+
+struct platform_device tegra_wdt_device = {
+ .name = "tegra_wdt",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(tegra_wdt_resources),
+ .resource = tegra_wdt_resources,
+};
+
+static struct resource tegra_pwfm0_resource = {
+ .start = TEGRA_PWFM0_BASE,
+ .end = TEGRA_PWFM0_BASE + TEGRA_PWFM0_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+};
+
+static struct resource tegra_pwfm1_resource = {
+ .start = TEGRA_PWFM1_BASE,
+ .end = TEGRA_PWFM1_BASE + TEGRA_PWFM1_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+};
+
+static struct resource tegra_pwfm2_resource = {
+ .start = TEGRA_PWFM2_BASE,
+ .end = TEGRA_PWFM2_BASE + TEGRA_PWFM2_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+};
+
+static struct resource tegra_pwfm3_resource = {
+ .start = TEGRA_PWFM3_BASE,
+ .end = TEGRA_PWFM3_BASE + TEGRA_PWFM3_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+};
+
+struct platform_device tegra_pwfm0_device = {
+ .name = "tegra_pwm",
+ .id = 0,
+ .num_resources = 1,
+ .resource = &tegra_pwfm0_resource,
+};
+
+struct platform_device tegra_pwfm1_device = {
+ .name = "tegra_pwm",
+ .id = 1,
+ .num_resources = 1,
+ .resource = &tegra_pwfm1_resource,
+};
+
+struct platform_device tegra_pwfm2_device = {
+ .name = "tegra_pwm",
+ .id = 2,
+ .num_resources = 1,
+ .resource = &tegra_pwfm2_resource,
+};
+
+struct platform_device tegra_pwfm3_device = {
+ .name = "tegra_pwm",
+ .id = 3,
+ .num_resources = 1,
+ .resource = &tegra_pwfm3_resource,
+};
+
+static struct resource tegra_grhost_resources[] = {
+ {
+ .start = TEGRA_HOST1X_BASE,
+ .end = TEGRA_HOST1X_BASE + TEGRA_HOST1X_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = TEGRA_DISPLAY_BASE,
+ .end = TEGRA_DISPLAY_BASE + TEGRA_DISPLAY_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = TEGRA_DISPLAY2_BASE,
+ .end = TEGRA_DISPLAY2_BASE + TEGRA_DISPLAY2_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = TEGRA_VI_BASE,
+ .end = TEGRA_VI_BASE + TEGRA_VI_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = TEGRA_ISP_BASE,
+ .end = TEGRA_ISP_BASE + TEGRA_ISP_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = TEGRA_MPE_BASE,
+ .end = TEGRA_MPE_BASE + TEGRA_MPE_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = INT_SYNCPT_THRESH_BASE,
+ .end = INT_SYNCPT_THRESH_BASE + INT_SYNCPT_THRESH_NR - 1,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .start = INT_HOST1X_MPCORE_GENERAL,
+ .end = INT_HOST1X_MPCORE_GENERAL,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device tegra_grhost_device = {
+ .name = "tegra_grhost",
+ .id = -1,
+ .resource = tegra_grhost_resources,
+ .num_resources = ARRAY_SIZE(tegra_grhost_resources),
+};
+
+static struct tegra_avp_platform_data tegra_avp_pdata = {
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+ .emc_clk_rate = ULONG_MAX,
+#else
+ .emc_clk_rate = 200000000,
+#endif
+};
+
+struct resource tegra_nvavp_resources[] = {
+ [0] = {
+ .start = INT_SHR_SEM_INBOX_IBF,
+ .end = INT_SHR_SEM_INBOX_IBF,
+ .flags = IORESOURCE_IRQ,
+ .name = "mbox_from_nvavp_pending",
+ },
+};
+
+struct nvhost_device nvavp_device = {
+ .name = "nvavp",
+ .id = -1,
+ .resource = tegra_nvavp_resources,
+ .num_resources = ARRAY_SIZE(tegra_nvavp_resources),
+};
+
+static struct resource tegra_avp_resources[] = {
+ [0] = {
+ .start = INT_SHR_SEM_INBOX_IBF,
+ .end = INT_SHR_SEM_INBOX_IBF,
+ .flags = IORESOURCE_IRQ,
+ .name = "mbox_from_avp_pending",
+ },
+};
+
+struct platform_device tegra_avp_device = {
+ .name = "tegra-avp",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(tegra_avp_resources),
+ .resource = tegra_avp_resources,
+ .dev = {
+ .coherent_dma_mask = 0xffffffffULL,
+ .platform_data = &tegra_avp_pdata,
+ },
+};
+
+static struct resource tegra_aes_resources[] = {
+ {
+ .start = TEGRA_VDE_BASE,
+ .end = TEGRA_VDE_BASE + TEGRA_VDE_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = TEGRA_BSEA_BASE,
+ .end = TEGRA_BSEA_BASE + TEGRA_BSEA_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static u64 tegra_aes_dma_mask = DMA_BIT_MASK(32);
+
+struct platform_device tegra_aes_device = {
+ .name = "tegra-aes",
+ .id = -1,
+ .resource = tegra_aes_resources,
+ .num_resources = ARRAY_SIZE(tegra_aes_resources),
+ .dev = {
+ .dma_mask = &tegra_aes_dma_mask,
+ .coherent_dma_mask = DMA_BIT_MASK(32),
+ },
+};
+
+static struct resource tegra_kbc_resources[] = {
+ [0] = {
+ .start = TEGRA_KBC_BASE,
+ .end = TEGRA_KBC_BASE + TEGRA_KBC_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = INT_KBC,
+ .end = INT_KBC,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device tegra_kbc_device = {
+ .name = "tegra-kbc",
+ .id = -1,
+ .resource = tegra_kbc_resources,
+ .num_resources = ARRAY_SIZE(tegra_kbc_resources),
+ .dev = {
+ .platform_data = 0,
+ },
+};
+
+#if defined(CONFIG_ARCH_TEGRA_3x_SOC)
+static struct resource tegra_tsensor_resources[]= {
+ {
+ .start = TEGRA_TSENSOR_BASE,
+ .end = TEGRA_TSENSOR_BASE + TEGRA_TSENSOR_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = INT_TSENSOR,
+ .end = INT_TSENSOR,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .start = TEGRA_PMC_BASE + 0x1B0,
+ /* 2 pmc registers mapped */
+ .end = TEGRA_PMC_BASE + 0x1B0 + (2 * 4),
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+struct platform_device tegra_tsensor_device = {
+ .name = "tegra-tsensor",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(tegra_tsensor_resources),
+ .resource = tegra_tsensor_resources,
+ .dev = {
+ .platform_data = 0,
+ },
+};
+#endif
+
+#if !defined(CONFIG_ARCH_TEGRA_2x_SOC)
+static u64 tegra_se_dma_mask = DMA_BIT_MASK(32);
+
+struct resource tegra_se_resources[] = {
+ [0] = {
+ .start = TEGRA_SE_BASE,
+ .end = TEGRA_SE_BASE + TEGRA_SE_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = TEGRA_PMC_BASE,
+ .end = TEGRA_PMC_BASE + SZ_256 - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [2] = {
+ .start = INT_SE,
+ .end = INT_SE,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device tegra_se_device = {
+ .name = "tegra-se",
+ .id = -1,
+ .dev = {
+ .coherent_dma_mask = DMA_BIT_MASK(32),
+ .dma_mask = &tegra_se_dma_mask,
+ },
+ .resource = tegra_se_resources,
+ .num_resources = ARRAY_SIZE(tegra_se_resources),
+};
+#endif
+
diff --git a/arch/arm/mach-tegra/devices.h b/arch/arm/mach-tegra/devices.h
index 4a7dc0a097d6..e3ece52c8056 100644
--- a/arch/arm/mach-tegra/devices.h
+++ b/arch/arm/mach-tegra/devices.h
@@ -5,6 +5,8 @@
* Colin Cross <ccross@android.com>
* Erik Gilling <ccross@android.com>
*
+ * Copyright (C) 2010-2011 NVIDIA Corporation.
+ *
* 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.
@@ -29,10 +31,24 @@ extern struct platform_device tegra_i2c_device1;
extern struct platform_device tegra_i2c_device2;
extern struct platform_device tegra_i2c_device3;
extern struct platform_device tegra_i2c_device4;
+extern struct platform_device tegra_kbc_device;
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+extern struct platform_device tegra_i2c_device5;
+#endif
extern struct platform_device tegra_spi_device1;
extern struct platform_device tegra_spi_device2;
extern struct platform_device tegra_spi_device3;
extern struct platform_device tegra_spi_device4;
+extern struct platform_device tegra_spi_slave_device1;
+extern struct platform_device tegra_spi_slave_device2;
+extern struct platform_device tegra_spi_slave_device3;
+extern struct platform_device tegra_spi_slave_device4;
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+extern struct platform_device tegra_spi_device5;
+extern struct platform_device tegra_spi_device6;
+extern struct platform_device tegra_spi_slave_device5;
+extern struct platform_device tegra_spi_slave_device6;
+#endif
extern struct platform_device tegra_ehci1_device;
extern struct platform_device tegra_ehci2_device;
extern struct platform_device tegra_ehci3_device;
@@ -44,7 +60,65 @@ extern struct platform_device tegra_uarte_device;
extern struct platform_device tegra_pmu_device;
extern struct platform_device tegra_i2s_device1;
extern struct platform_device tegra_i2s_device2;
+extern struct platform_device tegra_spdif_device;
extern struct platform_device tegra_das_device;
+extern struct platform_device spdif_dit_device;
+extern struct platform_device bluetooth_dit_device;
+extern struct platform_device baseband_dit_device;
extern struct platform_device tegra_pcm_device;
+extern struct platform_device tegra_w1_device;
+extern struct platform_device tegra_udc_device;
+extern struct platform_device tegra_ehci1_device;
+extern struct platform_device tegra_ehci2_device;
+extern struct platform_device tegra_ehci3_device;
+extern struct platform_device tegra_i2s_device1;
+extern struct platform_device tegra_i2s_device2;
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+extern struct platform_device tegra_i2s_device0;
+extern struct platform_device tegra_i2s_device3;
+extern struct platform_device tegra_i2s_device4;
+extern struct platform_device tegra_ahub_device;
+extern struct platform_device tegra_apbif0_device;
+extern struct platform_device tegra_apbif1_device;
+extern struct platform_device tegra_apbif2_device;
+extern struct platform_device tegra_apbif3_device;
+extern struct platform_device tegra_dam_device0;
+extern struct platform_device tegra_dam_device1;
+extern struct platform_device tegra_dam_device2;
+extern struct platform_device tegra_hda_device;
+extern struct platform_device tegra_sata_device;
+#endif
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
+extern struct platform_device tegra_gart_device;
+#else
+extern struct platform_device tegra_smmu_device;
+#endif
+extern struct platform_device tegra_wdt_device;
+extern struct platform_device tegra_pwfm0_device;
+extern struct platform_device tegra_pwfm1_device;
+extern struct platform_device tegra_pwfm2_device;
+extern struct platform_device tegra_pwfm3_device;
+extern struct platform_device tegra_otg_device;
+extern struct platform_device tegra_uarta_device;
+extern struct platform_device tegra_uartb_device;
+extern struct platform_device tegra_uartc_device;
+extern struct platform_device tegra_uartd_device;
+extern struct platform_device tegra_uarte_device;
+extern struct platform_device tegra_grhost_device;
+extern struct platform_device tegra_avp_device;
+extern struct nvhost_device nvavp_device;
+extern struct platform_device tegra_aes_device;
+#if !defined(CONFIG_ARCH_TEGRA_2x_SOC)
+extern struct platform_device tegra_tsensor_device;
+#endif
+extern struct platform_device tegra_nor_device;
+extern struct platform_device debug_uarta_device;
+extern struct platform_device debug_uartb_device;
+extern struct platform_device debug_uartc_device;
+extern struct platform_device debug_uartd_device;
+#if !defined(CONFIG_ARCH_TEGRA_2x_SOC)
+extern struct platform_device tegra_se_device;
+extern struct platform_device debug_uarte_device;
+#endif
#endif
diff --git a/arch/arm/mach-tegra/dma.c b/arch/arm/mach-tegra/dma.c
index f4ef5eb317bd..c36c31d6ec01 100644
--- a/arch/arm/mach-tegra/dma.c
+++ b/arch/arm/mach-tegra/dma.c
@@ -3,7 +3,7 @@
*
* System DMA driver for NVIDIA Tegra SoCs
*
- * Copyright (c) 2008-2009, NVIDIA Corporation.
+ * Copyright (c) 2008-2011, NVIDIA Corporation.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -28,10 +28,11 @@
#include <linux/irq.h>
#include <linux/delay.h>
#include <linux/clk.h>
+#include <linux/syscore_ops.h>
#include <mach/dma.h>
#include <mach/irqs.h>
#include <mach/iomap.h>
-#include <mach/suspend.h>
+#include <mach/clk.h>
#define APB_DMA_GEN 0x000
#define GEN_ENABLE (1<<31)
@@ -51,7 +52,6 @@
#define CSR_FLOW (1<<21)
#define CSR_REQ_SEL_SHIFT 16
#define CSR_REQ_SEL_MASK (0x1F<<CSR_REQ_SEL_SHIFT)
-#define CSR_REQ_SEL_INVALID (31<<CSR_REQ_SEL_SHIFT)
#define CSR_WCOUNT_SHIFT 2
#define CSR_WCOUNT_MASK 0xFFFC
@@ -97,14 +97,17 @@
#define APB_SEQ_WRAP_SHIFT 16
#define APB_SEQ_WRAP_MASK (0x7<<APB_SEQ_WRAP_SHIFT)
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
#define TEGRA_SYSTEM_DMA_CH_NR 16
+#else
+#define TEGRA_SYSTEM_DMA_CH_NR 32
+#endif
#define TEGRA_SYSTEM_DMA_AVP_CH_NUM 4
#define TEGRA_SYSTEM_DMA_CH_MIN 0
#define TEGRA_SYSTEM_DMA_CH_MAX \
(TEGRA_SYSTEM_DMA_CH_NR - TEGRA_SYSTEM_DMA_AVP_CH_NUM - 1)
-#define NV_DMA_MAX_TRASFER_SIZE 0x10000
-
+static struct clk *dma_clk;
const unsigned int ahb_addr_wrap_table[8] = {
0, 32, 64, 128, 256, 512, 1024, 2048
};
@@ -119,6 +122,7 @@ struct tegra_dma_channel {
int id;
spinlock_t lock;
char name[TEGRA_DMA_NAME_SIZE];
+ char client_name[TEGRA_DMA_NAME_SIZE];
void __iomem *addr;
int mode;
int irq;
@@ -129,6 +133,7 @@ struct tegra_dma_channel {
static bool tegra_dma_initialized;
static DEFINE_MUTEX(tegra_dma_lock);
+static DEFINE_SPINLOCK(enable_lock);
static DECLARE_BITMAP(channel_usage, NV_DMA_MAX_CHANNELS);
static struct tegra_dma_channel dma_channels[NV_DMA_MAX_CHANNELS];
@@ -174,91 +179,126 @@ void tegra_dma_stop(struct tegra_dma_channel *ch)
writel(status, ch->addr + APB_DMA_CHAN_STA);
}
+bool tegra_dma_is_stopped(struct tegra_dma_channel *ch)
+{
+ return !!(readl(ch->addr + APB_DMA_CHAN_STA) & CSR_ENB);
+}
+
int tegra_dma_cancel(struct tegra_dma_channel *ch)
{
- u32 csr;
unsigned long irq_flags;
spin_lock_irqsave(&ch->lock, irq_flags);
while (!list_empty(&ch->list))
list_del(ch->list.next);
- csr = readl(ch->addr + APB_DMA_CHAN_CSR);
- csr &= ~CSR_REQ_SEL_MASK;
- csr |= CSR_REQ_SEL_INVALID;
- writel(csr, ch->addr + APB_DMA_CHAN_CSR);
-
tegra_dma_stop(ch);
spin_unlock_irqrestore(&ch->lock, irq_flags);
return 0;
}
+EXPORT_SYMBOL(tegra_dma_cancel);
-int tegra_dma_dequeue_req(struct tegra_dma_channel *ch,
- struct tegra_dma_req *_req)
+static unsigned int get_channel_status(struct tegra_dma_channel *ch,
+ struct tegra_dma_req *req, bool is_stop_dma)
{
- unsigned int csr;
+ void __iomem *addr = IO_ADDRESS(TEGRA_APB_DMA_BASE);
unsigned int status;
- struct tegra_dma_req *req = NULL;
- int found = 0;
- unsigned long irq_flags;
- int to_transfer;
- int req_transfer_count;
- spin_lock_irqsave(&ch->lock, irq_flags);
- list_for_each_entry(req, &ch->list, node) {
- if (req == _req) {
- list_del(&req->node);
- found = 1;
- break;
+ if (is_stop_dma) {
+ /* STOP the DMA and get the transfer count.
+ * Getting the transfer count is tricky.
+ * - Globally disable DMA on all channels
+ * - Read the channel's status register to know the number
+ * of pending bytes to be transfered.
+ * - Stop the dma channel
+ * - Globally re-enable DMA to resume other transfers
+ */
+ spin_lock(&enable_lock);
+ writel(0, addr + APB_DMA_GEN);
+ udelay(20);
+ status = readl(ch->addr + APB_DMA_CHAN_STA);
+ tegra_dma_stop(ch);
+ writel(GEN_ENABLE, addr + APB_DMA_GEN);
+ spin_unlock(&enable_lock);
+ if (status & STA_ISE_EOC) {
+ pr_err("Got Dma Int here clearing");
+ writel(status, ch->addr + APB_DMA_CHAN_STA);
}
+ req->status = TEGRA_DMA_REQ_ERROR_ABORTED;
+ } else {
+ status = readl(ch->addr + APB_DMA_CHAN_STA);
}
- if (!found) {
- spin_unlock_irqrestore(&ch->lock, irq_flags);
- return 0;
- }
+ return status;
+}
- /* STOP the DMA and get the transfer count.
- * Getting the transfer count is tricky.
- * - Change the source selector to invalid to stop the DMA from
- * FIFO to memory.
- * - Read the status register to know the number of pending
- * bytes to be transferred.
- * - Finally stop or program the DMA to the next buffer in the
- * list.
- */
- csr = readl(ch->addr + APB_DMA_CHAN_CSR);
- csr &= ~CSR_REQ_SEL_MASK;
- csr |= CSR_REQ_SEL_INVALID;
- writel(csr, ch->addr + APB_DMA_CHAN_CSR);
+/* should be called with the channel lock held */
+static unsigned int dma_active_count(struct tegra_dma_channel *ch,
+ struct tegra_dma_req *req, unsigned int status)
+{
+ unsigned int to_transfer;
+ unsigned int req_transfer_count;
+
+ unsigned int bytes_transferred;
- /* Get the transfer count */
- status = readl(ch->addr + APB_DMA_CHAN_STA);
to_transfer = (status & STA_COUNT_MASK) >> STA_COUNT_SHIFT;
req_transfer_count = ch->req_transfer_count;
req_transfer_count += 1;
to_transfer += 1;
- req->bytes_transferred = req_transfer_count;
+ bytes_transferred = req_transfer_count;
if (status & STA_BUSY)
- req->bytes_transferred -= to_transfer;
+ bytes_transferred -= to_transfer;
/* In continuous transfer mode, DMA only tracks the count of the
* half DMA buffer. So, if the DMA already finished half the DMA
* then add the half buffer to the completed count.
- *
- * FIXME: There can be a race here. What if the req to
- * dequue happens at the same time as the DMA just moved to
- * the new buffer and SW didn't yet received the interrupt?
*/
- if (ch->mode & TEGRA_DMA_MODE_CONTINOUS)
+ if (ch->mode & TEGRA_DMA_MODE_CONTINUOUS_DOUBLE)
if (req->buffer_status == TEGRA_DMA_REQ_BUF_STATUS_HALF_FULL)
- req->bytes_transferred += req_transfer_count;
+ bytes_transferred += req_transfer_count;
- req->bytes_transferred *= 4;
+ if (status & STA_ISE_EOC)
+ bytes_transferred += req_transfer_count;
+
+ bytes_transferred *= 4;
+
+ return bytes_transferred;
+}
+
+int tegra_dma_dequeue_req(struct tegra_dma_channel *ch,
+ struct tegra_dma_req *_req)
+{
+ struct tegra_dma_req *req = NULL;
+ int found = 0;
+ unsigned int status;
+ unsigned long irq_flags;
+ int stop = 0;
+
+ spin_lock_irqsave(&ch->lock, irq_flags);
+
+ if (list_entry(ch->list.next, struct tegra_dma_req, node) == _req)
+ stop = 1;
+
+ list_for_each_entry(req, &ch->list, node) {
+ if (req == _req) {
+ list_del(&req->node);
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ spin_unlock_irqrestore(&ch->lock, irq_flags);
+ return 0;
+ }
+
+ if (!stop)
+ goto skip_status;
+
+ status = get_channel_status(ch, req, true);
+ req->bytes_transferred = dma_active_count(ch, req, status);
- tegra_dma_stop(ch);
if (!list_empty(&ch->list)) {
/* if the list is not empty, queue the next request */
struct tegra_dma_req *next_req;
@@ -266,6 +306,7 @@ int tegra_dma_dequeue_req(struct tegra_dma_channel *ch,
typeof(*next_req), node);
tegra_dma_update_hw(ch, next_req);
}
+skip_status:
req->status = -TEGRA_DMA_REQ_ERROR_ABORTED;
spin_unlock_irqrestore(&ch->lock, irq_flags);
@@ -308,6 +349,36 @@ bool tegra_dma_is_req_inflight(struct tegra_dma_channel *ch,
return false;
}
EXPORT_SYMBOL(tegra_dma_is_req_inflight);
+int tegra_dma_get_transfer_count(struct tegra_dma_channel *ch,
+ struct tegra_dma_req *req, bool is_stop_dma)
+{
+ unsigned int status;
+ unsigned long irq_flags;
+ int bytes_transferred = 0;
+
+ if (IS_ERR_OR_NULL(ch))
+ BUG();
+
+ spin_lock_irqsave(&ch->lock, irq_flags);
+
+ if (list_entry(ch->list.next, struct tegra_dma_req, node) != req) {
+ spin_unlock_irqrestore(&ch->lock, irq_flags);
+ pr_debug("The dma request is not the head req\n");
+ return req->bytes_transferred;
+ }
+
+ if (req->status != TEGRA_DMA_REQ_INFLIGHT) {
+ spin_unlock_irqrestore(&ch->lock, irq_flags);
+ pr_debug("The dma request is not running\n");
+ return req->bytes_transferred;
+ }
+
+ status = get_channel_status(ch, req, is_stop_dma);
+ bytes_transferred = dma_active_count(ch, req, status);
+ spin_unlock_irqrestore(&ch->lock, irq_flags);
+ return bytes_transferred;
+}
+EXPORT_SYMBOL(tegra_dma_get_transfer_count);
int tegra_dma_enqueue_req(struct tegra_dma_channel *ch,
struct tegra_dma_req *req)
@@ -316,7 +387,7 @@ int tegra_dma_enqueue_req(struct tegra_dma_channel *ch,
struct tegra_dma_req *_req;
int start_dma = 0;
- if (req->size > NV_DMA_MAX_TRASFER_SIZE ||
+ if (req->size > TEGRA_DMA_MAX_TRANSFER_SIZE ||
req->source_addr & 0x3 || req->dest_addr & 0x3) {
pr_err("Invalid DMA request for channel %d\n", ch->id);
return -EINVAL;
@@ -326,14 +397,15 @@ int tegra_dma_enqueue_req(struct tegra_dma_channel *ch,
list_for_each_entry(_req, &ch->list, node) {
if (req == _req) {
- spin_unlock_irqrestore(&ch->lock, irq_flags);
- return -EEXIST;
+ spin_unlock_irqrestore(&ch->lock, irq_flags);
+ return -EEXIST;
}
}
req->bytes_transferred = 0;
req->status = 0;
- req->buffer_status = 0;
+ /* STATUS_EMPTY just means the DMA hasn't processed the buf yet. */
+ req->buffer_status = TEGRA_DMA_REQ_BUF_STATUS_EMPTY;
if (list_empty(&ch->list))
start_dma = 1;
@@ -341,6 +413,34 @@ int tegra_dma_enqueue_req(struct tegra_dma_channel *ch,
if (start_dma)
tegra_dma_update_hw(ch, req);
+ /* Check to see if this request needs to be pushed immediately.
+ * For continuous single-buffer DMA:
+ * The first buffer is always in-flight. The 2nd buffer should
+ * also be in-flight. The 3rd buffer becomes in-flight when the
+ * first is completed in the interrupt.
+ */
+ else if (ch->mode & TEGRA_DMA_MODE_CONTINUOUS_SINGLE) {
+ struct tegra_dma_req *first_req, *second_req;
+ first_req = list_entry(ch->list.next,
+ typeof(*first_req), node);
+ second_req = list_entry(first_req->node.next,
+ typeof(*second_req), node);
+ if (second_req == req) {
+ unsigned long status =
+ readl(ch->addr + APB_DMA_CHAN_STA);
+ if (!(status & STA_ISE_EOC))
+ tegra_dma_update_hw_partial(ch, req);
+ /* Handle the case where the IRQ fired while we're
+ * writing the interrupts.
+ */
+ if (status & STA_ISE_EOC) {
+ /* Interrupt fired, let the IRQ stop/restart
+ * the DMA with this buffer in a clean way.
+ */
+ req->status = TEGRA_DMA_REQ_SUCCESS;
+ }
+ }
+ }
spin_unlock_irqrestore(&ch->lock, irq_flags);
@@ -348,10 +448,23 @@ int tegra_dma_enqueue_req(struct tegra_dma_channel *ch,
}
EXPORT_SYMBOL(tegra_dma_enqueue_req);
-struct tegra_dma_channel *tegra_dma_allocate_channel(int mode)
+static void tegra_dma_dump_channel_usage(void)
+{
+ int i;
+ pr_info("DMA channel allocation dump:\n");
+ for (i = TEGRA_SYSTEM_DMA_CH_MIN; i <= TEGRA_SYSTEM_DMA_CH_MAX; i++) {
+ struct tegra_dma_channel *ch = &dma_channels[i];
+ pr_warn("dma %d used by %s\n", i, ch->client_name);
+ }
+ return;
+}
+
+struct tegra_dma_channel *tegra_dma_allocate_channel(int mode,
+ const char namefmt[], ...)
{
int channel;
struct tegra_dma_channel *ch = NULL;
+ va_list args;
if (WARN_ON(!tegra_dma_initialized))
return NULL;
@@ -364,12 +477,18 @@ struct tegra_dma_channel *tegra_dma_allocate_channel(int mode)
} else {
channel = find_first_zero_bit(channel_usage,
ARRAY_SIZE(dma_channels));
- if (channel >= ARRAY_SIZE(dma_channels))
+ if (channel >= ARRAY_SIZE(dma_channels)) {
+ tegra_dma_dump_channel_usage();
goto out;
+ }
}
__set_bit(channel, channel_usage);
ch = &dma_channels[channel];
ch->mode = mode;
+ va_start(args, namefmt);
+ vsnprintf(ch->client_name, sizeof(ch->client_name),
+ namefmt, args);
+ va_end(args);
out:
mutex_unlock(&tegra_dma_lock);
@@ -384,6 +503,7 @@ void tegra_dma_free_channel(struct tegra_dma_channel *ch)
tegra_dma_cancel(ch);
mutex_lock(&tegra_dma_lock);
__clear_bit(ch->id, channel_usage);
+ memset(ch->client_name, 0, sizeof(ch->client_name));
mutex_unlock(&tegra_dma_lock);
}
EXPORT_SYMBOL(tegra_dma_free_channel);
@@ -393,6 +513,7 @@ static void tegra_dma_update_hw_partial(struct tegra_dma_channel *ch,
{
u32 apb_ptr;
u32 ahb_ptr;
+ u32 csr;
if (req->to_memory) {
apb_ptr = req->source_addr;
@@ -404,6 +525,15 @@ static void tegra_dma_update_hw_partial(struct tegra_dma_channel *ch,
writel(apb_ptr, ch->addr + APB_DMA_CHAN_APB_PTR);
writel(ahb_ptr, ch->addr + APB_DMA_CHAN_AHB_PTR);
+ if (ch->mode & TEGRA_DMA_MODE_CONTINUOUS_DOUBLE)
+ ch->req_transfer_count = (req->size >> 3) - 1;
+ else
+ ch->req_transfer_count = (req->size >> 2) - 1;
+ csr = readl(ch->addr + APB_DMA_CHAN_CSR);
+ csr &= ~CSR_WCOUNT_MASK;
+ csr |= ch->req_transfer_count << CSR_WCOUNT_SHIFT;
+ writel(csr, ch->addr + APB_DMA_CHAN_CSR);
+
req->status = TEGRA_DMA_REQ_INFLIGHT;
return;
}
@@ -424,26 +554,70 @@ static void tegra_dma_update_hw(struct tegra_dma_channel *ch,
u32 csr;
csr = CSR_IE_EOC | CSR_FLOW;
- ahb_seq = AHB_SEQ_INTR_ENB | AHB_SEQ_BURST_1;
+ ahb_seq = AHB_SEQ_INTR_ENB;
+
+ switch (req->req_sel) {
+ case TEGRA_DMA_REQ_SEL_SL2B1:
+ case TEGRA_DMA_REQ_SEL_SL2B2:
+ case TEGRA_DMA_REQ_SEL_SL2B3:
+ case TEGRA_DMA_REQ_SEL_SL2B4:
+#if !defined(CONFIG_ARCH_TEGRA_2x_SOC)
+ case TEGRA_DMA_REQ_SEL_SL2B5:
+ case TEGRA_DMA_REQ_SEL_SL2B6:
+ case TEGRA_DMA_REQ_SEL_APBIF_CH0:
+ case TEGRA_DMA_REQ_SEL_APBIF_CH1:
+ case TEGRA_DMA_REQ_SEL_APBIF_CH2:
+ case TEGRA_DMA_REQ_SEL_APBIF_CH3:
+#endif
+ case TEGRA_DMA_REQ_SEL_SPI:
+ /* For spi/slink the burst size based on transfer size
+ * i.e. if multiple of 32 bytes then busrt is
+ * 8 word else if multiple of 16 bytes then burst is
+ * 4 word else burst size is 1 word */
+ if (req->size & 0xF)
+ ahb_seq |= AHB_SEQ_BURST_1;
+ else if ((req->size >> 4) & 0x1)
+ ahb_seq |= AHB_SEQ_BURST_4;
+ else
+ ahb_seq |= AHB_SEQ_BURST_8;
+ break;
+
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
+ case TEGRA_DMA_REQ_SEL_I2S_2:
+ case TEGRA_DMA_REQ_SEL_I2S_1:
+ case TEGRA_DMA_REQ_SEL_SPD_I:
+ case TEGRA_DMA_REQ_SEL_UI_I:
+ case TEGRA_DMA_REQ_SEL_I2S2_2:
+ case TEGRA_DMA_REQ_SEL_I2S2_1:
+ /* For ARCH_2x i2s/spdif burst size is 4 word */
+ ahb_seq |= AHB_SEQ_BURST_4;
+ break;
+#endif
+
+ default:
+ ahb_seq |= AHB_SEQ_BURST_1;
+ break;
+ }
+
apb_seq = 0;
csr |= req->req_sel << CSR_REQ_SEL_SHIFT;
- /* One shot mode is always single buffered,
- * continuous mode is always double buffered
- * */
+ ch->req_transfer_count = (req->size >> 2) - 1;
+
+ /* One shot mode is always single buffered. Continuous mode could
+ * support either.
+ */
if (ch->mode & TEGRA_DMA_MODE_ONESHOT) {
csr |= CSR_ONCE;
- ch->req_transfer_count = (req->size >> 2) - 1;
- } else {
+ } else if (ch->mode & TEGRA_DMA_MODE_CONTINUOUS_DOUBLE) {
ahb_seq |= AHB_SEQ_DBL_BUF;
-
- /* In double buffered mode, we set the size to half the
- * requested size and interrupt when half the buffer
- * is full */
+ /* We want an interrupt halfway through, then on the
+ * completion. The double buffer means 2 interrupts
+ * pass before the DMA HW latches a new AHB_PTR etc.
+ */
ch->req_transfer_count = (req->size >> 3) - 1;
}
-
csr |= ch->req_transfer_count << CSR_WCOUNT_SHIFT;
if (req->to_memory) {
@@ -528,14 +702,8 @@ static void handle_oneshot_dma(struct tegra_dma_channel *ch)
req = list_entry(ch->list.next, typeof(*req), node);
if (req) {
- int bytes_transferred;
-
- bytes_transferred = ch->req_transfer_count;
- bytes_transferred += 1;
- bytes_transferred <<= 2;
-
list_del(&req->node);
- req->bytes_transferred = bytes_transferred;
+ req->bytes_transferred = req->size;
req->status = TEGRA_DMA_REQ_SUCCESS;
spin_unlock_irqrestore(&ch->lock, irq_flags);
@@ -556,9 +724,10 @@ static void handle_oneshot_dma(struct tegra_dma_channel *ch)
spin_unlock_irqrestore(&ch->lock, irq_flags);
}
-static void handle_continuous_dma(struct tegra_dma_channel *ch)
+static void handle_continuous_dbl_dma(struct tegra_dma_channel *ch)
{
struct tegra_dma_req *req;
+ struct tegra_dma_req *next_req;
unsigned long irq_flags;
spin_lock_irqsave(&ch->lock, irq_flags);
@@ -588,8 +757,6 @@ static void handle_continuous_dma(struct tegra_dma_channel *ch)
tegra_dma_stop(ch);
if (!list_is_last(&req->node, &ch->list)) {
- struct tegra_dma_req *next_req;
-
next_req = list_entry(req->node.next,
typeof(*next_req), node);
tegra_dma_update_hw(ch, next_req);
@@ -605,14 +772,12 @@ static void handle_continuous_dma(struct tegra_dma_channel *ch)
/* Load the next request into the hardware, if available
* */
if (!list_is_last(&req->node, &ch->list)) {
- struct tegra_dma_req *next_req;
-
next_req = list_entry(req->node.next,
typeof(*next_req), node);
tegra_dma_update_hw_partial(ch, next_req);
}
req->buffer_status = TEGRA_DMA_REQ_BUF_STATUS_HALF_FULL;
- req->status = TEGRA_DMA_REQ_SUCCESS;
+ req->bytes_transferred = req->size >> 1;
/* DMA lock is NOT held when callback is called */
spin_unlock_irqrestore(&ch->lock, irq_flags);
if (likely(req->threshold))
@@ -623,15 +788,23 @@ static void handle_continuous_dma(struct tegra_dma_channel *ch)
TEGRA_DMA_REQ_BUF_STATUS_HALF_FULL) {
/* Callback when the buffer is completely full (i.e on
* the second interrupt */
- int bytes_transferred;
-
- bytes_transferred = ch->req_transfer_count;
- bytes_transferred += 1;
- bytes_transferred <<= 3;
req->buffer_status = TEGRA_DMA_REQ_BUF_STATUS_FULL;
- req->bytes_transferred = bytes_transferred;
+ req->bytes_transferred = req->size;
req->status = TEGRA_DMA_REQ_SUCCESS;
+ if (list_is_last(&req->node, &ch->list))
+ tegra_dma_stop(ch);
+ else {
+ /* It may be possible that req came after
+ * half dma complete so it need to start
+ * immediately */
+ next_req = list_entry(req->node.next, typeof(*next_req), node);
+ if (next_req->status != TEGRA_DMA_REQ_INFLIGHT) {
+ tegra_dma_stop(ch);
+ tegra_dma_update_hw(ch, next_req);
+ }
+ }
+
list_del(&req->node);
/* DMA lock is NOT held when callbak is called */
@@ -640,12 +813,65 @@ static void handle_continuous_dma(struct tegra_dma_channel *ch)
return;
} else {
+ tegra_dma_stop(ch);
+ /* Dma should be stop much earlier */
BUG();
}
}
spin_unlock_irqrestore(&ch->lock, irq_flags);
}
+static void handle_continuous_sngl_dma(struct tegra_dma_channel *ch)
+{
+ struct tegra_dma_req *req;
+ struct tegra_dma_req *next_req;
+ struct tegra_dma_req *next_next_req;
+ unsigned long irq_flags;
+
+ spin_lock_irqsave(&ch->lock, irq_flags);
+ if (list_empty(&ch->list)) {
+ tegra_dma_stop(ch);
+ spin_unlock_irqrestore(&ch->lock, irq_flags);
+ pr_err("%s: No requests in the list.\n", __func__);
+ return;
+ }
+ req = list_entry(ch->list.next, typeof(*req), node);
+ if (!req || (req->buffer_status == TEGRA_DMA_REQ_BUF_STATUS_FULL)) {
+ tegra_dma_stop(ch);
+ spin_unlock_irqrestore(&ch->lock, irq_flags);
+ pr_err("%s: DMA complete irq without corresponding req\n",
+ __func__);
+ return;
+ }
+
+ /* Handle the case when buffer is completely full */
+ req->bytes_transferred = req->size;
+ req->buffer_status = TEGRA_DMA_REQ_BUF_STATUS_FULL;
+ req->status = TEGRA_DMA_REQ_SUCCESS;
+ if (list_is_last(&req->node, &ch->list)) {
+ pr_debug("%s: stop\n", __func__);
+ tegra_dma_stop(ch);
+ } else {
+ /* The next entry should have already been queued and is now
+ * in the middle of xfer. We can then write the next->next one
+ * if it exists.
+ */
+ next_req = list_entry(req->node.next, typeof(*next_req), node);
+ if (next_req->status != TEGRA_DMA_REQ_INFLIGHT) {
+ pr_debug("%s: interrupt during enqueue\n", __func__);
+ tegra_dma_stop(ch);
+ tegra_dma_update_hw(ch, next_req);
+ } else if (!list_is_last(&next_req->node, &ch->list)) {
+ next_next_req = list_entry(next_req->node.next,
+ typeof(*next_next_req), node);
+ tegra_dma_update_hw_partial(ch, next_next_req);
+ }
+ }
+ list_del(&req->node);
+ spin_unlock_irqrestore(&ch->lock, irq_flags);
+ req->complete(req);
+}
+
static irqreturn_t dma_isr(int irq, void *data)
{
struct tegra_dma_channel *ch = data;
@@ -658,19 +884,15 @@ static irqreturn_t dma_isr(int irq, void *data)
pr_warning("Got a spurious ISR for DMA channel %d\n", ch->id);
return IRQ_HANDLED;
}
- return IRQ_WAKE_THREAD;
-}
-
-static irqreturn_t dma_thread_fn(int irq, void *data)
-{
- struct tegra_dma_channel *ch = data;
if (ch->mode & TEGRA_DMA_MODE_ONESHOT)
handle_oneshot_dma(ch);
+ else if (ch->mode & TEGRA_DMA_MODE_CONTINUOUS_DOUBLE)
+ handle_continuous_dbl_dma(ch);
+ else if (ch->mode & TEGRA_DMA_MODE_CONTINUOUS_SINGLE)
+ handle_continuous_sngl_dma(ch);
else
- handle_continuous_dma(ch);
-
-
+ pr_err("Bad channel mode for DMA ISR to handle\n");
return IRQ_HANDLED;
}
@@ -696,11 +918,20 @@ int __init tegra_dma_init(void)
goto fail;
}
+ dma_clk = clk_get_sys("apbdma", "apbdma");
+ if (!IS_ERR_OR_NULL(dma_clk)) {
+ clk_enable(dma_clk);
+ tegra_periph_reset_assert(dma_clk);
+ udelay(10);
+ tegra_periph_reset_deassert(dma_clk);
+ udelay(10);
+ }
+
addr = IO_ADDRESS(TEGRA_APB_DMA_BASE);
writel(GEN_ENABLE, addr + APB_DMA_GEN);
writel(0, addr + APB_DMA_CNTRL);
writel(0xFFFFFFFFul >> (31 - TEGRA_SYSTEM_DMA_CH_MAX),
- addr + APB_DMA_IRQ_MASK_SET);
+ addr + APB_DMA_IRQ_MASK_SET);
for (i = TEGRA_SYSTEM_DMA_CH_MIN; i <= TEGRA_SYSTEM_DMA_CH_MAX; i++) {
struct tegra_dma_channel *ch = &dma_channels[i];
@@ -708,15 +939,21 @@ int __init tegra_dma_init(void)
ch->id = i;
snprintf(ch->name, TEGRA_DMA_NAME_SIZE, "dma_channel_%d", i);
+ memset(ch->client_name, 0, sizeof(ch->client_name));
+
ch->addr = IO_ADDRESS(TEGRA_APB_DMA_CH0_BASE +
TEGRA_APB_DMA_CH0_SIZE * i);
spin_lock_init(&ch->lock);
INIT_LIST_HEAD(&ch->list);
- irq = INT_APB_DMA_CH0 + i;
- ret = request_threaded_irq(irq, dma_isr, dma_thread_fn, 0,
- dma_channels[i].name, ch);
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+ if (i >= 16)
+ irq = INT_APB_DMA_CH16 + i - 16;
+ else
+#endif
+ irq = INT_APB_DMA_CH0 + i;
+ ret = request_irq(irq, dma_isr, 0, dma_channels[i].name, ch);
if (ret) {
pr_err("Failed to register IRQ %d for DMA %d\n",
irq, i);
@@ -743,10 +980,11 @@ fail:
}
postcore_initcall(tegra_dma_init);
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
+
static u32 apb_dma[5*TEGRA_SYSTEM_DMA_CH_NR + 3];
-void tegra_dma_suspend(void)
+static int tegra_dma_suspend(void)
{
void __iomem *addr = IO_ADDRESS(TEGRA_APB_DMA_BASE);
u32 *ctx = apb_dma;
@@ -766,9 +1004,11 @@ void tegra_dma_suspend(void)
*ctx++ = readl(addr + APB_DMA_CHAN_APB_PTR);
*ctx++ = readl(addr + APB_DMA_CHAN_APB_SEQ);
}
+
+ return 0;
}
-void tegra_dma_resume(void)
+static void tegra_dma_resume(void)
{
void __iomem *addr = IO_ADDRESS(TEGRA_APB_DMA_BASE);
u32 *ctx = apb_dma;
@@ -790,4 +1030,79 @@ void tegra_dma_resume(void)
}
}
+static struct syscore_ops tegra_dma_syscore_ops = {
+ .suspend = tegra_dma_suspend,
+ .resume = tegra_dma_resume,
+};
+
+static int tegra_dma_syscore_init(void)
+{
+ register_syscore_ops(&tegra_dma_syscore_ops);
+
+ return 0;
+}
+subsys_initcall(tegra_dma_syscore_init);
+#endif
+
+#ifdef CONFIG_DEBUG_FS
+
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+
+static int dbg_dma_show(struct seq_file *s, void *unused)
+{
+ int i;
+ void __iomem *addr = IO_ADDRESS(TEGRA_APB_DMA_BASE);
+
+ seq_printf(s, " APBDMA global register\n");
+ seq_printf(s, "DMA_GEN: 0x%08x\n", __raw_readl(addr + APB_DMA_GEN));
+ seq_printf(s, "DMA_CNTRL: 0x%08x\n", __raw_readl(addr + APB_DMA_CNTRL));
+ seq_printf(s, "IRQ_MASK: 0x%08x\n",
+ __raw_readl(addr + APB_DMA_IRQ_MASK));
+
+ for (i = 0; i < TEGRA_SYSTEM_DMA_CH_NR; i++) {
+ addr = IO_ADDRESS(TEGRA_APB_DMA_CH0_BASE +
+ TEGRA_APB_DMA_CH0_SIZE * i);
+
+ seq_printf(s, " APBDMA channel %02d register\n", i);
+ seq_printf(s, "0x00: 0x%08x 0x%08x 0x%08x 0x%08x\n",
+ __raw_readl(addr + 0x0),
+ __raw_readl(addr + 0x4),
+ __raw_readl(addr + 0x8),
+ __raw_readl(addr + 0xC));
+ seq_printf(s, "0x10: 0x%08x 0x%08x 0x%08x 0x%08x\n",
+ __raw_readl(addr + 0x10),
+ __raw_readl(addr + 0x14),
+ __raw_readl(addr + 0x18),
+ __raw_readl(addr + 0x1C));
+ }
+ seq_printf(s, "\nAPB DMA users\n");
+ seq_printf(s, "-------------\n");
+ for (i = TEGRA_SYSTEM_DMA_CH_MIN; i <= TEGRA_SYSTEM_DMA_CH_MAX; i++) {
+ struct tegra_dma_channel *ch = &dma_channels[i];
+ if (strlen(ch->client_name) > 0)
+ seq_printf(s, "dma %d -> %s\n", i, ch->client_name);
+ }
+ return 0;
+}
+
+static int dbg_dma_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, dbg_dma_show, &inode->i_private);
+}
+
+static const struct file_operations debug_fops = {
+ .open = dbg_dma_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int __init tegra_dma_debuginit(void)
+{
+ (void) debugfs_create_file("tegra_dma", S_IRUGO,
+ NULL, NULL, &debug_fops);
+ return 0;
+}
+late_initcall(tegra_dma_debuginit);
#endif
diff --git a/arch/arm/mach-tegra/dvfs.c b/arch/arm/mach-tegra/dvfs.c
new file mode 100644
index 000000000000..c39a10a9e46b
--- /dev/null
+++ b/arch/arm/mach-tegra/dvfs.c
@@ -0,0 +1,679 @@
+/*
+ *
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * Author:
+ * Colin Cross <ccross@google.com>
+ *
+ * Copyright (C) 2010-2011 NVIDIA Corporation.
+ *
+ * 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/clk.h>
+#include <linux/clkdev.h>
+#include <linux/debugfs.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/list_sort.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/suspend.h>
+#include <linux/delay.h>
+#include <linux/reboot.h>
+
+#include <mach/clk.h>
+
+#include "board.h"
+#include "clock.h"
+#include "dvfs.h"
+
+static LIST_HEAD(dvfs_rail_list);
+static DEFINE_MUTEX(dvfs_lock);
+static DEFINE_MUTEX(rail_disable_lock);
+
+static int dvfs_rail_update(struct dvfs_rail *rail);
+
+void tegra_dvfs_add_relationships(struct dvfs_relationship *rels, int n)
+{
+ int i;
+ struct dvfs_relationship *rel;
+
+ mutex_lock(&dvfs_lock);
+
+ for (i = 0; i < n; i++) {
+ rel = &rels[i];
+ list_add_tail(&rel->from_node, &rel->to->relationships_from);
+ list_add_tail(&rel->to_node, &rel->from->relationships_to);
+ }
+
+ mutex_unlock(&dvfs_lock);
+}
+
+int tegra_dvfs_init_rails(struct dvfs_rail *rails[], int n)
+{
+ int i;
+
+ mutex_lock(&dvfs_lock);
+
+ for (i = 0; i < n; i++) {
+ INIT_LIST_HEAD(&rails[i]->dvfs);
+ INIT_LIST_HEAD(&rails[i]->relationships_from);
+ INIT_LIST_HEAD(&rails[i]->relationships_to);
+ rails[i]->millivolts = rails[i]->nominal_millivolts;
+ rails[i]->new_millivolts = rails[i]->nominal_millivolts;
+ if (!rails[i]->step)
+ rails[i]->step = rails[i]->max_millivolts;
+
+ list_add_tail(&rails[i]->node, &dvfs_rail_list);
+ }
+
+ mutex_unlock(&dvfs_lock);
+
+ return 0;
+};
+
+static int dvfs_solve_relationship(struct dvfs_relationship *rel)
+{
+ return rel->solve(rel->from, rel->to);
+}
+
+/* Sets the voltage on a dvfs rail to a specific value, and updates any
+ * rails that depend on this rail. */
+static int dvfs_rail_set_voltage(struct dvfs_rail *rail, int millivolts)
+{
+ int ret = 0;
+ struct dvfs_relationship *rel;
+ int step = (millivolts > rail->millivolts) ? rail->step : -rail->step;
+ int i;
+ int steps;
+ bool jmp_to_zero;
+
+ if (!rail->reg) {
+ if (millivolts == rail->millivolts)
+ return 0;
+ else
+ return -EINVAL;
+ }
+
+ if (rail->disabled)
+ return 0;
+
+ rail->resolving_to = true;
+ jmp_to_zero = rail->jmp_to_zero &&
+ ((millivolts == 0) || (rail->millivolts == 0));
+ steps = jmp_to_zero ? 1 :
+ DIV_ROUND_UP(abs(millivolts - rail->millivolts), rail->step);
+
+ for (i = 0; i < steps; i++) {
+ if (!jmp_to_zero &&
+ (abs(millivolts - rail->millivolts) > rail->step))
+ rail->new_millivolts = rail->millivolts + step;
+ else
+ rail->new_millivolts = millivolts;
+
+ /* Before changing the voltage, tell each rail that depends
+ * on this rail that the voltage will change.
+ * This rail will be the "from" rail in the relationship,
+ * the rail that depends on this rail will be the "to" rail.
+ * from->millivolts will be the old voltage
+ * from->new_millivolts will be the new voltage */
+ list_for_each_entry(rel, &rail->relationships_to, to_node) {
+ ret = dvfs_rail_update(rel->to);
+ if (ret)
+ goto out;
+ }
+
+ if (!rail->disabled) {
+ rail->updating = true;
+ ret = regulator_set_voltage(rail->reg,
+ rail->new_millivolts * 1000,
+ rail->max_millivolts * 1000);
+ rail->updating = false;
+ }
+ if (ret) {
+ pr_err("Failed to set dvfs regulator %s\n", rail->reg_id);
+ goto out;
+ }
+
+ rail->millivolts = rail->new_millivolts;
+
+ /* After changing the voltage, tell each rail that depends
+ * on this rail that the voltage has changed.
+ * from->millivolts and from->new_millivolts will be the
+ * new voltage */
+ list_for_each_entry(rel, &rail->relationships_to, to_node) {
+ ret = dvfs_rail_update(rel->to);
+ if (ret)
+ goto out;
+ }
+ }
+
+ if (unlikely(rail->millivolts != millivolts)) {
+ pr_err("%s: rail didn't reach target %d in %d steps (%d)\n",
+ __func__, millivolts, steps, rail->millivolts);
+ ret = -EINVAL;
+ }
+
+out:
+ rail->resolving_to = false;
+ return ret;
+}
+
+/* Determine the minimum valid voltage for a rail, taking into account
+ * the dvfs clocks and any rails that this rail depends on. Calls
+ * dvfs_rail_set_voltage with the new voltage, which will call
+ * dvfs_rail_update on any rails that depend on this rail. */
+static int dvfs_rail_update(struct dvfs_rail *rail)
+{
+ int millivolts = 0;
+ struct dvfs *d;
+ struct dvfs_relationship *rel;
+ int ret = 0;
+ int steps;
+
+ /* if dvfs is suspended, return and handle it during resume */
+ if (rail->suspended)
+ return 0;
+
+ /* if regulators are not connected yet, return and handle it later */
+ if (!rail->reg)
+ return 0;
+
+ /* if rail update is entered while resolving circular dependencies,
+ abort recursion */
+ if (rail->resolving_to)
+ return 0;
+
+ /* Find the maximum voltage requested by any clock */
+ list_for_each_entry(d, &rail->dvfs, reg_node)
+ millivolts = max(d->cur_millivolts, millivolts);
+
+ /* retry update if limited by from-relationship to account for
+ circular dependencies */
+ steps = DIV_ROUND_UP(abs(millivolts - rail->millivolts), rail->step);
+ for (; steps >= 0; steps--) {
+ rail->new_millivolts = millivolts;
+
+ /* Check any rails that this rail depends on */
+ list_for_each_entry(rel, &rail->relationships_from, from_node)
+ rail->new_millivolts = dvfs_solve_relationship(rel);
+
+ if (rail->new_millivolts == rail->millivolts)
+ break;
+
+ ret = dvfs_rail_set_voltage(rail, rail->new_millivolts);
+ }
+
+ return ret;
+}
+
+static int dvfs_rail_connect_to_regulator(struct dvfs_rail *rail)
+{
+ struct regulator *reg;
+ int v;
+
+ if (!rail->reg) {
+ reg = regulator_get(NULL, rail->reg_id);
+ if (IS_ERR(reg)) {
+ pr_err("tegra_dvfs: failed to connect %s rail\n",
+ rail->reg_id);
+ return -EINVAL;
+ }
+ rail->reg = reg;
+ }
+
+ v = regulator_get_voltage(rail->reg);
+ if (v < 0) {
+ pr_err("tegra_dvfs: failed initial get %s voltage\n",
+ rail->reg_id);
+ return v;
+ }
+ rail->millivolts = v / 1000;
+ rail->new_millivolts = rail->millivolts;
+ return 0;
+}
+
+static int
+__tegra_dvfs_set_rate(struct dvfs *d, unsigned long rate)
+{
+ int i = 0;
+ int ret;
+
+ if (d->freqs == NULL || d->millivolts == NULL)
+ return -ENODEV;
+
+ if (rate > d->freqs[d->num_freqs - 1]) {
+ pr_warn("tegra_dvfs: rate %lu too high for dvfs on %s\n", rate,
+ d->clk_name);
+ return -EINVAL;
+ }
+
+ if (rate == 0) {
+ d->cur_millivolts = 0;
+ } else {
+ while (i < d->num_freqs && rate > d->freqs[i])
+ i++;
+
+ if ((d->max_millivolts) &&
+ (d->millivolts[i] > d->max_millivolts)) {
+ pr_warn("tegra_dvfs: voltage %d too high for dvfs on"
+ " %s\n", d->millivolts[i], d->clk_name);
+ return -EINVAL;
+ }
+ d->cur_millivolts = d->millivolts[i];
+ }
+
+ d->cur_rate = rate;
+
+ ret = dvfs_rail_update(d->dvfs_rail);
+ if (ret)
+ pr_err("Failed to set regulator %s for clock %s to %d mV\n",
+ d->dvfs_rail->reg_id, d->clk_name, d->cur_millivolts);
+
+ return ret;
+}
+
+int tegra_dvfs_predict_millivolts(struct clk *c, unsigned long rate)
+{
+ int i;
+
+ if (!rate || !c->dvfs)
+ return 0;
+
+ if (!c->dvfs->millivolts)
+ return -ENODEV;
+
+ for (i = 0; i < c->dvfs->num_freqs; i++) {
+ if (rate <= c->dvfs->freqs[i])
+ break;
+ }
+
+ if (i == c->dvfs->num_freqs)
+ return -EINVAL;
+
+ return c->dvfs->millivolts[i];
+}
+
+int tegra_dvfs_set_rate(struct clk *c, unsigned long rate)
+{
+ int ret;
+
+ if (!c->dvfs)
+ return -EINVAL;
+
+ mutex_lock(&dvfs_lock);
+ ret = __tegra_dvfs_set_rate(c->dvfs, rate);
+ mutex_unlock(&dvfs_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL(tegra_dvfs_set_rate);
+
+/* May only be called during clock init, does not take any locks on clock c. */
+int __init tegra_enable_dvfs_on_clk(struct clk *c, struct dvfs *d)
+{
+ int i;
+
+ if (c->dvfs) {
+ pr_err("Error when enabling dvfs on %s for clock %s:\n",
+ d->dvfs_rail->reg_id, c->name);
+ pr_err("DVFS already enabled for %s\n",
+ c->dvfs->dvfs_rail->reg_id);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < MAX_DVFS_FREQS; i++) {
+ if (d->millivolts[i] == 0)
+ break;
+
+ d->freqs[i] *= d->freqs_mult;
+
+ /* If final frequencies are 0, pad with previous frequency */
+ if (d->freqs[i] == 0 && i > 1)
+ d->freqs[i] = d->freqs[i - 1];
+ }
+ d->num_freqs = i;
+
+ if (d->auto_dvfs) {
+ c->auto_dvfs = true;
+ clk_set_cansleep(c);
+ }
+
+ c->dvfs = d;
+
+ mutex_lock(&dvfs_lock);
+ list_add_tail(&d->reg_node, &d->dvfs_rail->dvfs);
+ mutex_unlock(&dvfs_lock);
+
+ return 0;
+}
+
+static bool tegra_dvfs_all_rails_suspended(void)
+{
+ struct dvfs_rail *rail;
+ bool all_suspended = true;
+
+ list_for_each_entry(rail, &dvfs_rail_list, node)
+ if (!rail->suspended && !rail->disabled)
+ all_suspended = false;
+
+ return all_suspended;
+}
+
+static bool tegra_dvfs_from_rails_suspended_or_solved(struct dvfs_rail *to)
+{
+ struct dvfs_relationship *rel;
+ bool all_suspended = true;
+
+ list_for_each_entry(rel, &to->relationships_from, from_node)
+ if (!rel->from->suspended && !rel->from->disabled &&
+ !rel->solved_at_nominal)
+ all_suspended = false;
+
+ return all_suspended;
+}
+
+static int tegra_dvfs_suspend_one(void)
+{
+ struct dvfs_rail *rail;
+ int ret;
+
+ list_for_each_entry(rail, &dvfs_rail_list, node) {
+ if (!rail->suspended && !rail->disabled &&
+ tegra_dvfs_from_rails_suspended_or_solved(rail)) {
+ ret = dvfs_rail_set_voltage(rail,
+ rail->nominal_millivolts);
+ if (ret)
+ return ret;
+ rail->suspended = true;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static void tegra_dvfs_resume(void)
+{
+ struct dvfs_rail *rail;
+
+ mutex_lock(&dvfs_lock);
+
+ list_for_each_entry(rail, &dvfs_rail_list, node)
+ rail->suspended = false;
+
+ list_for_each_entry(rail, &dvfs_rail_list, node)
+ dvfs_rail_update(rail);
+
+ mutex_unlock(&dvfs_lock);
+}
+
+static int tegra_dvfs_suspend(void)
+{
+ int ret = 0;
+
+ mutex_lock(&dvfs_lock);
+
+ while (!tegra_dvfs_all_rails_suspended()) {
+ ret = tegra_dvfs_suspend_one();
+ if (ret)
+ break;
+ }
+
+ mutex_unlock(&dvfs_lock);
+
+ if (ret)
+ tegra_dvfs_resume();
+
+ return ret;
+}
+
+static int tegra_dvfs_pm_notify(struct notifier_block *nb,
+ unsigned long event, void *data)
+{
+ switch (event) {
+ case PM_SUSPEND_PREPARE:
+ if (tegra_dvfs_suspend())
+ return NOTIFY_STOP;
+ break;
+ case PM_POST_SUSPEND:
+ tegra_dvfs_resume();
+ break;
+ }
+
+ return NOTIFY_OK;
+};
+
+static struct notifier_block tegra_dvfs_nb = {
+ .notifier_call = tegra_dvfs_pm_notify,
+};
+
+static int tegra_dvfs_reboot_notify(struct notifier_block *nb,
+ unsigned long event, void *data)
+{
+ switch (event) {
+ case SYS_RESTART:
+ case SYS_HALT:
+ case SYS_POWER_OFF:
+ tegra_dvfs_suspend();
+ return NOTIFY_OK;
+ }
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block tegra_dvfs_reboot_nb = {
+ .notifier_call = tegra_dvfs_reboot_notify,
+};
+
+/* must be called with dvfs lock held */
+static void __tegra_dvfs_rail_disable(struct dvfs_rail *rail)
+{
+ int ret;
+
+ ret = dvfs_rail_set_voltage(rail, rail->nominal_millivolts);
+ if (ret)
+ pr_info("dvfs: failed to set regulator %s to disable "
+ "voltage %d\n", rail->reg_id,
+ rail->nominal_millivolts);
+ rail->disabled = true;
+}
+
+/* must be called with dvfs lock held */
+static void __tegra_dvfs_rail_enable(struct dvfs_rail *rail)
+{
+ rail->disabled = false;
+ dvfs_rail_update(rail);
+}
+
+void tegra_dvfs_rail_enable(struct dvfs_rail *rail)
+{
+ mutex_lock(&rail_disable_lock);
+
+ if (rail->disabled) {
+ mutex_lock(&dvfs_lock);
+ __tegra_dvfs_rail_enable(rail);
+ mutex_unlock(&dvfs_lock);
+
+ tegra_dvfs_rail_post_enable(rail);
+ }
+ mutex_unlock(&rail_disable_lock);
+
+}
+
+void tegra_dvfs_rail_disable(struct dvfs_rail *rail)
+{
+ mutex_lock(&rail_disable_lock);
+ if (rail->disabled)
+ goto out;
+
+ /* rail disable will set it to nominal voltage underneath clock
+ framework - need to re-configure clock rates that are not safe
+ at nominal (yes, unsafe at nominal is ugly, but possible). Rate
+ change must be done outside of dvfs lock. */
+ if (tegra_dvfs_rail_disable_prepare(rail)) {
+ pr_info("dvfs: failed to prepare regulator %s to disable\n",
+ rail->reg_id);
+ goto out;
+ }
+
+ mutex_lock(&dvfs_lock);
+ __tegra_dvfs_rail_disable(rail);
+ mutex_unlock(&dvfs_lock);
+out:
+ mutex_unlock(&rail_disable_lock);
+}
+
+int tegra_dvfs_rail_disable_by_name(const char *reg_id)
+{
+ struct dvfs_rail *rail = tegra_dvfs_get_rail_by_name(reg_id);
+ if (!rail)
+ return -EINVAL;
+
+ tegra_dvfs_rail_disable(rail);
+ return 0;
+}
+
+struct dvfs_rail *tegra_dvfs_get_rail_by_name(const char *reg_id)
+{
+ struct dvfs_rail *rail;
+
+ mutex_lock(&dvfs_lock);
+ list_for_each_entry(rail, &dvfs_rail_list, node) {
+ if (!strcmp(reg_id, rail->reg_id)) {
+ mutex_unlock(&dvfs_lock);
+ return rail;
+ }
+ }
+ mutex_unlock(&dvfs_lock);
+ return NULL;
+}
+
+bool tegra_dvfs_rail_updating(struct clk *clk)
+{
+ return (!clk ? false :
+ (!clk->dvfs ? false :
+ (!clk->dvfs->dvfs_rail ? false :
+ (clk->dvfs->dvfs_rail->updating))));
+}
+
+/*
+ * Iterate through all the dvfs regulators, finding the regulator exported
+ * by the regulator api for each one. Must be called in late init, after
+ * all the regulator api's regulators are initialized.
+ */
+int __init tegra_dvfs_late_init(void)
+{
+ bool connected = true;
+ struct dvfs_rail *rail;
+
+ mutex_lock(&dvfs_lock);
+
+ list_for_each_entry(rail, &dvfs_rail_list, node)
+ if (dvfs_rail_connect_to_regulator(rail))
+ connected = false;
+
+ list_for_each_entry(rail, &dvfs_rail_list, node)
+ if (connected)
+ dvfs_rail_update(rail);
+ else
+ __tegra_dvfs_rail_disable(rail);
+
+ mutex_unlock(&dvfs_lock);
+
+ register_pm_notifier(&tegra_dvfs_nb);
+ register_reboot_notifier(&tegra_dvfs_reboot_nb);
+
+ return 0;
+}
+late_initcall(tegra_dvfs_late_init);
+
+#ifdef CONFIG_DEBUG_FS
+static int dvfs_tree_sort_cmp(void *p, struct list_head *a, struct list_head *b)
+{
+ struct dvfs *da = list_entry(a, struct dvfs, reg_node);
+ struct dvfs *db = list_entry(b, struct dvfs, reg_node);
+ int ret;
+
+ ret = strcmp(da->dvfs_rail->reg_id, db->dvfs_rail->reg_id);
+ if (ret != 0)
+ return ret;
+
+ if (da->cur_millivolts < db->cur_millivolts)
+ return 1;
+ if (da->cur_millivolts > db->cur_millivolts)
+ return -1;
+
+ return strcmp(da->clk_name, db->clk_name);
+}
+
+static int dvfs_tree_show(struct seq_file *s, void *data)
+{
+ struct dvfs *d;
+ struct dvfs_rail *rail;
+ struct dvfs_relationship *rel;
+
+ seq_printf(s, " clock rate mV\n");
+ seq_printf(s, "--------------------------------\n");
+
+ mutex_lock(&dvfs_lock);
+
+ list_for_each_entry(rail, &dvfs_rail_list, node) {
+ seq_printf(s, "%s %d mV%s:\n", rail->reg_id,
+ rail->millivolts, rail->disabled ? " disabled" : "");
+ list_for_each_entry(rel, &rail->relationships_from, from_node) {
+ seq_printf(s, " %-10s %-7d mV %-4d mV\n",
+ rel->from->reg_id,
+ rel->from->millivolts,
+ dvfs_solve_relationship(rel));
+ }
+
+ list_sort(NULL, &rail->dvfs, dvfs_tree_sort_cmp);
+
+ list_for_each_entry(d, &rail->dvfs, reg_node) {
+ seq_printf(s, " %-10s %-10lu %-4d mV\n", d->clk_name,
+ d->cur_rate, d->cur_millivolts);
+ }
+ }
+
+ mutex_unlock(&dvfs_lock);
+
+ return 0;
+}
+
+static int dvfs_tree_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, dvfs_tree_show, inode->i_private);
+}
+
+static const struct file_operations dvfs_tree_fops = {
+ .open = dvfs_tree_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+int __init dvfs_debugfs_init(struct dentry *clk_debugfs_root)
+{
+ struct dentry *d;
+
+ d = debugfs_create_file("dvfs", S_IRUGO, clk_debugfs_root, NULL,
+ &dvfs_tree_fops);
+ if (!d)
+ return -ENOMEM;
+
+ return 0;
+}
+
+#endif
diff --git a/arch/arm/mach-tegra/dvfs.h b/arch/arm/mach-tegra/dvfs.h
new file mode 100644
index 000000000000..60f8d51c6f1a
--- /dev/null
+++ b/arch/arm/mach-tegra/dvfs.h
@@ -0,0 +1,144 @@
+/*
+ *
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * Author:
+ * Colin Cross <ccross@google.com>
+ *
+ * Copyright (C) 2010-2011 NVIDIA Corporation.
+ *
+ * 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 _TEGRA_DVFS_H_
+#define _TEGRA_DVFS_H_
+
+#define MAX_DVFS_FREQS 18
+
+struct clk;
+struct dvfs_rail;
+
+/*
+ * dvfs_relationship between to rails, "from" and "to"
+ * when the rail changes, it will call dvfs_rail_update on the rails
+ * in the relationship_to list.
+ * when determining the voltage to set a rail to, it will consider each
+ * rail in the relationship_from list.
+ */
+struct dvfs_relationship {
+ struct dvfs_rail *to;
+ struct dvfs_rail *from;
+ int (*solve)(struct dvfs_rail *, struct dvfs_rail *);
+
+ struct list_head to_node; /* node in relationship_to list */
+ struct list_head from_node; /* node in relationship_from list */
+ bool solved_at_nominal;
+};
+
+struct dvfs_rail {
+ const char *reg_id;
+ int min_millivolts;
+ int max_millivolts;
+ int nominal_millivolts;
+ int step;
+ bool jmp_to_zero;
+ bool disabled;
+ bool updating;
+ bool resolving_to;
+
+ struct list_head node; /* node in dvfs_rail_list */
+ struct list_head dvfs; /* list head of attached dvfs clocks */
+ struct list_head relationships_to;
+ struct list_head relationships_from;
+ struct regulator *reg;
+ int millivolts;
+ int new_millivolts;
+ bool suspended;
+};
+
+struct dvfs {
+ /* Used only by tegra2_clock.c */
+ const char *clk_name;
+ int speedo_id;
+ int process_id;
+
+ /* Must be initialized before tegra_dvfs_init */
+ int freqs_mult;
+ unsigned long freqs[MAX_DVFS_FREQS];
+ const int *millivolts;
+ struct dvfs_rail *dvfs_rail;
+ bool auto_dvfs;
+
+ /* Filled in by tegra_dvfs_init */
+ int max_millivolts;
+ int num_freqs;
+
+ int cur_millivolts;
+ unsigned long cur_rate;
+ struct list_head node;
+ struct list_head debug_node;
+ struct list_head reg_node;
+};
+
+#ifdef CONFIG_TEGRA_SILICON_PLATFORM
+void tegra_soc_init_dvfs(void);
+int tegra_enable_dvfs_on_clk(struct clk *c, struct dvfs *d);
+int dvfs_debugfs_init(struct dentry *clk_debugfs_root);
+int tegra_dvfs_late_init(void);
+int tegra_dvfs_init_rails(struct dvfs_rail *dvfs_rails[], int n);
+void tegra_dvfs_add_relationships(struct dvfs_relationship *rels, int n);
+void tegra_dvfs_rail_enable(struct dvfs_rail *rail);
+void tegra_dvfs_rail_disable(struct dvfs_rail *rail);
+bool tegra_dvfs_rail_updating(struct clk *clk);
+struct dvfs_rail *tegra_dvfs_get_rail_by_name(const char *reg_id);
+int tegra_dvfs_predict_millivolts(struct clk *c, unsigned long rate);
+void tegra_dvfs_core_cap_enable(bool enable);
+void tegra_dvfs_core_cap_level_set(int level);
+#else
+static inline void tegra_soc_init_dvfs(void)
+{}
+static inline int tegra_enable_dvfs_on_clk(struct clk *c, struct dvfs *d)
+{ return 0; }
+static inline int dvfs_debugfs_init(struct dentry *clk_debugfs_root)
+{ return 0; }
+static inline int tegra_dvfs_late_init(void)
+{ return 0; }
+static inline int tegra_dvfs_init_rails(struct dvfs_rail *dvfs_rails[], int n)
+{ return 0; }
+static inline void tegra_dvfs_add_relationships(struct dvfs_relationship *rels, int n)
+{}
+static inline void tegra_dvfs_rail_enable(struct dvfs_rail *rail)
+{}
+static inline void tegra_dvfs_rail_disable(struct dvfs_rail *rail)
+{}
+static inline bool tegra_dvfs_rail_updating(struct clk *clk)
+{ return false; }
+static inline struct dvfs_rail *tegra_dvfs_get_rail_by_name(const char *reg_id)
+{ return NULL; }
+static inline int tegra_dvfs_predict_millivolts(struct clk *c, unsigned long rate)
+{ return 0; }
+static inline void tegra_dvfs_core_cap_enable(bool enable)
+{}
+static inline void tegra_dvfs_core_cap_level_set(int level)
+{}
+#endif
+
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+int tegra_dvfs_rail_disable_prepare(struct dvfs_rail *rail);
+int tegra_dvfs_rail_post_enable(struct dvfs_rail *rail);
+#else
+static inline int tegra_dvfs_rail_disable_prepare(struct dvfs_rail *rail)
+{ return 0; }
+static inline int tegra_dvfs_rail_post_enable(struct dvfs_rail *rail)
+{ return 0; }
+#endif
+
+#endif
diff --git a/arch/arm/mach-tegra/edp.c b/arch/arm/mach-tegra/edp.c
new file mode 100644
index 000000000000..3808978565bd
--- /dev/null
+++ b/arch/arm/mach-tegra/edp.c
@@ -0,0 +1,387 @@
+/*
+ * arch/arm/mach-tegra/edp.c
+ *
+ * Copyright (C) 2011 NVIDIA, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <mach/edp.h>
+
+#include "fuse.h"
+
+static const struct tegra_edp_limits *edp_limits;
+static int edp_limits_size;
+
+static const unsigned int *system_edp_limits;
+
+/*
+ * Temperature step size cannot be less than 4C because of hysteresis
+ * delta
+ * Code assumes different temperatures for the same speedo_id /
+ * regulator_cur are adjacent in the table, and higest regulator_cur
+ * comes first
+ */
+static char __initdata tegra_edp_map[] = {
+ 0x00, 0x2f, 0x2d, 0x82, 0x78, 0x78, 0x78, 0x00,
+ 0x2f, 0x3c, 0x82, 0x78, 0x78, 0x78, 0x00, 0x2f,
+ 0x4b, 0x82, 0x78, 0x78, 0x78, 0x00, 0x2f, 0x55,
+ 0x82, 0x78, 0x78, 0x78, 0x00, 0x28, 0x2d, 0x82,
+ 0x78, 0x78, 0x78, 0x00, 0x28, 0x3c, 0x82, 0x78,
+ 0x78, 0x78, 0x00, 0x28, 0x4b, 0x82, 0x78, 0x78,
+ 0x73, 0x00, 0x28, 0x55, 0x82, 0x78, 0x78, 0x69,
+ 0x00, 0x23, 0x2d, 0x82, 0x78, 0x78, 0x78, 0x00,
+ 0x23, 0x3c, 0x82, 0x78, 0x78, 0x6e, 0x00, 0x23,
+ 0x4b, 0x82, 0x78, 0x78, 0x64, 0x00, 0x23, 0x55,
+ 0x82, 0x78, 0x6e, 0x5a, 0x00, 0x1e, 0x2d, 0x82,
+ 0x78, 0x78, 0x69, 0x00, 0x1e, 0x3c, 0x82, 0x78,
+ 0x78, 0x64, 0x00, 0x1e, 0x4b, 0x82, 0x78, 0x6e,
+ 0x5a, 0x00, 0x1e, 0x55, 0x82, 0x78, 0x64, 0x50,
+ 0x00, 0x19, 0x2d, 0x82, 0x78, 0x6e, 0x5a, 0x00,
+ 0x19, 0x3c, 0x82, 0x78, 0x69, 0x55, 0x00, 0x19,
+ 0x4b, 0x82, 0x78, 0x5f, 0x4b, 0x00, 0x19, 0x55,
+ 0x82, 0x73, 0x55, 0x3c, 0x01, 0x2f, 0x2d, 0x82,
+ 0x78, 0x78, 0x78, 0x01, 0x2f, 0x3c, 0x82, 0x78,
+ 0x78, 0x78, 0x01, 0x2f, 0x4b, 0x82, 0x78, 0x78,
+ 0x78, 0x01, 0x2f, 0x55, 0x82, 0x78, 0x78, 0x78,
+ 0x01, 0x28, 0x2d, 0x82, 0x78, 0x78, 0x78, 0x01,
+ 0x28, 0x3c, 0x82, 0x78, 0x78, 0x78, 0x01, 0x28,
+ 0x4b, 0x82, 0x78, 0x78, 0x73, 0x01, 0x28, 0x55,
+ 0x82, 0x78, 0x78, 0x69, 0x01, 0x23, 0x2d, 0x82,
+ 0x78, 0x78, 0x78, 0x01, 0x23, 0x3c, 0x82, 0x78,
+ 0x78, 0x6e, 0x01, 0x23, 0x4b, 0x82, 0x78, 0x78,
+ 0x64, 0x01, 0x23, 0x55, 0x82, 0x78, 0x6e, 0x5a,
+ 0x01, 0x1e, 0x2d, 0x82, 0x78, 0x78, 0x69, 0x01,
+ 0x1e, 0x3c, 0x82, 0x78, 0x78, 0x64, 0x01, 0x1e,
+ 0x4b, 0x82, 0x78, 0x6e, 0x5a, 0x01, 0x1e, 0x55,
+ 0x82, 0x78, 0x64, 0x50, 0x01, 0x19, 0x2d, 0x82,
+ 0x78, 0x6e, 0x5a, 0x01, 0x19, 0x3c, 0x82, 0x78,
+ 0x69, 0x55, 0x01, 0x19, 0x4b, 0x82, 0x78, 0x5f,
+ 0x4b, 0x01, 0x19, 0x55, 0x82, 0x73, 0x55, 0x3c,
+ 0x02, 0x3d, 0x2d, 0x8c, 0x82, 0x82, 0x82, 0x02,
+ 0x3d, 0x3c, 0x8c, 0x82, 0x82, 0x82, 0x02, 0x3d,
+ 0x4b, 0x8c, 0x82, 0x82, 0x82, 0x02, 0x3d, 0x55,
+ 0x8c, 0x82, 0x82, 0x82, 0x02, 0x32, 0x2d, 0x8c,
+ 0x82, 0x82, 0x82, 0x02, 0x32, 0x3c, 0x8c, 0x82,
+ 0x82, 0x82, 0x02, 0x32, 0x4b, 0x8c, 0x82, 0x82,
+ 0x78, 0x02, 0x32, 0x55, 0x8c, 0x82, 0x82, 0x6e,
+ 0x02, 0x28, 0x2d, 0x8c, 0x82, 0x82, 0x78, 0x02,
+ 0x28, 0x3c, 0x8c, 0x82, 0x82, 0x73, 0x02, 0x28,
+ 0x4b, 0x8c, 0x82, 0x78, 0x69, 0x02, 0x28, 0x55,
+ 0x8c, 0x82, 0x6e, 0x5a, 0x02, 0x23, 0x2d, 0x8c,
+ 0x82, 0x82, 0x6e, 0x02, 0x23, 0x3c, 0x8c, 0x82,
+ 0x78, 0x69, 0x02, 0x23, 0x4b, 0x8c, 0x82, 0x6e,
+ 0x5a, 0x02, 0x23, 0x55, 0x8c, 0x82, 0x64, 0x50,
+ 0x03, 0x3d, 0x2d, 0x8c, 0x82, 0x82, 0x82, 0x03,
+ 0x3d, 0x3c, 0x8c, 0x82, 0x82, 0x82, 0x03, 0x3d,
+ 0x4b, 0x8c, 0x82, 0x82, 0x82, 0x03, 0x3d, 0x55,
+ 0x8c, 0x82, 0x82, 0x82, 0x03, 0x32, 0x2d, 0x8c,
+ 0x82, 0x82, 0x82, 0x03, 0x32, 0x3c, 0x8c, 0x82,
+ 0x82, 0x82, 0x03, 0x32, 0x4b, 0x8c, 0x82, 0x82,
+ 0x78, 0x03, 0x32, 0x55, 0x8c, 0x82, 0x82, 0x6e,
+ 0x03, 0x28, 0x2d, 0x8c, 0x82, 0x82, 0x78, 0x03,
+ 0x28, 0x3c, 0x8c, 0x82, 0x82, 0x73, 0x03, 0x28,
+ 0x4b, 0x8c, 0x82, 0x78, 0x69, 0x03, 0x28, 0x55,
+ 0x8c, 0x82, 0x6e, 0x5a, 0x03, 0x23, 0x2d, 0x8c,
+ 0x82, 0x82, 0x6e, 0x03, 0x23, 0x3c, 0x8c, 0x82,
+ 0x78, 0x69, 0x03, 0x23, 0x4b, 0x8c, 0x82, 0x6e,
+ 0x5a, 0x03, 0x23, 0x55, 0x8c, 0x82, 0x64, 0x50,
+ 0x04, 0x32, 0x2d, 0x96, 0x8c, 0x8c, 0x8c, 0x04,
+ 0x32, 0x3c, 0x96, 0x8c, 0x8c, 0x8c, 0x04, 0x32,
+ 0x46, 0x96, 0x8c, 0x8c, 0x8c, 0x04, 0x32, 0x4b,
+ 0x82, 0x78, 0x78, 0x78, 0x04, 0x32, 0x55, 0x82,
+ 0x78, 0x78, 0x78, 0x04, 0x2f, 0x2d, 0x96, 0x8c,
+ 0x8c, 0x8c, 0x04, 0x2f, 0x3c, 0x96, 0x8c, 0x8c,
+ 0x8c, 0x04, 0x2f, 0x46, 0x96, 0x8c, 0x8c, 0x82,
+ 0x04, 0x2f, 0x4b, 0x82, 0x78, 0x78, 0x78, 0x04,
+ 0x2f, 0x55, 0x82, 0x78, 0x78, 0x78, 0x04, 0x28,
+ 0x2d, 0x96, 0x8c, 0x8c, 0x82, 0x04, 0x28, 0x3c,
+ 0x96, 0x8c, 0x8c, 0x82, 0x04, 0x28, 0x46, 0x96,
+ 0x8c, 0x8c, 0x78, 0x04, 0x28, 0x4b, 0x82, 0x78,
+ 0x78, 0x78, 0x04, 0x28, 0x55, 0x82, 0x78, 0x78,
+ 0x6e, 0x04, 0x23, 0x2d, 0x96, 0x8c, 0x8c, 0x78,
+ 0x04, 0x23, 0x3c, 0x96, 0x8c, 0x82, 0x78, 0x04,
+ 0x23, 0x46, 0x96, 0x8c, 0x82, 0x6e, 0x04, 0x23,
+ 0x4b, 0x82, 0x78, 0x78, 0x6e, 0x04, 0x23, 0x55,
+ 0x82, 0x78, 0x78, 0x64, 0x04, 0x1e, 0x2d, 0x96,
+ 0x8c, 0x82, 0x6e, 0x04, 0x1e, 0x3c, 0x96, 0x8c,
+ 0x78, 0x64, 0x04, 0x1e, 0x46, 0x96, 0x8c, 0x78,
+ 0x5a, 0x04, 0x1e, 0x4b, 0x82, 0x78, 0x78, 0x5a,
+ 0x04, 0x1e, 0x55, 0x82, 0x78, 0x69, 0x50, 0x04,
+ 0x19, 0x2d, 0x96, 0x8c, 0x6e, 0x5a, 0x04, 0x19,
+ 0x3c, 0x96, 0x82, 0x6e, 0x55, 0x04, 0x19, 0x46,
+ 0x96, 0x82, 0x64, 0x50, 0x04, 0x19, 0x4b, 0x82,
+ 0x78, 0x64, 0x50, 0x04, 0x19, 0x55, 0x82, 0x78,
+ 0x55, 0x3c, 0x05, 0x64, 0x3c, 0xaa, 0xa0, 0xa0,
+ 0xa0, 0x05, 0x64, 0x55, 0x8c, 0x82, 0x82, 0x82,
+ 0x05, 0x3c, 0x3c, 0x8c, 0x82, 0x82, 0x82, 0x05,
+ 0x3c, 0x55, 0x8c, 0x82, 0x82, 0x82, 0x06, 0x64,
+ 0x3c, 0xaa, 0xa0, 0x82, 0x82, 0x06, 0x64, 0x55,
+ 0x8c, 0x82, 0x82, 0x82, 0x06, 0x3c, 0x3c, 0x8c,
+ 0x82, 0x82, 0x82, 0x06, 0x3c, 0x55, 0x8c, 0x82,
+ 0x82, 0x82, 0x07, 0x3b, 0x2d, 0x82, 0x78, 0x78,
+ 0x78, 0x07, 0x3b, 0x3c, 0x82, 0x78, 0x78, 0x78,
+ 0x07, 0x3b, 0x4b, 0x82, 0x78, 0x78, 0x78, 0x07,
+ 0x3b, 0x5a, 0x82, 0x78, 0x78, 0x78, 0x07, 0x32,
+ 0x2d, 0x82, 0x78, 0x78, 0x78, 0x07, 0x32, 0x3c,
+ 0x82, 0x78, 0x78, 0x78, 0x07, 0x32, 0x4b, 0x82,
+ 0x78, 0x78, 0x78, 0x07, 0x32, 0x5a, 0x82, 0x78,
+ 0x6e, 0x64, 0x07, 0x28, 0x2d, 0x82, 0x78, 0x78,
+ 0x6e, 0x07, 0x28, 0x3c, 0x82, 0x78, 0x78, 0x64,
+ 0x07, 0x28, 0x4b, 0x82, 0x78, 0x78, 0x64, 0x07,
+ 0x28, 0x5a, 0x82, 0x78, 0x64, 0x50, 0x07, 0x23,
+ 0x2d, 0x82, 0x78, 0x78, 0x64, 0x07, 0x23, 0x3c,
+ 0x82, 0x78, 0x78, 0x64, 0x07, 0x23, 0x4b, 0x82,
+ 0x78, 0x64, 0x50, 0x07, 0x23, 0x5a, 0x82, 0x78,
+ 0x5a, 0x46, 0x08, 0x3b, 0x2d, 0x82, 0x78, 0x78,
+ 0x78, 0x08, 0x3b, 0x3c, 0x82, 0x78, 0x78, 0x78,
+ 0x08, 0x3b, 0x4b, 0x82, 0x78, 0x78, 0x78, 0x08,
+ 0x3b, 0x5a, 0x82, 0x78, 0x78, 0x78, 0x08, 0x32,
+ 0x2d, 0x82, 0x78, 0x78, 0x78, 0x08, 0x32, 0x3c,
+ 0x82, 0x78, 0x78, 0x78, 0x08, 0x32, 0x4b, 0x82,
+ 0x78, 0x78, 0x78, 0x08, 0x32, 0x5a, 0x82, 0x78,
+ 0x6e, 0x64, 0x08, 0x28, 0x2d, 0x82, 0x78, 0x78,
+ 0x6e, 0x08, 0x28, 0x3c, 0x82, 0x78, 0x78, 0x64,
+ 0x08, 0x28, 0x4b, 0x82, 0x78, 0x78, 0x64, 0x08,
+ 0x28, 0x5a, 0x82, 0x78, 0x64, 0x50, 0x08, 0x23,
+ 0x2d, 0x82, 0x78, 0x78, 0x64, 0x08, 0x23, 0x3c,
+ 0x82, 0x78, 0x78, 0x64, 0x08, 0x23, 0x4b, 0x82,
+ 0x78, 0x64, 0x50, 0x08, 0x23, 0x5a, 0x82, 0x78,
+ 0x5a, 0x46,
+};
+
+
+static struct system_edp_entry __initdata tegra_system_edp_map[] = {
+
+/* {SKU, power-limit (in 100mW), {freq limits (in 10Mhz)} } */
+
+ { 1, 49, {130, 120, 120, 120} },
+ { 1, 44, {130, 120, 120, 110} },
+ { 1, 37, {130, 120, 110, 100} },
+ { 1, 35, {130, 120, 110, 90} },
+ { 1, 29, {130, 120, 100, 80} },
+ { 1, 27, {130, 120, 90, 80} },
+ { 1, 25, {130, 110, 80, 60} },
+ { 1, 21, {130, 100, 80, 40} },
+};
+
+/*
+ * "Safe entry" to be used when no match for speedo_id /
+ * regulator_cur is found; must be the last one
+ */
+static struct tegra_edp_limits edp_default_limits[] = {
+ {85, {1000000, 1000000, 1000000, 1000000} },
+};
+
+
+
+/*
+ * Specify regulator current in mA, e.g. 5000mA
+ * Use 0 for default
+ */
+void __init tegra_init_cpu_edp_limits(unsigned int regulator_mA)
+{
+ int cpu_speedo_id = tegra_cpu_speedo_id();
+ int i, j;
+ struct tegra_edp_limits *e;
+ struct tegra_edp_entry *t = (struct tegra_edp_entry *)tegra_edp_map;
+ int tsize = sizeof(tegra_edp_map)/sizeof(struct tegra_edp_entry);
+
+ if (!regulator_mA) {
+ edp_limits = edp_default_limits;
+ edp_limits_size = ARRAY_SIZE(edp_default_limits);
+ return;
+ }
+
+ for (i = 0; i < tsize; i++) {
+ if (t[i].speedo_id == cpu_speedo_id &&
+ t[i].regulator_100mA <= regulator_mA / 100)
+ break;
+ }
+
+ /* No entry found in tegra_edp_map */
+ if (i >= tsize) {
+ edp_limits = edp_default_limits;
+ edp_limits_size = ARRAY_SIZE(edp_default_limits);
+ return;
+ }
+
+ /* Find all rows for this entry */
+ for (j = i + 1; j < tsize; j++) {
+ if (t[i].speedo_id != t[j].speedo_id ||
+ t[i].regulator_100mA != t[j].regulator_100mA)
+ break;
+ }
+
+ edp_limits_size = j - i;
+ e = kmalloc(sizeof(struct tegra_edp_limits) * edp_limits_size,
+ GFP_KERNEL);
+ BUG_ON(!e);
+
+ for (j = 0; j < edp_limits_size; j++) {
+ e[j].temperature = (int)t[i+j].temperature;
+ e[j].freq_limits[0] = (unsigned int)t[i+j].freq_limits[0] * 10000;
+ e[j].freq_limits[1] = (unsigned int)t[i+j].freq_limits[1] * 10000;
+ e[j].freq_limits[2] = (unsigned int)t[i+j].freq_limits[2] * 10000;
+ e[j].freq_limits[3] = (unsigned int)t[i+j].freq_limits[3] * 10000;
+ }
+
+ if (edp_limits != edp_default_limits)
+ kfree(edp_limits);
+
+ edp_limits = e;
+}
+
+
+void __init tegra_init_system_edp_limits(unsigned int power_limit_mW)
+{
+ int cpu_speedo_id = tegra_cpu_speedo_id();
+ int i;
+ unsigned int *e;
+ struct system_edp_entry *t =
+ (struct system_edp_entry *)tegra_system_edp_map;
+ int tsize = sizeof(tegra_system_edp_map) /
+ sizeof(struct system_edp_entry);
+
+ if (!power_limit_mW) {
+ e = NULL;
+ goto out;
+ }
+
+ for (i = 0; i < tsize; i++)
+ if (t[i].speedo_id == cpu_speedo_id)
+ break;
+
+ if (i >= tsize) {
+ e = NULL;
+ goto out;
+ }
+
+ do {
+ if (t[i].power_limit_100mW <= power_limit_mW / 100)
+ break;
+ i++;
+ } while (i < tsize && t[i].speedo_id == cpu_speedo_id);
+
+ if (i >= tsize || t[i].speedo_id != cpu_speedo_id)
+ i--; /* No low enough entry in the table, use best possible */
+
+ e = kmalloc(sizeof(unsigned int) * 4, GFP_KERNEL);
+ BUG_ON(!e);
+
+ e[0] = (unsigned int)t[i].freq_limits[0] * 10000;
+ e[1] = (unsigned int)t[i].freq_limits[1] * 10000;
+ e[2] = (unsigned int)t[i].freq_limits[2] * 10000;
+ e[3] = (unsigned int)t[i].freq_limits[3] * 10000;
+
+out:
+ kfree(system_edp_limits);
+
+ system_edp_limits = e;
+}
+
+
+void tegra_get_cpu_edp_limits(const struct tegra_edp_limits **limits, int *size)
+{
+ *limits = edp_limits;
+ *size = edp_limits_size;
+}
+
+void tegra_get_system_edp_limits(const unsigned int **limits)
+{
+ *limits = system_edp_limits;
+}
+
+#ifdef CONFIG_DEBUG_FS
+
+static int edp_limit_debugfs_show(struct seq_file *s, void *data)
+{
+ seq_printf(s, "%u\n", tegra_get_edp_limit());
+ return 0;
+}
+
+static int edp_debugfs_show(struct seq_file *s, void *data)
+{
+ int i;
+
+ seq_printf(s, "-- CPU EDP table --\n");
+ for (i = 0; i < edp_limits_size; i++) {
+ seq_printf(s, "%4dC: %10u %10u %10u %10u\n",
+ edp_limits[i].temperature,
+ edp_limits[i].freq_limits[0],
+ edp_limits[i].freq_limits[1],
+ edp_limits[i].freq_limits[2],
+ edp_limits[i].freq_limits[3]);
+ }
+
+ if (system_edp_limits) {
+ seq_printf(s, "\n-- System EDP table --\n");
+ seq_printf(s, "%10u %10u %10u %10u\n",
+ system_edp_limits[0],
+ system_edp_limits[1],
+ system_edp_limits[2],
+ system_edp_limits[3]);
+ }
+
+ return 0;
+}
+
+
+static int edp_debugfs_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, edp_debugfs_show, inode->i_private);
+}
+
+static int edp_limit_debugfs_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, edp_limit_debugfs_show, inode->i_private);
+}
+
+
+static const struct file_operations edp_debugfs_fops = {
+ .open = edp_debugfs_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static const struct file_operations edp_limit_debugfs_fops = {
+ .open = edp_limit_debugfs_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int __init tegra_edp_debugfs_init(void)
+{
+ struct dentry *d;
+
+ d = debugfs_create_file("edp", S_IRUGO, NULL, NULL,
+ &edp_debugfs_fops);
+ if (!d)
+ return -ENOMEM;
+
+ d = debugfs_create_file("edp_limit", S_IRUGO, NULL, NULL,
+ &edp_limit_debugfs_fops);
+
+ return 0;
+}
+
+late_initcall(tegra_edp_debugfs_init);
+#endif /* CONFIG_DEBUG_FS */
diff --git a/arch/arm/mach-tegra/fiq.c b/arch/arm/mach-tegra/fiq.c
new file mode 100644
index 000000000000..19f9c059d100
--- /dev/null
+++ b/arch/arm/mach-tegra/fiq.c
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * Author:
+ * Brian Swetland <swetland@google.com>
+ * Iliyan Malchev <malchev@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/kernel.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+
+#include <asm/hardware/gic.h>
+
+#include <mach/iomap.h>
+#include <mach/fiq.h>
+
+#include "board.h"
+
+#define ICTLR_CPU_IER 0x20
+#define ICTLR_CPU_IER_SET 0x24
+#define ICTLR_CPU_IER_CLR 0x28
+#define ICTLR_CPU_IEP_CLASS 0x2C
+
+#define FIRST_LEGACY_IRQ 32
+
+static void __iomem *ictlr_reg_base[] = {
+ IO_ADDRESS(TEGRA_PRIMARY_ICTLR_BASE),
+ IO_ADDRESS(TEGRA_SECONDARY_ICTLR_BASE),
+ IO_ADDRESS(TEGRA_TERTIARY_ICTLR_BASE),
+ IO_ADDRESS(TEGRA_QUATERNARY_ICTLR_BASE),
+};
+
+static void tegra_legacy_select_fiq(unsigned int irq, bool fiq)
+{
+ void __iomem *base;
+ pr_debug("%s: %d\n", __func__, irq);
+
+ irq -= FIRST_LEGACY_IRQ;
+ base = ictlr_reg_base[irq>>5];
+ writel(fiq << (irq & 31), base + ICTLR_CPU_IEP_CLASS);
+}
+
+static void tegra_fiq_mask(struct irq_data *d)
+{
+ void __iomem *base;
+ int leg_irq;
+
+ if (d->irq < FIRST_LEGACY_IRQ)
+ return;
+
+ leg_irq = d->irq - FIRST_LEGACY_IRQ;
+ base = ictlr_reg_base[leg_irq >> 5];
+ writel(1 << (leg_irq & 31), base + ICTLR_CPU_IER_CLR);
+}
+
+static void tegra_fiq_unmask(struct irq_data *d)
+{
+ void __iomem *base;
+ int leg_irq;
+
+ if (d->irq < FIRST_LEGACY_IRQ)
+ return;
+
+ leg_irq = d->irq - FIRST_LEGACY_IRQ;
+ base = ictlr_reg_base[leg_irq >> 5];
+ writel(1 << (leg_irq & 31), base + ICTLR_CPU_IER_SET);
+}
+
+void tegra_fiq_enable(int irq)
+{
+ void __iomem *base = IO_ADDRESS(TEGRA_ARM_PERIF_BASE + 0x100);
+ /* enable FIQ */
+ u32 val = readl(base + GIC_CPU_CTRL);
+ val &= ~8; /* pass FIQs through */
+ val |= 2; /* enableNS */
+ writel(val, base + GIC_CPU_CTRL);
+ tegra_legacy_select_fiq(irq, true);
+ tegra_fiq_unmask(irq_get_irq_data(irq));
+}
+
+void tegra_fiq_disable(int irq)
+{
+ tegra_fiq_mask(irq_get_irq_data(irq));
+ tegra_legacy_select_fiq(irq, false);
+}
diff --git a/arch/arm/mach-tegra/fuse.c b/arch/arm/mach-tegra/fuse.c
index 1fa26d9a1a68..fcc10afb07a1 100644
--- a/arch/arm/mach-tegra/fuse.c
+++ b/arch/arm/mach-tegra/fuse.c
@@ -2,6 +2,7 @@
* arch/arm/mach-tegra/fuse.c
*
* Copyright (C) 2010 Google, Inc.
+ * Copyright (C) 2010-2011 NVIDIA Corp.
*
* Author:
* Colin Cross <ccross@android.com>
@@ -19,24 +20,85 @@
#include <linux/kernel.h>
#include <linux/io.h>
+#include <linux/init.h>
+#include <linux/string.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
#include <mach/iomap.h>
+#include <mach/tegra_fuse.h>
#include "fuse.h"
+#include "apbio.h"
+#define FUSE_SKU_INFO 0x110
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
#define FUSE_UID_LOW 0x108
#define FUSE_UID_HIGH 0x10c
-#define FUSE_SKU_INFO 0x110
#define FUSE_SPARE_BIT 0x200
+#else
+#define FUSE_VENDOR_CODE 0x200
+#define FUSE_VENDOR_CODE_MASK 0xf
+#define FUSE_FAB_CODE 0x204
+#define FUSE_FAB_CODE_MASK 0x3f
+#define FUSE_LOT_CODE_0 0x208
+#define FUSE_LOT_CODE_1 0x20c
+#define FUSE_WAFER_ID 0x210
+#define FUSE_WAFER_ID_MASK 0x3f
+#define FUSE_X_COORDINATE 0x214
+#define FUSE_X_COORDINATE_MASK 0x1ff
+#define FUSE_Y_COORDINATE 0x218
+#define FUSE_Y_COORDINATE_MASK 0x1ff
+#define FUSE_GPU_INFO 0x390
+#define FUSE_GPU_INFO_MASK (1<<2)
+#define FUSE_SPARE_BIT 0x244
+/* fuse registers used in public fuse data read API */
+#define FUSE_TEST_PROGRAM_REVISION_0 0x128
+/* fuse spare bits are used to get Tj-ADT values */
+#define FUSE_SPARE_BIT_0_0 0x244
+#define NUM_TSENSOR_SPARE_BITS 28
+/* tsensor calibration register */
+#define FUSE_TSENSOR_CALIB_0 0x198
+
+#endif
+
+struct tegra_id {
+ enum tegra_chipid chipid;
+ unsigned int major, minor, netlist, patch;
+ enum tegra_revision revision;
+ char *priv;
+};
+
+static struct tegra_id tegra_id;
+static unsigned int tegra_chip_id;
+static unsigned int tegra_chip_rev;
+
+static const char *tegra_revision_name[TEGRA_REVISION_MAX] = {
+ [TEGRA_REVISION_UNKNOWN] = "unknown",
+ [TEGRA_REVISION_A01] = "A01",
+ [TEGRA_REVISION_A02] = "A02",
+ [TEGRA_REVISION_A03] = "A03",
+ [TEGRA_REVISION_A03p] = "A03 prime",
+};
+
+u32 tegra_fuse_readl(unsigned long offset)
+{
+ return tegra_apb_readl(TEGRA_FUSE_BASE + offset);
+}
+
+void tegra_fuse_writel(u32 value, unsigned long offset)
+{
+ tegra_apb_writel(value, TEGRA_FUSE_BASE + offset);
+}
-static inline u32 fuse_readl(unsigned long offset)
+static inline bool get_spare_fuse(int bit)
{
- return readl(IO_TO_VIRT(TEGRA_FUSE_BASE + offset));
+ return tegra_fuse_readl(FUSE_SPARE_BIT + bit * 4);
}
-static inline void fuse_writel(u32 value, unsigned long offset)
+const char *tegra_get_revision_name(void)
{
- writel(value, IO_TO_VIRT(TEGRA_FUSE_BASE + offset));
+ return tegra_revision_name[tegra_get_revision()];
}
void tegra_init_fuse(void)
@@ -44,41 +106,350 @@ void tegra_init_fuse(void)
u32 reg = readl(IO_TO_VIRT(TEGRA_CLK_RESET_BASE + 0x48));
reg |= 1 << 28;
writel(reg, IO_TO_VIRT(TEGRA_CLK_RESET_BASE + 0x48));
+ tegra_init_speedo_data();
+
+ pr_info("Tegra Revision: %s "
+ "SKU: 0x%x CPU Process: %d Core Process: %d\n",
+ tegra_get_revision_name(), tegra_sku_id(),
+ tegra_cpu_process_id(), tegra_core_process_id());
+}
- pr_info("Tegra SKU: %d CPU Process: %d Core Process: %d\n",
- tegra_sku_id(), tegra_cpu_process_id(),
- tegra_core_process_id());
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+int tegra_fuse_get_revision(u32 *rev)
+{
+ return -ENOENT;
}
+EXPORT_SYMBOL(tegra_fuse_get_revision);
+
+int tegra_fuse_get_tsensor_calibration_data(u32 *calib)
+{
+ return -ENOENT;
+}
+EXPORT_SYMBOL(tegra_fuse_get_tsensor_calibration_data);
+
+int tegra_fuse_get_tsensor_spare_bits(u32 *spare_bits)
+{
+ return -ENOENT;
+}
+EXPORT_SYMBOL(tegra_fuse_get_tsensor_spare_bits);
+
+#else
+
+int tegra_fuse_get_revision(u32 *rev)
+{
+ /* fuse revision */
+ *rev = tegra_fuse_readl(FUSE_TEST_PROGRAM_REVISION_0);
+ return 0;
+}
+EXPORT_SYMBOL(tegra_fuse_get_revision);
+
+int tegra_fuse_get_tsensor_calibration_data(u32 *calib)
+{
+ /* tsensor calibration fuse */
+ *calib = tegra_fuse_readl(FUSE_TSENSOR_CALIB_0);
+ return 0;
+}
+EXPORT_SYMBOL(tegra_fuse_get_tsensor_calibration_data);
+
+int tegra_fuse_get_tsensor_spare_bits(u32 *spare_bits)
+{
+ u32 value;
+ int i;
+
+ BUG_ON(NUM_TSENSOR_SPARE_BITS > (sizeof(u32) * 8));
+ if (!spare_bits)
+ return -ENOMEM;
+ *spare_bits = 0;
+ /* spare bits 0-27 */
+ for (i = 0; i < NUM_TSENSOR_SPARE_BITS; i++) {
+ value = tegra_fuse_readl(FUSE_SPARE_BIT_0_0 +
+ (i << 2));
+ if (value)
+ *spare_bits |= BIT(i);
+ }
+ return 0;
+}
+EXPORT_SYMBOL(tegra_fuse_get_tsensor_spare_bits);
+#endif
unsigned long long tegra_chip_uid(void)
{
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
unsigned long long lo, hi;
- lo = fuse_readl(FUSE_UID_LOW);
- hi = fuse_readl(FUSE_UID_HIGH);
+ lo = tegra_fuse_readl(FUSE_UID_LOW);
+ hi = tegra_fuse_readl(FUSE_UID_HIGH);
return (hi << 32ull) | lo;
+#else
+ u64 uid = 0ull;
+ u32 reg;
+ u32 cid;
+ u32 vendor;
+ u32 fab;
+ u32 lot;
+ u32 wafer;
+ u32 x;
+ u32 y;
+ u32 i;
+
+ /* This used to be so much easier in prior chips. Unfortunately, there
+ is no one-stop shopping for the unique id anymore. It must be
+ constructed from various bits of information burned into the fuses
+ during the manufacturing process. The 64-bit unique id is formed
+ by concatenating several bit fields. The notation used for the
+ various fields is <fieldname:size_in_bits> with the UID composed
+ thusly:
+
+ <CID:4><VENDOR:4><FAB:6><LOT:26><WAFER:6><X:9><Y:9>
+
+ Where:
+
+ Field Bits Position Data
+ ------- ---- -------- ----------------------------------------
+ CID 4 60 Chip id (encoded as zero for T30)
+ VENDOR 4 56 Vendor code
+ FAB 6 50 FAB code
+ LOT 26 24 Lot code (5-digit base-36-coded-decimal,
+ re-encoded to 26 bits binary)
+ WAFER 6 18 Wafer id
+ X 9 9 Wafer X-coordinate
+ Y 9 0 Wafer Y-coordinate
+ ------- ----
+ Total 64
+ */
+
+ /* Get the chip id and encode each chip variant as a unique value. */
+ reg = readl(IO_TO_VIRT(TEGRA_APB_MISC_BASE + 0x804));
+ reg = (reg & 0xFF00) >> 8;
+
+ switch (reg) {
+ case TEGRA_CHIPID_TEGRA3:
+ cid = 0;
+ break;
+
+ default:
+ BUG();
+ break;
+ }
+
+ vendor = tegra_fuse_readl(FUSE_VENDOR_CODE) & FUSE_VENDOR_CODE_MASK;
+ fab = tegra_fuse_readl(FUSE_FAB_CODE) & FUSE_FAB_CODE_MASK;
+
+ /* Lot code must be re-encoded from a 5 digit base-36 'BCD' number
+ to a binary number. */
+ lot = 0;
+ reg = tegra_fuse_readl(FUSE_LOT_CODE_0) << 2;
+
+ for (i = 0; i < 5; ++i) {
+ u32 digit = (reg & 0xFC000000) >> 26;
+ BUG_ON(digit >= 36);
+ lot *= 36;
+ lot += digit;
+ reg <<= 6;
+ }
+
+ wafer = tegra_fuse_readl(FUSE_WAFER_ID) & FUSE_WAFER_ID_MASK;
+ x = tegra_fuse_readl(FUSE_X_COORDINATE) & FUSE_X_COORDINATE_MASK;
+ y = tegra_fuse_readl(FUSE_Y_COORDINATE) & FUSE_Y_COORDINATE_MASK;
+
+ uid = ((unsigned long long)cid << 60ull)
+ | ((unsigned long long)vendor << 56ull)
+ | ((unsigned long long)fab << 50ull)
+ | ((unsigned long long)lot << 24ull)
+ | ((unsigned long long)wafer << 18ull)
+ | ((unsigned long long)x << 9ull)
+ | ((unsigned long long)y << 0ull);
+ return uid;
+#endif
+}
+
+unsigned int tegra_spare_fuse(int bit)
+{
+ BUG_ON(bit < 0 || bit > 61);
+ return tegra_fuse_readl(FUSE_SPARE_BIT + bit * 4);
}
int tegra_sku_id(void)
{
int sku_id;
- u32 reg = fuse_readl(FUSE_SKU_INFO);
+ u32 reg = tegra_fuse_readl(FUSE_SKU_INFO);
sku_id = reg & 0xFF;
return sku_id;
}
-int tegra_cpu_process_id(void)
+int tegra_gpu_register_sets(void)
+{
+#ifdef CONFIG_ARCH_TEGRA_HAS_DUAL_3D
+ u32 reg = readl(IO_TO_VIRT(TEGRA_CLK_RESET_BASE + FUSE_GPU_INFO));
+ if (reg & FUSE_GPU_INFO_MASK)
+ return 1;
+ else
+ return 2;
+#else
+ return 1;
+#endif
+}
+
+struct chip_revision {
+ enum tegra_chipid chipid;
+ unsigned int major;
+ unsigned int minor;
+ char prime;
+ enum tegra_revision revision;
+};
+
+#define CHIP_REVISION(id, m, n, p, rev) { \
+ .chipid = TEGRA_CHIPID_##id, \
+ .major = m, \
+ .minor = n, \
+ .prime = p, \
+ .revision = TEGRA_REVISION_##rev }
+
+static struct chip_revision tegra_chip_revisions[] = {
+ CHIP_REVISION(TEGRA2, 1, 2, 0, A02),
+ CHIP_REVISION(TEGRA2, 1, 3, 0, A03),
+ CHIP_REVISION(TEGRA2, 1, 3, 'p', A03p),
+ CHIP_REVISION(TEGRA3, 1, 1, 0, A01),
+ CHIP_REVISION(TEGRA3, 1, 2, 0, A02),
+ CHIP_REVISION(TEGRA3, 1, 3, 0, A03),
+};
+
+static enum tegra_revision tegra_decode_revision(const struct tegra_id *id)
+{
+ enum tegra_revision revision = TEGRA_REVISION_UNKNOWN;
+
+#if defined(CONFIG_TEGRA_SILICON_PLATFORM)
+ int i ;
+ char prime;
+
+ if (id->priv == NULL)
+ prime = 0;
+ else
+ prime = *(id->priv);
+
+ for (i = 0; i < ARRAY_SIZE(tegra_chip_revisions); i++) {
+ if ((id->chipid != tegra_chip_revisions[i].chipid) ||
+ (id->minor != tegra_chip_revisions[i].minor) ||
+ (id->major != tegra_chip_revisions[i].major) ||
+ (prime != tegra_chip_revisions[i].prime))
+ continue;
+
+ revision = tegra_chip_revisions[i].revision;
+ break;
+ }
+
+#elif defined(CONFIG_TEGRA_FPGA_PLATFORM)
+ if ((id->chipid & 0xf0) == TEGRA_CHIPID_TEGRA3) {
+ if ((id->major == 0) && (id->minor == 1)) {
+ unsigned int patch = id->patch & 0xF;
+ if ((id->netlist == 12) && (patch == 12))
+ revision = TEGRA_REVISION_A01;
+ else if ((id->netlist == 12) && (patch > 12))
+ revision = TEGRA_REVISION_A02;
+ else if (id->netlist > 12)
+ revision = TEGRA_REVISION_A02;
+ }
+ }
+#endif
+
+ return revision;
+}
+
+static void tegra_set_tegraid(u32 chipid,
+ u32 major, u32 minor,
+ u32 nlist, u32 patch, const char *priv)
+{
+ tegra_id.chipid = (enum tegra_chipid) chipid;
+ tegra_id.major = major;
+ tegra_id.minor = minor;
+ tegra_id.netlist = nlist;
+ tegra_id.patch = patch;
+ tegra_id.priv = (char *)priv;
+ tegra_id.revision = tegra_decode_revision(&tegra_id);
+}
+
+static void tegra_get_tegraid_from_hw(void)
{
- int cpu_process_id;
- u32 reg = fuse_readl(FUSE_SPARE_BIT);
- cpu_process_id = (reg >> 6) & 3;
- return cpu_process_id;
+ void __iomem *chip_id = IO_ADDRESS(TEGRA_APB_MISC_BASE) + 0x804;
+ void __iomem *netlist = IO_ADDRESS(TEGRA_APB_MISC_BASE) + 0x860;
+ u32 cid = readl(chip_id);
+ u32 nlist = readl(netlist);
+ char *priv = NULL;
+
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+ if (get_spare_fuse(18) || get_spare_fuse(19))
+ priv = "p";
+#endif
+ tegra_set_tegraid((cid >> 8) & 0xff,
+ (cid >> 4) & 0xf,
+ (cid >> 16) & 0xf,
+ (nlist >> 0) & 0xffff,
+ (nlist >> 16) & 0xffff,
+ priv);
}
-int tegra_core_process_id(void)
+enum tegra_chipid tegra_get_chipid(void)
{
- int core_process_id;
- u32 reg = fuse_readl(FUSE_SPARE_BIT);
- core_process_id = (reg >> 12) & 3;
- return core_process_id;
+ if (tegra_id.chipid == TEGRA_CHIPID_UNKNOWN) {
+ /* Boot loader did not pass a valid chip ID.
+ * Get it from hardware */
+ tegra_get_tegraid_from_hw();
+ }
+
+ return tegra_id.chipid;
}
+
+enum tegra_revision tegra_get_revision(void)
+{
+ if (tegra_id.chipid == TEGRA_CHIPID_UNKNOWN) {
+ /* Boot loader did not pass a valid chip ID.
+ * Get it from hardware */
+ tegra_get_tegraid_from_hw();
+ }
+
+ return tegra_id.revision;
+}
+
+static char chippriv[16]; /* Permanent buffer for private string */
+static int __init tegra_bootloader_tegraid(char *str)
+{
+ u32 id[5];
+ int i = 0;
+ char *priv = NULL;
+
+ do {
+ id[i++] = simple_strtoul(str, &str, 16);
+ } while (*str++ && i < ARRAY_SIZE(id));
+
+ if (*(str - 1) == '.') {
+ strncpy(chippriv, str, sizeof(chippriv) - 1);
+ priv = chippriv;
+ if (strlen(str) > sizeof(chippriv) - 1)
+ pr_err("### tegraid.priv in kernel arg truncated\n");
+ }
+
+ while (i < ARRAY_SIZE(id))
+ id[i++] = 0;
+
+ tegra_set_tegraid(id[0], id[1], id[2], id[3], id[4], priv);
+ return 0;
+}
+
+static unsigned int get_chip_id(char *val, struct kernel_param *kp)
+{
+ tegra_chip_id = (unsigned int)tegra_get_chipid();
+ return param_get_uint(val, kp);
+}
+static unsigned int get_chip_rev(char *val, struct kernel_param *kp)
+{
+ tegra_chip_rev = (unsigned int)tegra_get_revision();
+ return param_get_uint(val, kp);
+}
+
+module_param_call(tegra_chip_id, NULL, get_chip_id, &tegra_chip_id, 0444);
+__MODULE_PARM_TYPE(tegra_chip_id, "uint");
+module_param_call(tegra_chip_rev, NULL, get_chip_rev, &tegra_chip_rev, 0444);
+__MODULE_PARM_TYPE(tegra_chip_rev, "uint");
+
+/* tegraid=chipid.major.minor.netlist.patch[.priv] */
+early_param("tegraid", tegra_bootloader_tegraid);
diff --git a/arch/arm/mach-tegra/fuse.h b/arch/arm/mach-tegra/fuse.h
index 584b2e27dbda..88ea8402978a 100644
--- a/arch/arm/mach-tegra/fuse.h
+++ b/arch/arm/mach-tegra/fuse.h
@@ -1,7 +1,8 @@
/*
- * arch/arm/mach-tegra/fuse.c
+ * arch/arm/mach-tegra/fuse.h
*
* Copyright (C) 2010 Google, Inc.
+ * Copyright (C) 2010-2011 NVIDIA Corp.
*
* Author:
* Colin Cross <ccross@android.com>
@@ -17,8 +18,46 @@
*
*/
+#include <mach/hardware.h>
+
+#define INVALID_PROCESS_ID 99 /* don't expect to have 100 process id's */
+
unsigned long long tegra_chip_uid(void);
+unsigned int tegra_spare_fuse(int bit);
int tegra_sku_id(void);
+void tegra_init_fuse(void);
+u32 tegra_fuse_readl(unsigned long offset);
+void tegra_fuse_writel(u32 value, unsigned long offset);
+const char *tegra_get_revision_name(void);
+
+#ifdef CONFIG_TEGRA_SILICON_PLATFORM
+
int tegra_cpu_process_id(void);
int tegra_core_process_id(void);
-void tegra_init_fuse(void);
+int tegra_soc_speedo_id(void);
+void tegra_init_speedo_data(void);
+
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+int tegra_package_id(void);
+int tegra_cpu_speedo_id(void);
+int tegra_cpu_speedo_mv(void);
+int tegra_core_speedo_mv(void);
+#else
+static inline int tegra_package_id(void) { return -1; }
+static inline int tegra_cpu_speedo_id(void) { return 0; }
+static inline int tegra_cpu_speedo_mv(void) { return 1000; }
+static inline int tegra_core_speedo_mv(void) { return 1200; }
+#endif
+
+#else
+
+static inline int tegra_cpu_process_id(void) { return 0; }
+static inline int tegra_core_process_id(void) { return 0; }
+static inline int tegra_cpu_speedo_id(void) { return 0; }
+static inline int tegra_soc_speedo_id(void) { return 0; }
+static inline int tegra_package_id(void) { return -1; }
+static inline int tegra_cpu_speedo_mv(void) { return 1000; }
+static inline int tegra_core_speedo_mv(void) { return 1200; }
+static inline void tegra_init_speedo_data(void) { }
+
+#endif
diff --git a/arch/arm/mach-tegra/gic.c b/arch/arm/mach-tegra/gic.c
new file mode 100644
index 000000000000..50cecc4eed64
--- /dev/null
+++ b/arch/arm/mach-tegra/gic.c
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2010-2011, NVIDIA Corporation
+ *
+ * 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/cpumask.h> /* Required by asm/hardware/gic.h */
+#include <linux/io.h>
+#include <linux/irqnr.h>
+
+#include <asm/hardware/gic.h>
+
+#include <mach/iomap.h>
+#include <mach/irqs.h>
+
+#include "gic.h"
+#include "pm.h"
+
+#if defined(CONFIG_HOTPLUG_CPU) || defined(CONFIG_PM_SLEEP)
+static void __iomem *gic_cpu_base = IO_ADDRESS(TEGRA_ARM_PERIF_BASE + 0x100);
+
+void tegra_gic_cpu_disable(void)
+{
+ writel(0, gic_cpu_base + GIC_CPU_CTRL);
+}
+
+void tegra_gic_cpu_enable(void)
+{
+ writel(1, gic_cpu_base + GIC_CPU_CTRL);
+}
+
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+
+void tegra_gic_pass_through_disable(void)
+{
+ u32 val = readl(gic_cpu_base + GIC_CPU_CTRL);
+ val |= 2; /* enableNS = disable GIC pass through */
+ writel(val, gic_cpu_base + GIC_CPU_CTRL);
+}
+
+#endif
+#endif
+
+#if defined(CONFIG_PM_SLEEP)
+
+int tegra_gic_pending_interrupt(void)
+{
+ u32 irq = readl(gic_cpu_base + GIC_CPU_HIGHPRI);
+ irq &= 0x3FF;
+
+ return irq;
+}
+
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+
+static void __iomem *gic_dist_base = IO_ADDRESS(TEGRA_ARM_INT_DIST_BASE);
+static u32 gic_affinity[INT_GIC_NR/4];
+
+void tegra_gic_dist_disable(void)
+{
+ writel(0, gic_dist_base + GIC_DIST_CTRL);
+}
+
+void tegra_gic_dist_enable(void)
+{
+ writel(1, gic_dist_base + GIC_DIST_CTRL);
+}
+
+void tegra_gic_disable_affinity(void)
+{
+ unsigned int i;
+
+ BUG_ON(is_lp_cluster());
+
+ /* The GIC distributor TARGET register is one byte per IRQ. */
+ for (i = 32; i < INT_GIC_NR; i += 4) {
+ /* Save the affinity. */
+ gic_affinity[i/4] = __raw_readl(gic_dist_base +
+ GIC_DIST_TARGET + i);
+
+ /* Force this interrupt to CPU0. */
+ __raw_writel(0x01010101, gic_dist_base + GIC_DIST_TARGET + i);
+ }
+
+ wmb();
+}
+
+void tegra_gic_restore_affinity(void)
+{
+ unsigned int i;
+
+ BUG_ON(is_lp_cluster());
+
+ /* The GIC distributor TARGET register is one byte per IRQ. */
+ for (i = 32; i < INT_GIC_NR; i += 4) {
+#ifdef CONFIG_BUG
+ u32 reg = __raw_readl(gic_dist_base + GIC_DIST_TARGET + i);
+ if (reg & 0xFEFEFEFE)
+ panic("GIC affinity changed!");
+#endif
+ /* Restore this interrupt's affinity. */
+ __raw_writel(gic_affinity[i/4], gic_dist_base +
+ GIC_DIST_TARGET + i);
+ }
+
+ wmb();
+}
+
+void tegra_gic_affinity_to_cpu0(void)
+{
+ unsigned int i;
+
+ BUG_ON(is_lp_cluster());
+
+ for (i = 32; i < INT_GIC_NR; i += 4)
+ __raw_writel(0x01010101, gic_dist_base + GIC_DIST_TARGET + i);
+ wmb();
+}
+#endif
+#endif
diff --git a/arch/arm/mach-tegra/gic.h b/arch/arm/mach-tegra/gic.h
new file mode 100644
index 000000000000..94dab6e581af
--- /dev/null
+++ b/arch/arm/mach-tegra/gic.h
@@ -0,0 +1,49 @@
+/*
+ * arch/arm/mach-tegra/include/mach/gic.h
+ *
+ * Copyright (C) 2010-2011 NVIDIA Corporation
+ *
+ * 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 _MACH_TEGRA_GIC_H_
+#define _MACH_TEGRA_GIC_H_
+
+#if defined(CONFIG_HOTPLUG_CPU) || defined(CONFIG_PM_SLEEP)
+
+void tegra_gic_cpu_disable(void);
+void tegra_gic_cpu_enable(void);
+
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+
+void tegra_gic_pass_through_disable(void);
+
+#endif
+#endif
+
+
+#if defined(CONFIG_PM_SLEEP)
+
+int tegra_gic_pending_interrupt(void);
+
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+
+void tegra_gic_dist_disable(void);
+void tegra_gic_dist_enable(void);
+
+void tegra_gic_disable_affinity(void);
+void tegra_gic_restore_affinity(void);
+void tegra_gic_affinity_to_cpu0(void);
+
+#endif
+#endif
+
+#endif /* _MACH_TEGRA_GIC_H_ */
diff --git a/arch/arm/mach-tegra/gpio-names.h b/arch/arm/mach-tegra/gpio-names.h
index f28220a641b2..cb3c5ce29c0f 100644
--- a/arch/arm/mach-tegra/gpio-names.h
+++ b/arch/arm/mach-tegra/gpio-names.h
@@ -2,6 +2,7 @@
* arch/arm/mach-tegra/include/mach/gpio-names.h
*
* Copyright (c) 2010 Google, Inc
+ * Copyright (C) 2011 NVIDIA Corporation.
*
* Author:
* Erik Gilling <konkers@google.com>
@@ -243,5 +244,26 @@
#define TEGRA_GPIO_PBB5 221
#define TEGRA_GPIO_PBB6 222
#define TEGRA_GPIO_PBB7 223
-
+#define TEGRA_GPIO_PCC0 224
+#define TEGRA_GPIO_PCC1 225
+#define TEGRA_GPIO_PCC2 226
+#define TEGRA_GPIO_PCC3 227
+#define TEGRA_GPIO_PCC4 228
+#define TEGRA_GPIO_PCC5 229
+#define TEGRA_GPIO_PCC6 230
+#define TEGRA_GPIO_PCC7 231
+#define TEGRA_GPIO_PDD0 232
+#define TEGRA_GPIO_PDD1 233
+#define TEGRA_GPIO_PDD2 234
+#define TEGRA_GPIO_PDD3 235
+#define TEGRA_GPIO_PDD4 236
+#define TEGRA_GPIO_PDD5 237
+#define TEGRA_GPIO_PDD6 238
+#define TEGRA_GPIO_PDD7 239
+#define TEGRA_GPIO_PEE0 240
+#define TEGRA_GPIO_PEE1 241
+#define TEGRA_GPIO_PEE2 242
+#define TEGRA_GPIO_PEE3 243
+#define TEGRA_GPIO_INVALID 244
+#define TEGRA_MAX_GPIO 245
#endif
diff --git a/arch/arm/mach-tegra/headsmp.S b/arch/arm/mach-tegra/headsmp.S
index b5349b2f13d2..4763528a5f16 100644
--- a/arch/arm/mach-tegra/headsmp.S
+++ b/arch/arm/mach-tegra/headsmp.S
@@ -1,61 +1,313 @@
+/*
+ * arch/arm/mach-tegra/headsmp.S
+ *
+ * CPU initialization routines for Tegra SoCs
+ *
+ * Copyright (c) 2009-2011, NVIDIA Corporation.
+ * Copyright (c) 2011 Google, Inc.
+ * Author: Colin Cross <ccross@android.com>
+ * Gary King <gking@nvidia.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.
+ *
+ * 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/linkage.h>
#include <linux/init.h>
- .section ".text.head", "ax"
- __CPUINIT
+#include <asm/assembler.h>
+#include <asm/cache.h>
+#include <asm/page.h>
+
+#include <mach/iomap.h>
+#include <mach/io.h>
+
+#include "asm_macros.h"
+#include "reset.h"
+#include "sleep.h"
+
+#define DEBUG_CPU_RESET_HANDLER 0 /* Non-zero enables debug code */
+
+#define PMC_SCRATCH41 0x140
+#define RESET_DATA(x) ((TEGRA_RESET_##x)*4)
+
+
+#ifdef CONFIG_SMP
/*
- * Tegra specific entry point for secondary CPUs.
- * The secondary kernel init calls v7_flush_dcache_all before it enables
- * the L1; however, the L1 comes out of reset in an undefined state, so
- * the clean + invalidate performed by v7_flush_dcache_all causes a bunch
- * of cache lines with uninitialized data and uninitialized tags to get
- * written out to memory, which does really unpleasant things to the main
- * processor. We fix this by performing an invalidate, rather than a
- * clean + invalidate, before jumping into the kernel.
+ * tegra_secondary_startup
+ *
+ * Initial secondary processor boot vector; jumps to kernel's
+ * secondary_startup routine. Used for initial boot and hotplug
+ * of secondary CPUs.
*/
-ENTRY(v7_invalidate_l1)
- mov r0, #0
- mcr p15, 2, r0, c0, c0, 0
- mrc p15, 1, r0, c0, c0, 0
-
- ldr r1, =0x7fff
- and r2, r1, r0, lsr #13
-
- ldr r1, =0x3ff
-
- and r3, r1, r0, lsr #3 @ NumWays - 1
- add r2, r2, #1 @ NumSets
-
- and r0, r0, #0x7
- add r0, r0, #4 @ SetShift
-
- clz r1, r3 @ WayShift
- add r4, r3, #1 @ NumWays
-1: sub r2, r2, #1 @ NumSets--
- mov r3, r4 @ Temp = NumWays
-2: subs r3, r3, #1 @ Temp--
- mov r5, r3, lsl r1
- mov r6, r2, lsl r0
- orr r5, r5, r6 @ Reg = (Temp<<WayShift)|(NumSets<<SetShift)
- mcr p15, 0, r5, c7, c6, 2
- bgt 2b
- cmp r2, #0
- bgt 1b
- dsb
- isb
- mov pc, lr
-ENDPROC(v7_invalidate_l1)
-
ENTRY(tegra_secondary_startup)
- msr cpsr_fsxc, #0xd3
- bl v7_invalidate_l1
- mrc p15, 0, r0, c0, c0, 5
- and r0, r0, #15
- ldr r1, =0x6000f100
- str r0, [r1]
-1: ldr r2, [r1]
- cmp r0, r2
- beq 1b
- b secondary_startup
+ bl tegra_invalidate_l1
+ bl tegra_enable_coresite
+ b secondary_startup
ENDPROC(tegra_secondary_startup)
+#endif
+
+#ifdef CONFIG_PM_SLEEP
+/*
+ * tegra_resume
+ *
+ * CPU boot vector when restarting the a CPU following
+ * an LP2 transition. Also branched to by LP0 and LP1 resume after
+ * re-enabling sdram.
+ */
+ENTRY(tegra_resume)
+ bl tegra_enable_coresite
+ bl tegra_invalidate_l1
+
+ cpu_id r0
+ cmp r0, #0 @ CPU0?
+ bne tegra_cpu_resume_phys @ no
+
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+ @ Clear the flow controller flags for this CPU.
+ mov32 r2, TEGRA_FLOW_CTRL_BASE+8 @ CPU0 CSR
+ ldr r1, [r2]
+ orr r1, r1, #(1 << 15) | (1 << 14) @ write to clear event & intr
+ movw r0, #0x0FFD @ enable, cluster_switch, immed, & bitmaps
+ bic r1, r1, r0
+ str r1, [r2]
+#endif
+
+ /* enable SCU */
+ mov32 r0, TEGRA_ARM_PERIF_BASE
+ ldr r1, [r0]
+ orr r1, r1, #1
+ str r1, [r0]
+
+#ifdef CONFIG_TRUSTED_FOUNDATIONS
+ /* wake up (should have specified args?) */
+ bl tegra_generic_smc
+#endif
+
+ b tegra_cpu_resume_phys
+ENDPROC(tegra_resume)
+#endif
+
+/*
+ * tegra_invalidate_l1
+ *
+ * Invalidates the L1 data cache (no clean) during initial boot of a cpu
+ *
+ * Corrupted registers: r0-r6
+ */
+tegra_invalidate_l1:
+ mov r0, #0
+ mcr p15, 2, r0, c0, c0, 0
+ mrc p15, 1, r0, c0, c0, 0
+
+ movw r1, #0x7fff
+ and r2, r1, r0, lsr #13
+
+ movw r1, #0x3ff
+
+ and r3, r1, r0, lsr #3 @ NumWays - 1
+ add r2, r2, #1 @ NumSets
+
+ and r0, r0, #0x7
+ add r0, r0, #4 @ SetShift
+
+ clz r1, r3 @ WayShift
+ add r4, r3, #1 @ NumWays
+1: sub r2, r2, #1 @ NumSets--
+ mov r3, r4 @ Temp = NumWays
+2: subs r3, r3, #1 @ Temp--
+ mov r5, r3, lsl r1
+ mov r6, r2, lsl r0
+ orr r5, r5, r6 @ Reg = (Temp<<WayShift)|(NumSets<<SetShift)
+ mcr p15, 0, r5, c7, c6, 2
+ bgt 2b
+ cmp r2, #0
+ bgt 1b
+ dsb
+ isb
+ mov pc, lr
+
+ /* Enable Coresight access on cpu */
+tegra_enable_coresite:
+ mov32 r0, 0xC5ACCE55
+ mcr p14, 0, r0, c7, c12, 6
+ mov pc, lr
+
+/*
+ * __tegra_cpu_reset_handler_halt_failed:
+ *
+ * Alternate entry point for reset handler for cases where the
+ * WFI halt failed to take effect.
+ *
+ */
+ .align L1_CACHE_SHIFT
+ENTRY(__tegra_cpu_reset_handler_start)
+
+/*
+ * __tegra_cpu_reset_handler:
+ *
+ * Common handler for all CPU reset events.
+ *
+ * Register usage within the reset handler:
+ *
+ * R7 = CPU present (to the OS) mask
+ * R8 = CPU in LP1 state mask
+ * R9 = CPU in LP2 state mask
+ * R10 = CPU number
+ * R11 = CPU mask
+ * R12 = pointer to reset handler data
+ *
+ * NOTE: This code is copied to IRAM. All code and data accesses
+ * must be position-independent.
+ */
+
+ .align L1_CACHE_SHIFT
+ENTRY(__tegra_cpu_reset_handler)
+
+#if DEBUG_CPU_RESET_HANDLER
+ mov32 r0, 0xC5ACCE55
+ mcr p14, 0, r0, c7, c12, 6 @ Enable CoreSight access
+ b .
+#endif
+#ifndef CONFIG_TRUSTED_FOUNDATIONS
+ cpsid aif, 0x13 @ SVC mode, interrupts disabled
+ mrc p15, 0, r0, c0, c0, 0 @ read main ID register
+ and r5, r0, #0x00f00000 @ variant
+ and r6, r0, #0x0000000f @ revision
+ orr r6, r6, r5, lsr #20-4 @ combine variant and revision
+#ifdef CONFIG_ARM_ERRATA_743622
+ teq r6, #0x20 @ present in r2p0
+ teqne r6, #0x21 @ present in r2p1
+ teqne r6, #0x22 @ present in r2p2
+ teqne r6, #0x27 @ present in r2p7
+ teqne r6, #0x29 @ present in r2p9
+ mrceq p15, 0, r10, c15, c0, 1 @ read diagnostic register
+ orreq r10, r10, #1 << 6 @ set bit #6
+ mcreq p15, 0, r10, c15, c0, 1 @ write diagnostic register
+#endif
+#endif
+ mrc p15, 0, r10, c0, c0, 5 @ MPIDR
+ and r10, r10, #0x3 @ R10 = CPU number
+ mov r11, #1
+ mov r11, r11, lsl r10 @ R11 = CPU mask
+ adr r12, __tegra_cpu_reset_handler_data
+
+#ifdef CONFIG_SMP
+ /* Does the OS know about this CPU? */
+ ldr r7, [r12, #RESET_DATA(MASK_PRESENT)]
+ tst r7, r11 @ if !present
+ bleq __die @ CPU not present (to OS)
+#endif
+
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+ /* If CPU1, don't let CPU0 reset CPU1 now that CPU1 is coming up. */
+ mov32 r6, TEGRA_PMC_BASE
+ mov r0, #0
+ cmp r10, #0
+ strne r0, [r6, #PMC_SCRATCH41]
+#endif
+
+#ifdef CONFIG_PM_SLEEP
+ /* Waking up from LP1? */
+ ldr r8, [r12, #RESET_DATA(MASK_LP1)]
+ tst r8, r11 @ if in_lp1
+ beq __is_not_lp1
+ cmp r10, #0
+ bne __die @ only CPU0 can be here
+ ldr lr, [r12, #RESET_DATA(STARTUP_LP1)]
+ cmp lr, #0
+ bleq __die @ no LP1 startup handler
+ bx lr
+__is_not_lp1:
+#endif
+
+ /* Waking up from LP2? */
+ ldr r9, [r12, #RESET_DATA(MASK_LP2)]
+ tst r9, r11 @ if in_lp2
+ beq __is_not_lp2
+ ldr lr, [r12, #RESET_DATA(STARTUP_LP2)]
+ cmp lr, #0
+ bleq __die @ no LP2 startup handler
+ bx lr
+
+__is_not_lp2:
+
+#ifdef CONFIG_SMP
+ /* Can only be secondary boot (initial or hotplug) but CPU 0
+ cannot be here. */
+ cmp r10, #0
+ bleq __die @ CPU0 cannot be here
+ ldr lr, [r12, #RESET_DATA(STARTUP_SECONDARY)]
+ cmp lr, #0
+ bleq __die @ no secondary startup handler
+ bx lr
+#endif
+
+/*
+ * We don't know why the CPU reset. Just kill it.
+ * The LR register will contain the address we died at + 4.
+ */
+
+__die:
+ sub lr, lr, #4
+ mov32 r7, TEGRA_PMC_BASE
+ str lr, [r7, #PMC_SCRATCH41]
+
+ mov32 r7, TEGRA_CLK_RESET_BASE
+
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+ mov32 r0, 0x1111
+ mov r1, r0, lsl r10
+ str r1, [r7, #0x340] @ CLK_RST_CPU_CMPLX_SET
+#else
+ mov32 r6, TEGRA_FLOW_CTRL_BASE
+
+ cmp r10, #0
+ moveq r1, #FLOW_CTRL_HALT_CPU0_EVENTS
+ moveq r2, #FLOW_CTRL_CPU0_CSR
+ movne r1, r10, lsl #3
+ addne r2, r1, #(FLOW_CTRL_CPU1_CSR-8)
+ addne r1, r1, #(FLOW_CTRL_HALT_CPU1_EVENTS-8)
+
+ /* Clear CPU "event" and "interrupt" flags and power gate
+ it when halting but not before it is in the "WFI" state. */
+ ldr r0, [r6, +r2]
+ orr r0, r0, #FLOW_CTRL_CSR_INTR_FLAG | FLOW_CTRL_CSR_EVENT_FLAG
+ orr r0, r0, #FLOW_CTRL_CSR_ENABLE
+ str r0, [r6, +r2]
+
+ /* Unconditionally halt this CPU */
+ mov r0, #FLOW_CTRL_WAITEVENT
+ str r0, [r6, +r1]
+ ldr r0, [r6, +r1] @ memory barrier
+
+ dsb
+ isb
+ wfi @ CPU should be power gated here
+
+ /* If the CPU didn't power gate above just kill it's clock. */
+
+ mov r0, r11, lsl #8
+ str r0, [r7, #348] @ CLK_CPU_CMPLX_SET
+#endif
+ /* If the CPU still isn't dead, just spin here. */
+ b .
+
+ENDPROC(__tegra_cpu_reset_handler)
+ .align L1_CACHE_SHIFT
+ .type __tegra_cpu_reset_handler_data, %object
+ .globl __tegra_cpu_reset_handler_data
+__tegra_cpu_reset_handler_data:
+ .rept TEGRA_RESET_DATA_SIZE
+ .long 0
+ .endr
+ .size __tegra_cpu_reset_handler_data, .-tegra_cpu_reset_handler_data
+ .align L1_CACHE_SHIFT
+ENTRY(__tegra_cpu_reset_handler_end)
diff --git a/arch/arm/mach-tegra/hotplug.c b/arch/arm/mach-tegra/hotplug.c
index f3294040d357..d44e457e7182 100644
--- a/arch/arm/mach-tegra/hotplug.c
+++ b/arch/arm/mach-tegra/hotplug.c
@@ -1,119 +1,83 @@
/*
- * linux/arch/arm/mach-realview/hotplug.c
+ * arch/arm/mach-tegra/hotplug.c
*
- * Copyright (C) 2002 ARM Ltd.
- * All Rights Reserved
+ * Copyright (C) 2010-2011 NVIDIA Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/kernel.h>
-#include <linux/errno.h>
+#include <linux/io.h>
#include <linux/smp.h>
+#include <asm/cpu_pm.h>
#include <asm/cacheflush.h>
-static inline void cpu_enter_lowpower(void)
-{
- unsigned int v;
+#include <mach/iomap.h>
- flush_cache_all();
- asm volatile(
- " mcr p15, 0, %1, c7, c5, 0\n"
- " mcr p15, 0, %1, c7, c10, 4\n"
- /*
- * Turn off coherency
- */
- " mrc p15, 0, %0, c1, c0, 1\n"
- " bic %0, %0, #0x20\n"
- " mcr p15, 0, %0, c1, c0, 1\n"
- " mrc p15, 0, %0, c1, c0, 0\n"
- " bic %0, %0, %2\n"
- " mcr p15, 0, %0, c1, c0, 0\n"
- : "=&r" (v)
- : "r" (0), "Ir" (CR_C)
- : "cc");
-}
+#include "gic.h"
+#include "sleep.h"
-static inline void cpu_leave_lowpower(void)
-{
- unsigned int v;
-
- asm volatile(
- "mrc p15, 0, %0, c1, c0, 0\n"
- " orr %0, %0, %1\n"
- " mcr p15, 0, %0, c1, c0, 0\n"
- " mrc p15, 0, %0, c1, c0, 1\n"
- " orr %0, %0, #0x20\n"
- " mcr p15, 0, %0, c1, c0, 1\n"
- : "=&r" (v)
- : "Ir" (CR_C)
- : "cc");
-}
+#define CPU_CLOCK(cpu) (0x1<<(8+cpu))
-static inline void platform_do_lowpower(unsigned int cpu, int *spurious)
-{
- /*
- * there is no power-control hardware on this platform, so all
- * we can do is put the core into WFI; this is safe as the calling
- * code will have already disabled interrupts
- */
- for (;;) {
- /*
- * here's the WFI
- */
- asm(".word 0xe320f003\n"
- :
- :
- : "memory", "cc");
-
- /*if (pen_release == cpu) {*/
- /*
- * OK, proper wakeup, we're done
- */
- break;
- /*}*/
-
- /*
- * Getting here, means that we have come out of WFI without
- * having been woken up - this shouldn't happen
- *
- * Just note it happening - when we're woken, we can report
- * its occurrence.
- */
- (*spurious)++;
- }
-}
+#define CLK_RST_CONTROLLER_CLK_CPU_CMPLX \
+ (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x4c)
+#define CLK_RST_CONTROLLER_RST_CPU_CMPLX_SET \
+ (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x340)
+#define CLK_RST_CONTROLLER_RST_CPU_CMPLX_CLR \
+ (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x344)
+
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+/* For Tegra2 use the software-written value of the reset register for status.*/
+#define CLK_RST_CONTROLLER_CPU_CMPLX_STATUS CLK_RST_CONTROLLER_RST_CPU_CMPLX_SET
+#else
+#define CLK_RST_CONTROLLER_CPU_CMPLX_STATUS \
+ (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x470)
+#endif
int platform_cpu_kill(unsigned int cpu)
{
+ unsigned int reg;
+
+ do {
+ reg = readl(CLK_RST_CONTROLLER_CPU_CMPLX_STATUS);
+ cpu_relax();
+ } while (!(reg & (1<<cpu)));
+
+ reg = readl(CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
+ writel(reg | CPU_CLOCK(cpu), CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
+
return 1;
}
-/*
- * platform-specific code to shutdown a CPU
- *
- * Called with IRQs disabled
- */
void platform_cpu_die(unsigned int cpu)
{
- int spurious = 0;
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+ /* Flush the L1 data cache. */
+ flush_cache_all();
- /*
- * we're ready for shutdown now, so do it
- */
- cpu_enter_lowpower();
- platform_do_lowpower(cpu, &spurious);
+ /* Place the current CPU in reset. */
+ tegra2_hotplug_shutdown();
+#else
+ /* Disable GIC CPU interface for this CPU. */
+ tegra_gic_cpu_disable();
- /*
- * bring this CPU back into the world of cache
- * coherency, and then restore interrupts
- */
- cpu_leave_lowpower();
+ /* Tegra3 enters LPx states via WFI - do not propagate legacy IRQs
+ to CPU core to avoid fall through WFI; then GIC output will be
+ enabled, however at this time - CPU is dying - no interrupt should
+ have affinity to this CPU. */
+ tegra_gic_pass_through_disable();
+
+ /* Flush the L1 data cache. */
+ flush_cache_all();
+
+ /* Shut down the current CPU. */
+ tegra3_hotplug_shutdown();
+#endif
- if (spurious)
- pr_warn("CPU%u: %u spurious wakeup calls\n", cpu, spurious);
+ /* Should never return here. */
+ BUG();
}
int platform_cpu_disable(unsigned int cpu)
diff --git a/arch/arm/mach-tegra/i2c_error_recovery.c b/arch/arm/mach-tegra/i2c_error_recovery.c
new file mode 100644
index 000000000000..a3ac4e122a8f
--- /dev/null
+++ b/arch/arm/mach-tegra/i2c_error_recovery.c
@@ -0,0 +1,103 @@
+/*
+ * arch/arm/mach-tegra/i2c_error_recovery.c
+ *
+ * Copyright (c) 2011, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+
+#include "board.h"
+
+#define RETRY_MAX_COUNT (9*8+1) /*I2C controller supports eight-byte burst transfer*/
+
+int arb_lost_recovery(int scl_gpio, int sda_gpio)
+{
+ int ret;
+ int retry = RETRY_MAX_COUNT;
+ int recovered_successfully = 0;
+ int val;
+
+ if ((!scl_gpio) || (!sda_gpio)) {
+ pr_err("not proper input:scl_gpio 0x%08x,"
+ "sda_gpio 0x%08x\n", scl_gpio, sda_gpio);
+ return -EINVAL;;
+ }
+
+ ret = gpio_request(scl_gpio, "scl_gpio");
+ if (ret < 0) {
+ pr_err("error in gpio 0x%08x request 0x%08x\n",
+ scl_gpio, ret);
+ return -EINVAL;;
+ }
+ tegra_gpio_enable(scl_gpio);
+
+ ret = gpio_request(sda_gpio, "sda_gpio");
+ if (ret < 0) {
+ pr_err("error in gpio 0x%08x request 0x%08x\n",
+ sda_gpio, ret);
+ goto err;
+ }
+ tegra_gpio_enable(sda_gpio);
+ gpio_direction_input(sda_gpio);
+
+ while (retry--) {
+ gpio_direction_output(scl_gpio,0);
+ udelay(5);
+ gpio_direction_output(scl_gpio,1);
+ udelay(5);
+
+ /* check whether sda struct low release */
+ val = gpio_get_value(sda_gpio);
+ if (val) {
+ /* send START */
+ gpio_direction_output(sda_gpio,0);
+ udelay(5);
+
+ /* send STOP in next clock cycle */
+ gpio_direction_output(scl_gpio,0);
+ udelay(5);
+ gpio_direction_output(scl_gpio,1);
+ udelay(5);
+ gpio_direction_output(sda_gpio,1);
+ udelay(5);
+
+ recovered_successfully = 1;
+ break;
+ }
+ }
+
+ gpio_free(scl_gpio);
+ tegra_gpio_disable(scl_gpio);
+ gpio_free(sda_gpio);
+ tegra_gpio_disable(sda_gpio);
+
+ if (likely(recovered_successfully)) {
+ pr_err("arbitration lost recovered by re-try-count 0x%08x\n",
+ RETRY_MAX_COUNT - retry);
+ return 0;
+ } else {
+ pr_err("Un-recovered arbitration lost.\n");
+ return -EINVAL;
+ }
+
+err:
+ gpio_free(scl_gpio);
+ tegra_gpio_disable(scl_gpio);
+ return ret;
+}
+
diff --git a/arch/arm/mach-tegra/include/mach/arb_sema.h b/arch/arm/mach-tegra/include/mach/arb_sema.h
new file mode 100644
index 000000000000..9283f079cf61
--- /dev/null
+++ b/arch/arm/mach-tegra/include/mach/arb_sema.h
@@ -0,0 +1,35 @@
+/*
+ * arch/arm/mach-tegra/include/mach/arb_sema.h
+ *
+ * Hardware arbitration semaphore interface
+ *
+ * Copyright (c) 2010, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __MACH_TEGRA_ARB_SEMA_H
+#define __MACH_TEGRA_ARB_SEMA_H
+
+enum tegra_arb_module {
+ TEGRA_ARB_BSEV = 0,
+ TEGRA_ARB_BSEA,
+};
+
+int tegra_arb_mutex_lock_timeout(enum tegra_arb_module lock, int msecs);
+
+int tegra_arb_mutex_unlock(enum tegra_arb_module lock);
+
+#endif
diff --git a/arch/arm/mach-tegra/include/mach/audio.h b/arch/arm/mach-tegra/include/mach/audio.h
new file mode 100644
index 000000000000..5950ececae00
--- /dev/null
+++ b/arch/arm/mach-tegra/include/mach/audio.h
@@ -0,0 +1,57 @@
+/*
+ * arch/arm/mach-tegra/include/mach/audio.h
+ *
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * Author:
+ * Iliyan Malchev <malchev@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.
+ *
+ */
+
+#ifndef __ARCH_ARM_MACH_TEGRA_AUDIO_H
+#define __ARCH_ARM_MACH_TEGRA_AUDIO_H
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <mach/i2s.h>
+
+#define FIFO1 0
+#define FIFO2 1
+
+/* FIXME: this is not enforced by the hardware. */
+#define I2S_FIFO_TX FIFO1
+#define I2S_FIFO_RX FIFO2
+
+#define TEGRA_AUDIO_ENABLE_TX 1
+#define TEGRA_AUDIO_ENABLE_RX 2
+
+struct tegra_audio_platform_data {
+ bool i2s_master;
+ bool dsp_master;
+ int i2s_master_clk; /* When I2S mode and master, the framesync rate. */
+ int dsp_master_clk; /* When DSP mode and master, the framesync rate. */
+ bool dma_on;
+ unsigned long i2s_clk_rate;
+ const char *dap_clk;
+ const char *audio_sync_clk;
+
+ int mode; /* I2S, LJM, RJM, etc. */
+ int fifo_fmt;
+ int bit_size;
+ int i2s_bus_width; /* 32-bit for 16-bit packed I2S */
+ int dsp_bus_width; /* 16-bit for DSP data format */
+ int mask; /* enable tx and rx? */
+ bool stereo_capture; /* True if hardware supports stereo */
+ void *driver_data;
+};
+
+#endif /* __ARCH_ARM_MACH_TEGRA_AUDIO_H */
diff --git a/arch/arm/mach-tegra/include/mach/clk.h b/arch/arm/mach-tegra/include/mach/clk.h
index c8baf8f80d23..abd7b11dc8b9 100644
--- a/arch/arm/mach-tegra/include/mach/clk.h
+++ b/arch/arm/mach-tegra/include/mach/clk.h
@@ -6,6 +6,8 @@
* Author:
* Erik Gilling <konkers@google.com>
*
+ * Copyright (C) 2010-2011 NVIDIA Corporation
+ *
* 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.
@@ -21,11 +23,47 @@
#define __MACH_CLK_H
struct clk;
+struct dvfs;
+struct notifier_block;
+
+enum tegra_clk_ex_param {
+ TEGRA_CLK_VI_INP_SEL,
+ TEGRA_CLK_DTV_INVERT,
+ TEGRA_CLK_NAND_PAD_DIV2_ENB,
+ TEGRA_CLK_PLLD_CSI_OUT_ENB,
+ TEGRA_CLK_PLLD_DSI_OUT_ENB,
+ TEGRA_CLK_PLLD_MIPI_MUX_SEL,
+};
void tegra_periph_reset_deassert(struct clk *c);
void tegra_periph_reset_assert(struct clk *c);
+#ifdef CONFIG_TEGRA_SILICON_PLATFORM
+int tegra_dvfs_set_rate(struct clk *c, unsigned long rate);
+#else
+static inline int tegra_dvfs_set_rate(struct clk *c, unsigned long rate)
+{ return 0; }
+#endif
unsigned long clk_get_rate_all_locked(struct clk *c);
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
void tegra_sdmmc_tap_delay(struct clk *c, int delay);
+#else
+static inline void tegra_sdmmc_tap_delay(struct clk *c, int delay)
+{
+}
+#endif
+int tegra_dvfs_rail_disable_by_name(const char *reg_id);
+int tegra_clk_cfg_ex(struct clk *c, enum tegra_clk_ex_param p, u32 setting);
+int tegra_register_clk_rate_notifier(struct clk *c, struct notifier_block *nb);
+void tegra_unregister_clk_rate_notifier(
+ struct clk *c, struct notifier_block *nb);
+
+/**
+ * tegra_is_clk_enabled - get info if the clk is enabled or not
+ * @clk: clock source
+ *
+ * Returns refcnt.
+ */
+int tegra_is_clk_enabled(struct clk *clk);
#endif
diff --git a/arch/arm/mach-tegra/include/mach/csi.h b/arch/arm/mach-tegra/include/mach/csi.h
new file mode 100644
index 000000000000..8797f98f3cc4
--- /dev/null
+++ b/arch/arm/mach-tegra/include/mach/csi.h
@@ -0,0 +1,38 @@
+/*
+ * arch/arm/mach-tegra/include/mach/csi.h
+ *
+ * Copyright (C) 2010-2011 NVIDIA Corporation.
+ *
+ * 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 __MACH_TEGRA_CSI_H
+#define __MACH_TEGRA_CSI_H
+
+#define CSI_CILA_MIPI_CAL_CONFIG_0 0x22a
+#define MIPI_CAL_TERMOSA(x) (((x) & 0x1f) << 0)
+
+#define CSI_CILB_MIPI_CAL_CONFIG_0 0x22b
+#define MIPI_CAL_TERMOSB(x) (((x) & 0x1f) << 0)
+
+
+#define CSI_DSI_MIPI_CAL_CONFIG 0x234
+#define MIPI_CAL_HSPDOSD(x) (((x) & 0x1f) << 16)
+#define MIPI_CAL_HSPUOSD(x) (((x) & 0x1f) << 8)
+
+#define CSI_MIPIBIAS_PAD_CONFIG 0x235
+#define PAD_DRIV_DN_REF(x) (((x) & 0x7) << 16)
+#define PAD_DRIV_UP_REF(x) (((x) & 0x7) << 8)
+
+int tegra_vi_csi_readl(u32 offset, u32 *val);
+int tegra_vi_csi_writel(u32 value, u32 offset);
+
+#endif
diff --git a/arch/arm/mach-tegra/include/mach/dc.h b/arch/arm/mach-tegra/include/mach/dc.h
new file mode 100644
index 000000000000..caca9d84c1be
--- /dev/null
+++ b/arch/arm/mach-tegra/include/mach/dc.h
@@ -0,0 +1,558 @@
+/*
+ * arch/arm/mach-tegra/include/mach/dc.h
+ *
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * Author:
+ * Erik Gilling <konkers@google.com>
+ *
+ * Copyright (C) 2010-2011 NVIDIA Corporation
+ *
+ * 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 __MACH_TEGRA_DC_H
+#define __MACH_TEGRA_DC_H
+
+#include <linux/pm.h>
+#include <linux/types.h>
+#include <drm/drm_fixed.h>
+
+#define TEGRA_MAX_DC 2
+#define DC_N_WINDOWS 3
+
+
+/* DSI pixel data format */
+enum {
+ TEGRA_DSI_PIXEL_FORMAT_16BIT_P,
+ TEGRA_DSI_PIXEL_FORMAT_18BIT_P,
+ TEGRA_DSI_PIXEL_FORMAT_18BIT_NP,
+ TEGRA_DSI_PIXEL_FORMAT_24BIT_P,
+};
+
+/* DSI virtual channel number */
+enum {
+ TEGRA_DSI_VIRTUAL_CHANNEL_0,
+ TEGRA_DSI_VIRTUAL_CHANNEL_1,
+ TEGRA_DSI_VIRTUAL_CHANNEL_2,
+ TEGRA_DSI_VIRTUAL_CHANNEL_3,
+};
+
+/* DSI transmit method for video data */
+enum {
+ TEGRA_DSI_VIDEO_TYPE_VIDEO_MODE,
+ TEGRA_DSI_VIDEO_TYPE_COMMAND_MODE,
+};
+
+/* DSI HS clock mode */
+enum {
+ TEGRA_DSI_VIDEO_CLOCK_CONTINUOUS,
+ TEGRA_DSI_VIDEO_CLOCK_TX_ONLY,
+};
+
+/* DSI burst mode setting in video mode. Each mode is assigned with a
+ * fixed value. The rationale behind this is to avoid change of these
+ * values, since the calculation of dsi clock depends on them. */
+enum {
+ TEGRA_DSI_VIDEO_NONE_BURST_MODE = 0,
+ TEGRA_DSI_VIDEO_NONE_BURST_MODE_WITH_SYNC_END = 1,
+ TEGRA_DSI_VIDEO_BURST_MODE_LOWEST_SPEED = 2,
+ TEGRA_DSI_VIDEO_BURST_MODE_LOW_SPEED = 3,
+ TEGRA_DSI_VIDEO_BURST_MODE_MEDIUM_SPEED = 4,
+ TEGRA_DSI_VIDEO_BURST_MODE_FAST_SPEED = 5,
+ TEGRA_DSI_VIDEO_BURST_MODE_FASTEST_SPEED = 6,
+};
+
+enum {
+ TEGRA_DSI_PACKET_CMD,
+ TEGRA_DSI_DELAY_MS,
+};
+
+struct tegra_dsi_cmd {
+ u8 cmd_type;
+ u8 data_id;
+ union {
+ u16 data_len;
+ u16 delay_ms;
+ struct{
+ u8 data0;
+ u8 data1;
+ }sp;
+ }sp_len_dly;
+ u8 *pdata;
+};
+
+#define DSI_CMD_SHORT(di, p0, p1) { \
+ .cmd_type = TEGRA_DSI_PACKET_CMD, \
+ .data_id = di, \
+ .sp_len_dly.sp.data0 = p0, \
+ .sp_len_dly.sp.data1 = p1, \
+ }
+#define DSI_DLY_MS(ms) { \
+ .cmd_type = TEGRA_DSI_DELAY_MS, \
+ .sp_len_dly.delay_ms = ms, \
+ }
+
+#define DSI_CMD_LONG(di, ptr) { \
+ .cmd_type = TEGRA_DSI_PACKET_CMD, \
+ .data_id = di, \
+ .sp_len_dly.data_len = ARRAY_SIZE(ptr), \
+ .pdata = ptr, \
+ }
+
+struct dsi_phy_timing_ns {
+ u16 t_hsdexit_ns;
+ u16 t_hstrail_ns;
+ u16 t_hsprepr_ns;
+ u16 t_datzero_ns;
+
+ u16 t_clktrail_ns;
+ u16 t_clkpost_ns;
+ u16 t_clkzero_ns;
+ u16 t_tlpx_ns;
+};
+
+struct tegra_dsi_out {
+ u8 n_data_lanes; /* required */
+ u8 pixel_format; /* required */
+ u8 refresh_rate; /* required */
+ u8 panel_reset; /* required */
+ u8 virtual_channel; /* required */
+ u8 dsi_instance;
+ u8 chip_id;
+ u8 chip_rev;
+
+ bool panel_has_frame_buffer; /* required*/
+
+ struct tegra_dsi_cmd* dsi_init_cmd; /* required */
+ u16 n_init_cmd; /* required */
+
+ struct tegra_dsi_cmd* dsi_early_suspend_cmd;
+ u16 n_early_suspend_cmd;
+
+ struct tegra_dsi_cmd* dsi_late_resume_cmd;
+ u16 n_late_resume_cmd;
+
+ struct tegra_dsi_cmd* dsi_suspend_cmd; /* required */
+ u16 n_suspend_cmd; /* required */
+
+ u8 video_data_type; /* required */
+ u8 video_clock_mode;
+ u8 video_burst_mode;
+
+ u16 panel_buffer_size_byte;
+ u16 panel_reset_timeout_msec;
+
+ bool hs_cmd_mode_supported;
+ bool hs_cmd_mode_on_blank_supported;
+ bool enable_hs_clock_on_lp_cmd_mode;
+ bool no_pkt_seq_eot; /* 1st generation panel may not
+ * support eot. Don't set it for
+ * most panels. */
+ bool te_polarity_low;
+ bool power_saving_suspend;
+
+ u32 max_panel_freq_khz;
+ u32 lp_cmd_mode_freq_khz;
+ u32 lp_read_cmd_mode_freq_khz;
+ u32 hs_clk_in_lp_cmd_mode_freq_khz;
+ u32 burst_mode_freq_khz;
+
+ struct dsi_phy_timing_ns phy_timing;
+};
+
+enum {
+ TEGRA_DC_STEREO_MODE_2D,
+ TEGRA_DC_STEREO_MODE_3D
+};
+
+enum {
+ TEGRA_DC_STEREO_LANDSCAPE,
+ TEGRA_DC_STEREO_PORTRAIT
+};
+
+struct tegra_stereo_out {
+ int mode_2d_3d;
+ int orientation;
+
+ void (*set_mode)(int mode);
+ void (*set_orientation)(int orientation);
+};
+
+struct tegra_dc_mode {
+ int pclk;
+ int h_ref_to_sync;
+ int v_ref_to_sync;
+ int h_sync_width;
+ int v_sync_width;
+ int h_back_porch;
+ int v_back_porch;
+ int h_active;
+ int v_active;
+ int h_front_porch;
+ int v_front_porch;
+ int stereo_mode;
+ u32 flags;
+};
+
+#define TEGRA_DC_MODE_FLAG_NEG_V_SYNC (1 << 0)
+#define TEGRA_DC_MODE_FLAG_NEG_H_SYNC (1 << 1)
+
+enum {
+ TEGRA_DC_OUT_RGB,
+ TEGRA_DC_OUT_HDMI,
+ TEGRA_DC_OUT_DSI,
+};
+
+struct tegra_dc_out_pin {
+ int name;
+ int pol;
+};
+
+enum {
+ TEGRA_DC_OUT_PIN_DATA_ENABLE,
+ TEGRA_DC_OUT_PIN_H_SYNC,
+ TEGRA_DC_OUT_PIN_V_SYNC,
+ TEGRA_DC_OUT_PIN_PIXEL_CLOCK,
+};
+
+enum {
+ TEGRA_DC_OUT_PIN_POL_LOW,
+ TEGRA_DC_OUT_PIN_POL_HIGH,
+};
+
+enum {
+ TEGRA_DC_DISABLE_DITHER = 1,
+ TEGRA_DC_ORDERED_DITHER,
+ TEGRA_DC_ERRDIFF_DITHER,
+};
+
+typedef u8 tegra_dc_bl_output[256];
+typedef u8 *p_tegra_dc_bl_output;
+
+struct tegra_dc_sd_blp {
+ u16 time_constant;
+ u8 step;
+};
+
+struct tegra_dc_sd_fc {
+ u8 time_limit;
+ u8 threshold;
+};
+
+struct tegra_dc_sd_rgb {
+ u8 r;
+ u8 g;
+ u8 b;
+};
+
+struct tegra_dc_sd_agg_priorities {
+ u8 pri_lvl;
+ u8 agg[4];
+};
+
+struct tegra_dc_sd_settings {
+ unsigned enable;
+ bool use_auto_pwm;
+ u8 hw_update_delay;
+ u8 aggressiveness;
+ short bin_width;
+ u8 phase_in_settings;
+ u8 phase_in_adjustments;
+ u8 cmd;
+ u8 final_agg;
+ u16 cur_agg_step;
+ u16 phase_settings_step;
+ u16 phase_adj_step;
+ u16 num_phase_in_steps;
+
+ struct tegra_dc_sd_agg_priorities agg_priorities;
+
+ bool use_vid_luma;
+ struct tegra_dc_sd_rgb coeff;
+
+ struct tegra_dc_sd_fc fc;
+ struct tegra_dc_sd_blp blp;
+ u8 bltf[4][4][4];
+ struct tegra_dc_sd_rgb lut[4][9];
+
+ atomic_t *sd_brightness;
+ struct platform_device *bl_device;
+};
+
+enum {
+ NO_CMD = 0x0,
+ ENABLE = 0x1,
+ DISABLE = 0x2,
+ PHASE_IN = 0x4,
+ AGG_CHG = 0x8,
+};
+
+enum {
+ TEGRA_PIN_OUT_CONFIG_SEL_LHP0_LD21,
+ TEGRA_PIN_OUT_CONFIG_SEL_LHP1_LD18,
+ TEGRA_PIN_OUT_CONFIG_SEL_LHP2_LD19,
+ TEGRA_PIN_OUT_CONFIG_SEL_LVP0_LVP0_Out,
+ TEGRA_PIN_OUT_CONFIG_SEL_LVP1_LD20,
+
+ TEGRA_PIN_OUT_CONFIG_SEL_LM1_M1,
+ TEGRA_PIN_OUT_CONFIG_SEL_LM1_LD21,
+ TEGRA_PIN_OUT_CONFIG_SEL_LM1_PM1,
+
+ TEGRA_PIN_OUT_CONFIG_SEL_LDI_LD22,
+ TEGRA_PIN_OUT_CONFIG_SEL_LPP_LD23,
+ TEGRA_PIN_OUT_CONFIG_SEL_LDC_SDC,
+ TEGRA_PIN_OUT_CONFIG_SEL_LSPI_DE,
+};
+
+struct tegra_dc_out {
+ int type;
+ unsigned flags;
+
+ /* size in mm */
+ unsigned h_size;
+ unsigned v_size;
+
+ int dcc_bus;
+ int hotplug_gpio;
+ const char *parent_clk;
+
+ unsigned max_pixclock;
+ unsigned order;
+ unsigned align;
+ unsigned depth;
+ unsigned dither;
+
+ struct tegra_dc_mode *modes;
+ int n_modes;
+
+ struct tegra_dsi_out *dsi;
+ struct tegra_stereo_out *stereo;
+
+ unsigned height; /* mm */
+ unsigned width; /* mm */
+
+ struct tegra_dc_out_pin *out_pins;
+ unsigned n_out_pins;
+
+ struct tegra_dc_sd_settings *sd_settings;
+
+ u8 *out_sel_configs;
+ unsigned n_out_sel_configs;
+
+ int (*enable)(void);
+ int (*postpoweron)(void);
+ int (*disable)(void);
+
+ int (*hotplug_init)(void);
+ int (*postsuspend)(void);
+};
+
+/* bits for tegra_dc_out.flags */
+#define TEGRA_DC_OUT_HOTPLUG_HIGH (0 << 1)
+#define TEGRA_DC_OUT_HOTPLUG_LOW (1 << 1)
+#define TEGRA_DC_OUT_HOTPLUG_MASK (1 << 1)
+#define TEGRA_DC_OUT_NVHDCP_POLICY_ALWAYS_ON (0 << 2)
+#define TEGRA_DC_OUT_NVHDCP_POLICY_ON_DEMAND (1 << 2)
+#define TEGRA_DC_OUT_NVHDCP_POLICY_MASK (1 << 2)
+#define TEGRA_DC_OUT_CONTINUOUS_MODE (0 << 3)
+#define TEGRA_DC_OUT_ONE_SHOT_MODE (1 << 3)
+#define TEGRA_DC_OUT_N_SHOT_MODE (1 << 4)
+
+#define TEGRA_DC_ALIGN_MSB 0
+#define TEGRA_DC_ALIGN_LSB 1
+
+#define TEGRA_DC_ORDER_RED_BLUE 0
+#define TEGRA_DC_ORDER_BLUE_RED 1
+
+struct tegra_dc;
+struct nvmap_handle_ref;
+
+struct tegra_dc_csc {
+ unsigned short yof;
+ unsigned short kyrgb;
+ unsigned short kur;
+ unsigned short kvr;
+ unsigned short kug;
+ unsigned short kvg;
+ unsigned short kub;
+ unsigned short kvb;
+};
+
+/* palette lookup table */
+struct tegra_dc_lut {
+ u8 r[256];
+ u8 g[256];
+ u8 b[256];
+};
+
+struct tegra_dc_win {
+ u8 idx;
+ u8 fmt;
+ u8 ppflags; /* see TEGRA_WIN_PPFLAG* */
+ u32 flags;
+
+ void *virt_addr;
+ dma_addr_t phys_addr;
+ dma_addr_t phys_addr_u;
+ dma_addr_t phys_addr_v;
+ unsigned stride;
+ unsigned stride_uv;
+ fixed20_12 x;
+ fixed20_12 y;
+ fixed20_12 w;
+ fixed20_12 h;
+ unsigned out_x;
+ unsigned out_y;
+ unsigned out_w;
+ unsigned out_h;
+ unsigned z;
+
+ struct tegra_dc_csc csc;
+
+ int dirty;
+ int underflows;
+ struct tegra_dc *dc;
+
+ struct nvmap_handle_ref *cur_handle;
+ unsigned bandwidth;
+ unsigned new_bandwidth;
+ struct tegra_dc_lut lut;
+};
+
+#define TEGRA_WIN_PPFLAG_CP_ENABLE (1 << 0) /* enable RGB color lut */
+#define TEGRA_WIN_PPFLAG_CP_FBOVERRIDE (1 << 1) /* override fbdev color lut */
+
+#define TEGRA_WIN_FLAG_ENABLED (1 << 0)
+#define TEGRA_WIN_FLAG_BLEND_PREMULT (1 << 1)
+#define TEGRA_WIN_FLAG_BLEND_COVERAGE (1 << 2)
+#define TEGRA_WIN_FLAG_INVERT_H (1 << 3)
+#define TEGRA_WIN_FLAG_INVERT_V (1 << 4)
+#define TEGRA_WIN_FLAG_TILED (1 << 5)
+#define TEGRA_WIN_FLAG_H_FILTER (1 << 6)
+#define TEGRA_WIN_FLAG_V_FILTER (1 << 7)
+
+
+#define TEGRA_WIN_BLEND_FLAGS_MASK \
+ (TEGRA_WIN_FLAG_BLEND_PREMULT | TEGRA_WIN_FLAG_BLEND_COVERAGE)
+
+/* Note: These are the actual values written to the DC_WIN_COLOR_DEPTH register
+ * and may change in new tegra architectures.
+ */
+#define TEGRA_WIN_FMT_P1 0
+#define TEGRA_WIN_FMT_P2 1
+#define TEGRA_WIN_FMT_P4 2
+#define TEGRA_WIN_FMT_P8 3
+#define TEGRA_WIN_FMT_B4G4R4A4 4
+#define TEGRA_WIN_FMT_B5G5R5A 5
+#define TEGRA_WIN_FMT_B5G6R5 6
+#define TEGRA_WIN_FMT_AB5G5R5 7
+#define TEGRA_WIN_FMT_B8G8R8A8 12
+#define TEGRA_WIN_FMT_R8G8B8A8 13
+#define TEGRA_WIN_FMT_B6x2G6x2R6x2A8 14
+#define TEGRA_WIN_FMT_R6x2G6x2B6x2A8 15
+#define TEGRA_WIN_FMT_YCbCr422 16
+#define TEGRA_WIN_FMT_YUV422 17
+#define TEGRA_WIN_FMT_YCbCr420P 18
+#define TEGRA_WIN_FMT_YUV420P 19
+#define TEGRA_WIN_FMT_YCbCr422P 20
+#define TEGRA_WIN_FMT_YUV422P 21
+#define TEGRA_WIN_FMT_YCbCr422R 22
+#define TEGRA_WIN_FMT_YUV422R 23
+#define TEGRA_WIN_FMT_YCbCr422RA 24
+#define TEGRA_WIN_FMT_YUV422RA 25
+
+struct tegra_fb_data {
+ int win;
+
+ int xres;
+ int yres;
+ int bits_per_pixel; /* -1 means autodetect */
+
+ unsigned long flags;
+};
+
+#define TEGRA_FB_FLIP_ON_PROBE (1 << 0)
+
+struct tegra_dc_platform_data {
+ unsigned long flags;
+ unsigned long emc_clk_rate;
+ struct tegra_dc_out *default_out;
+ struct tegra_fb_data *fb;
+};
+
+#define TEGRA_DC_FLAG_ENABLED (1 << 0)
+
+struct tegra_dc *tegra_dc_get_dc(unsigned idx);
+struct tegra_dc_win *tegra_dc_get_window(struct tegra_dc *dc, unsigned win);
+bool tegra_dc_get_connected(struct tegra_dc *);
+
+void tegra_dc_blank(struct tegra_dc *dc);
+
+void tegra_dc_enable(struct tegra_dc *dc);
+void tegra_dc_disable(struct tegra_dc *dc);
+
+u32 tegra_dc_get_syncpt_id(const struct tegra_dc *dc, int i);
+u32 tegra_dc_incr_syncpt_max(struct tegra_dc *dc, int i);
+void tegra_dc_incr_syncpt_min(struct tegra_dc *dc, int i, u32 val);
+
+/* tegra_dc_update_windows and tegra_dc_sync_windows do not support windows
+ * with differenct dcs in one call
+ */
+int tegra_dc_update_windows(struct tegra_dc_win *windows[], int n);
+int tegra_dc_sync_windows(struct tegra_dc_win *windows[], int n);
+
+int tegra_dc_set_mode(struct tegra_dc *dc, const struct tegra_dc_mode *mode);
+struct fb_videomode;
+int tegra_dc_set_fb_mode(struct tegra_dc *dc, const struct fb_videomode *fbmode,
+ bool stereo_mode);
+
+unsigned tegra_dc_get_out_height(const struct tegra_dc *dc);
+unsigned tegra_dc_get_out_width(const struct tegra_dc *dc);
+unsigned tegra_dc_get_out_max_pixclock(const struct tegra_dc *dc);
+
+/* PM0 and PM1 signal control */
+#define TEGRA_PWM_PM0 0
+#define TEGRA_PWM_PM1 1
+
+struct tegra_dc_pwm_params {
+ int which_pwm;
+ void (*switch_to_sfio)(int);
+ int gpio_conf_to_sfio;
+ unsigned int period;
+ unsigned int clk_div;
+ unsigned int clk_select;
+ unsigned int duty_cycle;
+};
+
+void tegra_dc_config_pwm(struct tegra_dc *dc, struct tegra_dc_pwm_params *cfg);
+
+int tegra_dsi_send_panel_short_cmd(struct tegra_dc *dc, u8 *pdata, u8 data_len);
+
+int tegra_dc_update_csc(struct tegra_dc *dc, int win_index);
+
+int tegra_dc_update_lut(struct tegra_dc *dc, int win_index, int fboveride);
+
+/*
+ * In order to get a dc's current EDID, first call tegra_dc_get_edid() from an
+ * interruptible context. The returned value (if non-NULL) points to a
+ * snapshot of the current state; after copying data from it, call
+ * tegra_dc_put_edid() on that pointer. Do not dereference anything through
+ * that pointer after calling tegra_dc_put_edid().
+ */
+struct tegra_dc_edid {
+ size_t len;
+ u8 buf[0];
+};
+struct tegra_dc_edid *tegra_dc_get_edid(struct tegra_dc *dc);
+void tegra_dc_put_edid(struct tegra_dc_edid *edid);
+
+#endif
diff --git a/arch/arm/mach-tegra/include/mach/delay.h b/arch/arm/mach-tegra/include/mach/delay.h
new file mode 100644
index 000000000000..2defb7b9b658
--- /dev/null
+++ b/arch/arm/mach-tegra/include/mach/delay.h
@@ -0,0 +1,41 @@
+/*
+ * arch/arm/mach-tegra/include/mach/delay.h
+ *
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * Author:
+ * Colin Cross <ccross@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.
+ *
+ */
+#ifndef __MACH_TEGRA_DELAY_H
+#define __MACH_TEGRA_DELAY_H
+
+/* needed by loops_per_jiffy calculations */
+extern void __delay(int loops);
+
+extern void __udelay(unsigned long usecs);
+extern void __const_udelay(unsigned long usecs);
+
+/* we don't have any restrictions on maximum udelay length, but we'll enforce
+ * the same restriction as the ARM default so we don't introduce any
+ * incompatibilties in drivers.
+ */
+extern void __bad_udelay(void);
+
+#define MAX_UDELAY_MS 2
+
+#define udelay(n) \
+ ((__builtin_constant_p(n) && (n) > (MAX_UDELAY_MS * 1000)) ? \
+ __bad_udelay() : \
+ __udelay(n))
+
+#endif /* defined(__MACH_TEGRA_DELAY_H) */
diff --git a/arch/arm/mach-tegra/include/mach/dma.h b/arch/arm/mach-tegra/include/mach/dma.h
index d0132e8031a1..42084b697b7f 100644
--- a/arch/arm/mach-tegra/include/mach/dma.h
+++ b/arch/arm/mach-tegra/include/mach/dma.h
@@ -1,7 +1,7 @@
/*
* arch/arm/mach-tegra/include/mach/dma.h
*
- * Copyright (c) 2008-2009, NVIDIA Corporation.
+ * Copyright (c) 2008-2010, NVIDIA Corporation.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -30,9 +30,13 @@ struct tegra_dma_channel;
#define TEGRA_DMA_REQ_SEL_CNTR 0
#define TEGRA_DMA_REQ_SEL_I2S_2 1
+#define TEGRA_DMA_REQ_SEL_APBIF_CH0 TEGRA_DMA_REQ_SEL_I2S_2
#define TEGRA_DMA_REQ_SEL_I2S_1 2
+#define TEGRA_DMA_REQ_SEL_APBIF_CH1 TEGRA_DMA_REQ_SEL_I2S_1
#define TEGRA_DMA_REQ_SEL_SPD_I 3
+#define TEGRA_DMA_REQ_SEL_APBIF_CH2 TEGRA_DMA_REQ_SEL_SPD_I
#define TEGRA_DMA_REQ_SEL_UI_I 4
+#define TEGRA_DMA_REQ_SEL_APBIF_CH3 TEGRA_DMA_REQ_SEL_UI_I
#define TEGRA_DMA_REQ_SEL_MIPI 5
#define TEGRA_DMA_REQ_SEL_I2S2_2 6
#define TEGRA_DMA_REQ_SEL_I2S2_1 7
@@ -40,6 +44,7 @@ struct tegra_dma_channel;
#define TEGRA_DMA_REQ_SEL_UARTB 9
#define TEGRA_DMA_REQ_SEL_UARTC 10
#define TEGRA_DMA_REQ_SEL_SPI 11
+#define TEGRA_DMA_REQ_SEL_DTV TEGRA_DMA_REQ_SEL_SPI
#define TEGRA_DMA_REQ_SEL_AC97 12
#define TEGRA_DMA_REQ_SEL_ACMODEM 13
#define TEGRA_DMA_REQ_SEL_SL4B 14
@@ -54,12 +59,20 @@ struct tegra_dma_channel;
#define TEGRA_DMA_REQ_SEL_I2C3 23
#define TEGRA_DMA_REQ_SEL_DVC_I2C 24
#define TEGRA_DMA_REQ_SEL_OWR 25
+#define TEGRA_DMA_REQ_SEL_OWR 25
+#define TEGRA_DMA_REQ_SEL_I2C4 26
+#define TEGRA_DMA_REQ_SEL_SL2B5 27
+#define TEGRA_DMA_REQ_SEL_SL2B6 28
#define TEGRA_DMA_REQ_SEL_INVALID 31
+#define TEGRA_DMA_MAX_TRANSFER_SIZE 0x10000
+
enum tegra_dma_mode {
TEGRA_DMA_SHARED = 1,
- TEGRA_DMA_MODE_CONTINOUS = 2,
- TEGRA_DMA_MODE_ONESHOT = 4,
+ TEGRA_DMA_MODE_CONTINUOUS = 2,
+ TEGRA_DMA_MODE_CONTINUOUS_DOUBLE = TEGRA_DMA_MODE_CONTINUOUS,
+ TEGRA_DMA_MODE_CONTINUOUS_SINGLE = 4,
+ TEGRA_DMA_MODE_ONESHOT = 8,
};
enum tegra_dma_req_error {
@@ -143,13 +156,23 @@ void tegra_dma_flush(struct tegra_dma_channel *ch);
bool tegra_dma_is_req_inflight(struct tegra_dma_channel *ch,
struct tegra_dma_req *req);
+int tegra_dma_get_transfer_count(struct tegra_dma_channel *ch,
+ struct tegra_dma_req *req, bool is_stop_dma);
bool tegra_dma_is_empty(struct tegra_dma_channel *ch);
+bool tegra_dma_is_stopped(struct tegra_dma_channel *ch);
-struct tegra_dma_channel *tegra_dma_allocate_channel(int mode);
+struct tegra_dma_channel *tegra_dma_allocate_channel(int mode, const char namefmt [ ],...);
void tegra_dma_free_channel(struct tegra_dma_channel *ch);
+int tegra_dma_cancel(struct tegra_dma_channel *ch);
int __init tegra_dma_init(void);
+#else /* !defined(CONFIG_TEGRA_SYSTEM_DMA) */
+static inline int tegra_dma_init(void)
+{
+ return 0;
+}
+
#endif
#endif
diff --git a/arch/arm/mach-tegra/include/mach/edp.h b/arch/arm/mach-tegra/include/mach/edp.h
new file mode 100644
index 000000000000..48321cae4959
--- /dev/null
+++ b/arch/arm/mach-tegra/include/mach/edp.h
@@ -0,0 +1,80 @@
+/*
+ * arch/arm/mach-tegra/include/mach/edp.h
+ *
+ * Copyright (c) 2011, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __MACH_EDP_H
+#define __MACH_EDP_H
+
+#include <linux/debugfs.h>
+
+struct tegra_edp_entry {
+ char speedo_id;
+ char regulator_100mA;
+ char temperature;
+ char freq_limits[4];
+};
+
+struct tegra_edp_limits {
+ int temperature;
+ unsigned int freq_limits[4];
+};
+
+struct system_edp_entry {
+ char speedo_id;
+ char power_limit_100mW;
+ char freq_limits[4];
+};
+
+#ifdef CONFIG_TEGRA_EDP_LIMITS
+
+
+int tegra_edp_update_thermal_zone(int temperature);
+void tegra_init_cpu_edp_limits(unsigned int regulator_mA);
+void tegra_init_system_edp_limits(unsigned int power_limit_mW);
+void tegra_get_cpu_edp_limits(const struct tegra_edp_limits **limits, int *size);
+unsigned int tegra_get_edp_limit(void);
+void tegra_get_system_edp_limits(const unsigned int **limits);
+int tegra_system_edp_alarm(bool alarm);
+
+#else
+static inline void tegra_init_cpu_edp_limits(int regulator_mA)
+{}
+static inline void tegra_init_system_edp_limits(int power_limit_mW)
+{}
+static inline int tegra_edp_update_thermal_zone(int temperature)
+{ return -1; }
+static inline void tegra_get_cpu_edp_limits(struct tegra_edp_limits **limits,
+ int *size)
+{}
+static inline unsigned int tegra_get_edp_limit(void)
+{ return -1; }
+static inline void tegra_get_system_edp_limits(unsigned int **limits)
+{}
+static inline int tegra_system_edp_alarm(bool alarm)
+{ return -1; }
+#endif
+
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+static inline void tegra_edp_throttle_cpu_now(u8 factor)
+{}
+#else
+void tegra_edp_throttle_cpu_now(u8 factor);
+#endif
+
+#endif /* __MACH_EDP_H */
diff --git a/arch/arm/mach-tegra/include/mach/entry-macro.S b/arch/arm/mach-tegra/include/mach/entry-macro.S
index dd165c53889d..50d1b212da2b 100644
--- a/arch/arm/mach-tegra/include/mach/entry-macro.S
+++ b/arch/arm/mach-tegra/include/mach/entry-macro.S
@@ -20,7 +20,7 @@
#include <asm/hardware/entry-macro-gic.S>
/* Uses the GIC interrupt controller built into the cpu */
-#define ICTRL_BASE (IO_CPU_VIRT + 0x100)
+#define ICTRL_BASE (IO_CPU_VIRT + 0x40100)
.macro disable_fiq
.endm
@@ -33,24 +33,5 @@
.macro arch_ret_to_user, tmp1, tmp2
.endm
#else
- /* legacy interrupt controller for AP16 */
- .macro disable_fiq
- .endm
-
- .macro get_irqnr_preamble, base, tmp
- @ enable imprecise aborts
- cpsie a
- @ EVP base at 0xf010f000
- mov \base, #0xf0000000
- orr \base, #0x00100000
- orr \base, #0x0000f000
- .endm
-
- .macro arch_ret_to_user, tmp1, tmp2
- .endm
-
- .macro get_irqnr_and_base, irqnr, irqstat, base, tmp
- ldr \irqnr, [\base, #0x20] @ EVT_IRQ_STS
- cmp \irqnr, #0x80
- .endm
+#error "Unsupported configuration"
#endif
diff --git a/arch/arm/mach-tegra/include/mach/fb.h b/arch/arm/mach-tegra/include/mach/fb.h
new file mode 100644
index 000000000000..ced6f9c2cb44
--- /dev/null
+++ b/arch/arm/mach-tegra/include/mach/fb.h
@@ -0,0 +1,61 @@
+/*
+ * arch/arm/mach-tegra/include/mach/fb.h
+ *
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * Author:
+ * Erik Gilling <konkers@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.
+ *
+ */
+
+#ifndef __MACH_TEGRA_FB_H
+#define __MACH_TEGRA_FB_H
+
+#include <linux/fb.h>
+
+struct nvhost_device;
+struct tegra_dc;
+struct tegra_fb_data;
+struct tegra_fb_info;
+struct resource;
+
+#ifdef CONFIG_FB_TEGRA
+struct tegra_fb_info *tegra_fb_register(struct nvhost_device *ndev,
+ struct tegra_dc *dc,
+ struct tegra_fb_data *fb_data,
+ struct resource *fb_mem);
+void tegra_fb_unregister(struct tegra_fb_info *fb_info);
+void tegra_fb_update_monspecs(struct tegra_fb_info *fb_info,
+ struct fb_monspecs *specs,
+ bool (*mode_filter)(const struct tegra_dc *dc,
+ struct fb_videomode *mode));
+#else
+static inline struct tegra_fb_info *tegra_fb_register(struct nvhost_device *ndev,
+ struct tegra_dc *dc,
+ struct tegra_fb_data *fb_data,
+ struct resource *fb_mem)
+{
+ return NULL;
+}
+
+static inline void tegra_fb_unregister(struct tegra_fb_info *fb_info)
+{
+}
+
+static inline void tegra_fb_update_monspecs(struct tegra_fb_info *fb_info,
+ struct fb_monspecs *specs,
+ bool (*mode_filter)(struct fb_videomode *mode))
+{
+}
+#endif
+
+#endif
diff --git a/arch/arm/mach-tegra/include/mach/fiq.h b/arch/arm/mach-tegra/include/mach/fiq.h
new file mode 100644
index 000000000000..17625facf627
--- /dev/null
+++ b/arch/arm/mach-tegra/include/mach/fiq.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * Author:
+ * Iliyan Malchev <malchev@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.
+ *
+ */
+
+#ifndef __ASM_ARCH_TEGRA_FIQ_H
+#define __ASM_ARCH_TEGRA_FIQ_H
+
+/* enable/disable an interrupt that is an FIQ (safe from FIQ context?) */
+void tegra_fiq_enable(int n);
+void tegra_fiq_disable(int n);
+
+#endif
diff --git a/arch/arm/mach-tegra/include/mach/gpio.h b/arch/arm/mach-tegra/include/mach/gpio.h
index 196f114dc241..b7357ab0c4dd 100644
--- a/arch/arm/mach-tegra/include/mach/gpio.h
+++ b/arch/arm/mach-tegra/include/mach/gpio.h
@@ -24,8 +24,18 @@
#include <mach/irqs.h>
#define TEGRA_NR_GPIOS INT_GPIO_NR
+#define ARCH_NR_GPIOS (TEGRA_NR_GPIOS + 128)
#include <asm-generic/gpio.h>
+#include "pinmux.h"
+
+struct gpio_init_pin_info {
+ char name[16];
+ int gpio_nr;
+ bool is_gpio;
+ bool is_input;
+ int value; /* Value if it is output*/
+};
#define gpio_get_value __gpio_get_value
#define gpio_set_value __gpio_set_value
@@ -36,16 +46,23 @@
static inline int gpio_to_irq(unsigned int gpio)
{
+ /* SOC gpio */
if (gpio < TEGRA_NR_GPIOS)
return INT_GPIO_BASE + gpio;
- return -EINVAL;
+
+ /* For non soc gpio, the external peripheral driver need to
+ * provide the implementation */
+ return __gpio_to_irq(gpio);
}
static inline int irq_to_gpio(unsigned int irq)
{
+ /* SOC gpio */
if ((irq >= INT_GPIO_BASE) && (irq < INT_GPIO_BASE + INT_GPIO_NR))
return irq - INT_GPIO_BASE;
- return -EINVAL;
+
+ /* we don't supply reverse mappings for non-SOC gpios */
+ return -EIO;
}
struct tegra_gpio_table {
@@ -56,5 +73,8 @@ struct tegra_gpio_table {
void tegra_gpio_config(struct tegra_gpio_table *table, int num);
void tegra_gpio_enable(int gpio);
void tegra_gpio_disable(int gpio);
-
+int tegra_gpio_resume_init(void);
+void tegra_gpio_init_configure(unsigned gpio, bool is_input, int value);
+void tegra_gpio_set_tristate(int gpio, enum tegra_tristate ts);
+int tegra_gpio_get_bank_int_nr(int gpio);
#endif
diff --git a/arch/arm/mach-tegra/include/mach/gpufuse.h b/arch/arm/mach-tegra/include/mach/gpufuse.h
new file mode 100644
index 000000000000..4aa6cb66d5d9
--- /dev/null
+++ b/arch/arm/mach-tegra/include/mach/gpufuse.h
@@ -0,0 +1,19 @@
+/*
+ * arch/arm/mach-tegra/include/mach/gpufuse.h
+ *
+ * Copyright (C) 2010-2011 NVIDIA Corporation.
+ *
+ * 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.
+ *
+ */
+
+/* Number of register sets to handle in host context switching */
+int tegra_gpu_register_sets(void);
+
diff --git a/arch/arm/mach-tegra/include/mach/suspend.h b/arch/arm/mach-tegra/include/mach/hardware.h
index 5af8715d2e1e..c36dc02da0b2 100644
--- a/arch/arm/mach-tegra/include/mach/suspend.h
+++ b/arch/arm/mach-tegra/include/mach/hardware.h
@@ -1,10 +1,10 @@
/*
- * arch/arm/mach-tegra/include/mach/suspend.h
- *
* Copyright (C) 2010 Google, Inc.
+ * Copyright (C) 2011 NVIDIA Corp.
*
* Author:
* Colin Cross <ccross@google.com>
+ * Erik Gilling <konkers@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
@@ -14,25 +14,27 @@
* 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 MACH_TEGRA_HARDWARE_H
+#define MACH_TEGRA_HARDWARE_H
-#ifndef _MACH_TEGRA_SUSPEND_H_
-#define _MACH_TEGRA_SUSPEND_H_
+enum tegra_chipid {
+ TEGRA_CHIPID_UNKNOWN = 0,
+ TEGRA_CHIPID_TEGRA2 = 0x20,
+ TEGRA_CHIPID_TEGRA3 = 0x30,
+};
-void tegra_pinmux_suspend(void);
-void tegra_irq_suspend(void);
-void tegra_gpio_suspend(void);
-void tegra_clk_suspend(void);
-void tegra_dma_suspend(void);
-void tegra_timer_suspend(void);
+enum tegra_revision {
+ TEGRA_REVISION_UNKNOWN = 0,
+ TEGRA_REVISION_A01,
+ TEGRA_REVISION_A02,
+ TEGRA_REVISION_A03,
+ TEGRA_REVISION_A03p,
+ TEGRA_REVISION_MAX,
+};
-void tegra_pinmux_resume(void);
-void tegra_irq_resume(void);
-void tegra_gpio_resume(void);
-void tegra_clk_resume(void);
-void tegra_dma_resume(void);
-void tegra_timer_resume(void);
+enum tegra_chipid tegra_get_chipid(void);
+enum tegra_revision tegra_get_revision(void);
-#endif /* _MACH_TEGRA_SUSPEND_H_ */
+#endif
diff --git a/arch/arm/mach-tegra/include/mach/hdmi-audio.h b/arch/arm/mach-tegra/include/mach/hdmi-audio.h
new file mode 100644
index 000000000000..7d760690081a
--- /dev/null
+++ b/arch/arm/mach-tegra/include/mach/hdmi-audio.h
@@ -0,0 +1,46 @@
+/*
+ * arch/arm/mach-tegra/include/mach/hdmi-audio.h
+ *
+ * Copyright (c) 2008-2011, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __MACH_TEGRA_HDMI_AUDIO_H
+#define __MACH_TEGRA_HDMI_AUDIO_H
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+
+enum {
+ AUDIO_FREQ_32K = 32000,
+ AUDIO_FREQ_44_1K = 44100,
+ AUDIO_FREQ_48K = 48000,
+ AUDIO_FREQ_88_2K = 88200,
+ AUDIO_FREQ_96K = 96000,
+ AUDIO_FREQ_176_4K = 176400,
+ AUDIO_FREQ_192K = 192000,
+};
+
+enum {
+ AUTO = 0,
+ SPDIF,
+ HDA,
+};
+
+int tegra_hdmi_setup_audio_freq_source(unsigned audio_freq, unsigned audio_source);
+int tegra_hdmi_setup_hda_presence(void);
+
+#endif /* __MACH_TEGRA_HDMI_AUDIO_H */
diff --git a/arch/arm/mach-tegra/include/mach/i2s.h b/arch/arm/mach-tegra/include/mach/i2s.h
new file mode 100644
index 000000000000..42cce885cdac
--- /dev/null
+++ b/arch/arm/mach-tegra/include/mach/i2s.h
@@ -0,0 +1,316 @@
+/*
+ * arch/arm/mach-tegra/include/mach/i2s.h
+ *
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * Author:
+ * Iliyan Malchev <malchev@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.
+ *
+ */
+
+#ifndef __ARCH_ARM_MACH_TEGRA_I2S_H
+#define __ARCH_ARM_MACH_TEGRA_I2S_H
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/platform_device.h>
+
+/* Offsets from TEGRA_I2S1_BASE and TEGRA_I2S2_BASE */
+
+#define I2S_I2S_CTRL_0 0
+#define I2S_I2S_STATUS_0 4
+#define I2S_I2S_TIMING_0 8
+#define I2S_I2S_FIFO_SCR_0 0x0c
+#define I2S_I2S_PCM_CTRL_0 0x10
+#define I2S_I2S_NW_CTRL_0 0x14
+#define I2S_I2S_TDM_CTRL_0 0x20
+#define I2S_I2S_TDM_TX_RX_CTRL_0 0x24
+#define I2S_I2S_FIFO1_0 0x40
+#define I2S_I2S_FIFO2_0 0x80
+
+/*
+ * I2S_I2S_CTRL_0
+ */
+
+#define I2S_I2S_CTRL_FIFO2_TX_ENABLE (1<<30)
+#define I2S_I2S_CTRL_FIFO1_ENABLE (1<<29)
+#define I2S_I2S_CTRL_FIFO2_ENABLE (1<<28)
+#define I2S_I2S_CTRL_FIFO1_RX_ENABLE (1<<27)
+#define I2S_I2S_CTRL_FIFO_LPBK_ENABLE (1<<26)
+#define I2S_I2S_CTRL_MASTER_ENABLE (1<<25)
+#define I2S_I2S_CTRL_L_R_CTRL (1<<24) /* 0 = L/R: low/high */
+
+#define I2S_BIT_FORMAT_I2S 0
+#define I2S_BIT_FORMAT_RJM 1
+#define I2S_BIT_FORMAT_LJM 2
+#define I2S_BIT_FORMAT_DSP 3
+#define I2S_BIT_FORMAT_SHIFT 10
+
+#define I2S_I2S_CTRL_BIT_FORMAT_MASK (3<<10)
+#define I2S_I2S_CTRL_BIT_FORMAT_I2S (I2S_BIT_FORMAT_I2S<<10)
+#define I2S_I2S_CTRL_BIT_FORMAT_RJM (I2S_BIT_FORMAT_RJM<<10)
+#define I2S_I2S_CTRL_BIT_FORMAT_LJM (I2S_BIT_FORMAT_LJM<<10)
+#define I2S_I2S_CTRL_BIT_FORMAT_DSP (I2S_BIT_FORMAT_DSP<<10)
+
+#define I2S_BIT_SIZE_16 0
+#define I2S_BIT_SIZE_20 1
+#define I2S_BIT_SIZE_24 2
+#define I2S_BIT_SIZE_32 3
+#define I2S_BIT_SIZE_SHIFT 8
+
+#define I2S_I2S_CTRL_BIT_SIZE_MASK (3 << I2S_BIT_SIZE_SHIFT)
+#define I2S_I2S_CTRL_BIT_SIZE_16 (I2S_BIT_SIZE_16 << I2S_BIT_SIZE_SHIFT)
+#define I2S_I2S_CTRL_BIT_SIZE_20 (I2S_BIT_SIZE_20 << I2S_BIT_SIZE_SHIFT)
+#define I2S_I2S_CTRL_BIT_SIZE_24 (I2S_BIT_SIZE_24 << I2S_BIT_SIZE_SHIFT)
+#define I2S_I2S_CTRL_BIT_SIZE_32 (I2S_BIT_SIZE_32 << I2S_BIT_SIZE_SHIFT)
+
+#define I2S_FIFO_16_LSB 0
+#define I2S_FIFO_20_LSB 1
+#define I2S_FIFO_24_LSB 2
+#define I2S_FIFO_32 3
+#define I2S_FIFO_PACKED 7
+#define I2S_FIFO_SHIFT 4
+
+#define I2S_I2S_CTRL_FIFO_FORMAT_MASK (7<<4)
+#define I2S_I2S_CTRL_FIFO_FORMAT_16_LSB \
+ (I2S_FIFO_16_LSB << I2S_FIFO_SHIFT)
+#define I2S_I2S_CTRL_FIFO_FORMAT_20_LSB \
+ (I2S_FIFO_20_LSB << I2S_FIFO_SHIFT)
+#define I2S_I2S_CTRL_FIFO_FORMAT_24_LSB \
+ (I2S_FIFO_24_LSB << I2S_FIFO_SHIFT)
+#define I2S_I2S_CTRL_FIFO_FORMAT_32 \
+ (I2S_FIFO_32 << I2S_FIFO_SHIFT)
+#define I2S_I2S_CTRL_FIFO_FORMAT_PACKED \
+ (I2S_FIFO_PACKED << I2S_FIFO_SHIFT)
+
+#define I2S_I2S_IE_FIFO1_ERR (1<<3)
+#define I2S_I2S_IE_FIFO2_ERR (1<<2)
+#define I2S_I2S_QE_FIFO1 (1<<1)
+#define I2S_I2S_QE_FIFO2 (1<<0)
+
+/*
+ * I2S_I2S_STATUS_0
+ */
+
+#define I2S_I2S_STATUS_FIFO1_RDY (1<<31)
+#define I2S_I2S_STATUS_FIFO2_RDY (1<<30)
+#define I2S_I2S_STATUS_FIFO1_BSY (1<<29)
+#define I2S_I2S_STATUS_FIFO2_BSY (1<<28)
+#define I2S_I2S_STATUS_FIFO1_ERR (1<<3)
+#define I2S_I2S_STATUS_FIFO2_ERR (1<<2)
+#define I2S_I2S_STATUS_QS_FIFO1 (1<<1)
+#define I2S_I2S_STATUS_QS_FIFO2 (1<<0)
+
+/*
+ * I2S_I2S_TIMING_0
+ */
+
+#define I2S_I2S_TIMING_NON_SYM_ENABLE (1<<12)
+#define I2S_I2S_TIMING_CHANNEL_BIT_COUNT_MASK 0x7ff
+#define I2S_I2S_TIMING_CHANNEL_BIT_COUNT (1<<0)
+
+/*
+ * I2S_I2S_FIFO_SCR_0
+ */
+
+#define I2S_I2S_FIFO_SCR_FIFO_FULL_EMPTY_COUNT_MASK 0x3f
+#define I2S_I2S_FIFO_SCR_FIFO2_FULL_EMPTY_COUNT_SHIFT 24
+#define I2S_I2S_FIFO_SCR_FIFO1_FULL_EMPTY_COUNT_SHIFT 16
+
+#define I2S_I2S_FIFO_SCR_FIFO2_FULL_EMPTY_COUNT_MASK (0x3f<<24)
+#define I2S_I2S_FIFO_SCR_FIFO1_FULL_EMPTY_COUNT_MASK (0x3f<<16)
+
+#define I2S_I2S_FIFO_SCR_FIFO2_CLR (1<<12)
+#define I2S_I2S_FIFO_SCR_FIFO1_CLR (1<<8)
+
+#define I2S_FIFO_ATN_LVL_ONE_SLOT 0
+#define I2S_FIFO_ATN_LVL_FOUR_SLOTS 1
+#define I2S_FIFO_ATN_LVL_EIGHT_SLOTS 2
+#define I2S_FIFO_ATN_LVL_TWELVE_SLOTS 3
+#define I2S_FIFO2_ATN_LVL_SHIFT 4
+#define I2S_FIFO1_ATN_LVL_SHIFT 0
+
+#define I2S_I2S_FIFO_SCR_FIFO2_ATN_LVL_MASK \
+ (3 << I2S_FIFO2_ATN_LVL_SHIFT)
+#define I2S_I2S_FIFO_SCR_FIFO2_ATN_LVL_ONE_SLOT \
+ (I2S_FIFO_ATN_LVL_ONE_SLOT << I2S_FIFO2_ATN_LVL_SHIFT)
+#define I2S_I2S_FIFO_SCR_FIFO2_ATN_LVL_FOUR_SLOTS \
+ (I2S_FIFO_ATN_LVL_FOUR_SLOTS << I2S_FIFO2_ATN_LVL_SHIFT)
+#define I2S_I2S_FIFO_SCR_FIFO2_ATN_LVL_EIGHT_SLOTS \
+ (I2S_FIFO_ATN_LVL_EIGHT_SLOTS << I2S_FIFO2_ATN_LVL_SHIFT)
+#define I2S_I2S_FIFO_SCR_FIFO2_ATN_LVL_TWELVE_SLOTS \
+ (I2S_FIFO_ATN_LVL_TWELVE_SLOTS << I2S_FIFO2_ATN_LVL_SHIFT)
+
+#define I2S_I2S_FIFO_SCR_FIFO1_ATN_LVL_MASK \
+ (3 << I2S_FIFO1_ATN_LVL_SHIFT)
+#define I2S_I2S_FIFO_SCR_FIFO1_ATN_LVL_ONE_SLOT \
+ (I2S_FIFO_ATN_LVL_ONE_SLOT << I2S_FIFO1_ATN_LVL_SHIFT)
+#define I2S_I2S_FIFO_SCR_FIFO1_ATN_LVL_FOUR_SLOTS \
+ (I2S_FIFO_ATN_LVL_FOUR_SLOTS << I2S_FIFO1_ATN_LVL_SHIFT)
+#define I2S_I2S_FIFO_SCR_FIFO1_ATN_LVL_EIGHT_SLOTS \
+ (I2S_FIFO_ATN_LVL_EIGHT_SLOTS << I2S_FIFO1_ATN_LVL_SHIFT)
+#define I2S_I2S_FIFO_SCR_FIFO1_ATN_LVL_TWELVE_SLOTS \
+ (I2S_FIFO_ATN_LVL_TWELVE_SLOTS << I2S_FIFO1_ATN_LVL_SHIFT)
+/*
+ * I2S_I2S_PCM_CTRL_0
+ */
+#define I2S_PCM_TRM_EDGE_POS_EDGE_NO_HIGHZ 0
+#define I2S_PCM_TRM_EDGE_POS_EDGE_HIGHZ 1
+#define I2S_PCM_TRM_EDGE_NEG_EDGE_NO_HIGHZ 2
+#define I2S_PCM_TRM_EDGE_NEG_EDGE_HIGHZ 3
+#define I2S_PCM_TRM_EDGE_CTRL_SHIFT 9
+
+#define I2S_I2S_PCM_TRM_EDGE_CTRL_MASK \
+ (3 << I2S_I2S_PCM_TRM_EDGE_CTRL_SHIFT)
+#define I2S_I2S_PCM_TRM_EDGE_POS_EDGE_NO_HIGHZ \
+ (I2S_PCM_TRM_EDGE_POS_EDGE_HIGHZ \
+ << I2S_PCM_TRM_EDGE_CTRL_SHIFT)
+#define I2S_I2S_PCM_TRM_EDGE_POS_EDGE_HIGHZ \
+ (I2S_PCM_TRM_EDGE_POS_EDGE_NO_HIGHZ \
+ << I2S_PCM_TRM_EDGE_CTRL_SHIFT)
+#define I2S_I2S_PCM_TRM_EDGE_NEG_EDGE_NO_HIGHZ \
+ (I2S_PCM_TRM_EDGE_NEG_EDGE_NO_HIGHZ \
+ << I2S_PCM_TRM_EDGE_CTRL_SHIFT)
+#define I2S_I2S_PCM_TRM_EDGE_NEG_EDGE_HIGHZ \
+ (I2S_PCM_TRM_EDGE_NEG_EDGE_HIGHZ \
+ << I2S_PCM_TRM_EDGE_CTRL_SHIFT)
+
+#define I2S_PCM_TRM_MASK_BITS_ZERO 0
+#define I2S_PCM_TRM_MASK_BITS_ONE 1
+#define I2S_PCM_TRM_MASK_BITS_TWO 2
+#define I2S_PCM_TRM_MASK_BITS_THREE 3
+#define I2S_PCM_TRM_MASK_BITS_FOUR 4
+#define I2S_PCM_TRM_MASK_BITS_FIVE 5
+#define I2S_PCM_TRM_MASK_BITS_SIX 6
+#define I2S_PCM_TRM_MASK_BITS_SEVEN 7
+#define I2S_PCM_TRM_MASK_BITS_SHIFT 6
+
+#define I2S_I2S_PCM_TRM_MASK_BITS_MASK \
+ (7 << I2S_PCM_TRM_MASK_BITS_SHIFT)
+#define I2S_I2S_PCM_TRM_MASK_BITS_ZERO \
+ (I2S_PCM_TRM_MASK_BITS_ZERO \
+ << I2S_PCM_TRM_MASK_BITS_SHIFT)
+#define I2S_I2S_PCM_TRM_MASK_BITS_ONE \
+ (I2S_PCM_TRM_MASK_BITS_ONE \
+ << I2S_PCM_TRM_MASK_BITS_SHIFT)
+#define I2S_I2S_PCM_TRM_MASK_BITS_TWO \
+ (I2S_PCM_TRM_MASK_BITS_TWO \
+ << I2S_PCM_TRM_MASK_BITS_SHIFT)
+#define I2S_I2S_PCM_TRM_MASK_BITS_THREE \
+ (I2S_PCM_TRM_MASK_BITS_THREE \
+ << I2S_PCM_TRM_MASK_BITS_SHIFT)
+#define I2S_I2S_PCM_TRM_MASK_BITS_FOUR \
+ (I2S_PCM_TRM_MASK_BITS_FOUR \
+ << I2S_PCM_TRM_MASK_BITS_SHIFT)
+#define I2S_I2S_PCM_TRM_MASK_BITS_FIVE \
+ (I2S_PCM_TRM_MASK_BITS_FIVE \
+ << I2S_PCM_TRM_MASK_BITS_SHIFT)
+#define I2S_I2S_PCM_TRM_MASK_BITS_SIX \
+ (I2S_PCM_TRM_MASK_BITS_SIX \
+ << I2S_PCM_TRM_MASK_BITS_SHIFT)
+#define I2S_I2S_PCM_TRM_MASK_BITS_SEVEN \
+ (I2S_PCM_TRM_MASK_BITS_SEVEN \
+ << I2S_PCM_TRM_MASK_BITS_SHIFT)
+
+#define I2S_I2S_PCM_CTRL_FSYNC_PCM_CTRL (1<<5)
+#define I2S_I2S_PCM_CTRL_TRM_MODE (1<<4)
+
+#define I2S_PCM_RCV_MASK_BITS_ZERO 0
+#define I2S_PCM_RCV_MASK_BITS_ONE 1
+#define I2S_PCM_RCV_MASK_BITS_TWO 2
+#define I2S_PCM_RCV_MASK_BITS_THREE 3
+#define I2S_PCM_RCV_MASK_BITS_FOUR 4
+#define I2S_PCM_RCV_MASK_BITS_FIVE 5
+#define I2S_PCM_RCV_MASK_BITS_SIX 6
+#define I2S_PCM_RCV_MASK_BITS_SEVEN 7
+#define I2S_PCM_RCV_MASK_BITS_SHIFT 1
+
+#define I2S_I2S_PCM_RCV_MASK_BITS_MASK \
+ (7 << I2S_PCM_RCV_MASK_BITS_SHIFT)
+#define I2S_I2S_PCM_RCV_MASK_BITS_ZERO \
+ (I2S_PCM_RCV_MASK_BITS_ZERO \
+ << I2S_PCM_RCV_MASK_BITS_SHIFT)
+#define I2S_I2S_PCM_RCV_MASK_BITS_ONE \
+ (I2S_PCM_RCV_MASK_BITS_ONE \
+ << I2S_PCM_RCV_MASK_BITS_SHIFT)
+#define I2S_I2S_PCM_RCV_MASK_BITS_TWO \
+ (I2S_PCM_RCV_MASK_BITS_TWO \
+ << I2S_PCM_RCV_MASK_BITS_SHIFT)
+#define I2S_I2S_PCM_RCV_MASK_BITS_THREE \
+ (I2S_PCM_RCV_MASK_BITS_THREE \
+ << I2S_PCM_RCV_MASK_BITS_SHIFT)
+#define I2S_I2S_PCM_RCV_MASK_BITS_FOUR \
+ (I2S_PCM_RCV_MASK_BITS_FOUR \
+ << I2S_PCM_RCV_MASK_BITS_SHIFT)
+#define I2S_I2S_PCM_RCV_MASK_BITS_FIVE \
+ (I2S_PCM_RCV_MASK_BITS_FIVE \
+ << I2S_PCM_RCV_MASK_BITS_SHIFT)
+#define I2S_I2S_PCM_RCV_MASK_BITS_SIX \
+ (I2S_PCM_RCV_MASK_BITS_SIX \
+ << I2S_PCM_RCV_MASK_BITS_SHIFT)
+#define I2S_I2S_PCM_RCV_MASK_BITS_SEVEN \
+ (I2S_PCM_RCV_MASK_BITS_SEVEN \
+ << I2S_PCM_RCV_MASK_BITS_SHIFT)
+
+#define I2S_I2S_PCM_CTRL_RCV_MODE (1<<0)
+
+/*
+ * I2S_I2S_NW_CTRL_0
+ */
+
+#define I2S_TRM_TLPHY_SLOT_SEL_SLOT1 0
+#define I2S_TRM_TLPHY_SLOT_SEL_SLOT2 1
+#define I2S_TRM_TLPHY_SLOT_SEL_SLOT3 2
+#define I2S_TRM_TLPHY_SLOT_SEL_SLOT4 3
+#define I2S_I2S_NW_TRM_TLPHY_SLOT_SEL_SHIFT 4
+
+#define I2S_I2S_NW_TRM_TLPHY_SLOT_SEL_MASK \
+ (3 << I2S_TRM_TLPHY_SLOT_SEL_SHIFT)
+#define I2S_I2S_TRM_TLPHY_SLOT_SEL_SLOT1 \
+ (I2S_TRM_TLPHY_SLOT_SEL_SLOT1 \
+ << I2S_I2S_NW_TRM_TLPHY_SLOT_SEL_SHIFT)
+#define I2S_I2S_TRM_TLPHY_SLOT_SEL_SLOT2 \
+ (I2S_TRM_TLPHY_SLOT_SEL_SLOT2 \
+ << I2S_I2S_NW_TRM_TLPHY_SLOT_SEL_SHIFT)
+#define I2S_I2S_TRM_TLPHY_SLOT_SEL_SLOT3 \
+ (I2S_TRM_TLPHY_SLOT_SEL_SLOT3 \
+ << I2S_I2S_NW_TRM_TLPHY_SLOT_SEL_SHIFT)
+#define I2S_I2S_TRM_TLPHY_SLOT_SEL_SLOT4 \
+ (I2S_TRM_TLPHY_SLOT_SEL_SLOT4 \
+ << I2S_I2S_NW_TRM_TLPHY_SLOT_SEL_SHIFT)
+
+#define I2S_I2S_NW_CTRL_TRM_TLPHY_MODE (1<<3)
+
+#define I2S_RCV_TLPHY_SLOT_SEL_SLOT1 0
+#define I2S_RCV_TLPHY_SLOT_SEL_SLOT2 1
+#define I2S_RCV_TLPHY_SLOT_SEL_SLOT3 2
+#define I2S_RCV_TLPHY_SLOT_SEL_SLOT4 3
+#define I2S_I2S_NW_RCV_TLPHY_SLOT_SEL_SHIFT 1
+
+#define I2S_I2S_NW_RCV_TLPHY_SLOT_SEL_MASK \
+ (3 << I2S_RCV_TLPHY_SLOT_SEL_SHIFT)
+#define I2S_I2S_RCV_TLPHY_SLOT_SEL_SLOT1 \
+ (I2S_RCV_TLPHY_SLOT_SEL_SLOT1 \
+ << I2S_I2S_NW_RCV_TLPHY_SLOT_SEL_SHIFT)
+#define I2S_I2S_RCV_TLPHY_SLOT_SEL_SLOT2 \
+ (I2S_RCV_TLPHY_SLOT_SEL_SLOT2 \
+ << I2S_I2S_NW_RCV_TLPHY_SLOT_SEL_SHIFT)
+#define I2S_I2S_RCV_TLPHY_SLOT_SEL_SLOT3 \
+ (I2S_RCV_TLPHY_SLOT_SEL_SLOT3 \
+ << I2S_I2S_NW_RCV_TLPHY_SLOT_SEL_SHIFT)
+#define I2S_I2S_RCV_TLPHY_SLOT_SEL_SLOT4 \
+ (I2S_RCV_TLPHY_SLOT_SEL_SLOT4 \
+ << I2S_I2S_NW_RCV_TLPHY_SLOT_SEL_SHIFT)
+
+#define I2S_I2S_NW_CTRL_RCV_TLPHY_MODE (1<<0)
+
+#endif /* __ARCH_ARM_MACH_TEGRA_I2S_H */
diff --git a/arch/arm/mach-tegra/include/mach/io.h b/arch/arm/mach-tegra/include/mach/io.h
index 4cea2230c8dc..77cc39a30ed3 100644
--- a/arch/arm/mach-tegra/include/mach/io.h
+++ b/arch/arm/mach-tegra/include/mach/io.h
@@ -2,6 +2,7 @@
* arch/arm/mach-tegra/include/mach/io.h
*
* Copyright (C) 2010 Google, Inc.
+ * Copyright (C) 2011 NVIDIA Corporation.
*
* Author:
* Colin Cross <ccross@google.com>
@@ -37,9 +38,9 @@
#define IO_IRAM_VIRT 0xFE400000
#define IO_IRAM_SIZE SZ_256K
-#define IO_CPU_PHYS 0x50040000
-#define IO_CPU_VIRT 0xFE000000
-#define IO_CPU_SIZE SZ_16K
+#define IO_CPU_PHYS 0x50000000
+#define IO_CPU_VIRT 0xFE000000
+#define IO_CPU_SIZE SZ_1M
#define IO_PPSB_PHYS 0x60000000
#define IO_PPSB_VIRT 0xFE200000
@@ -49,6 +50,26 @@
#define IO_APB_VIRT 0xFE300000
#define IO_APB_SIZE SZ_1M
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+#define IO_USB_PHYS 0xC5000000
+#else
+#define IO_USB_PHYS 0x7D000000
+#endif
+#define IO_USB_VIRT 0xFE500000
+#define IO_USB_SIZE SZ_1M
+
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+#define IO_SDMMC_PHYS 0xC8000000
+#else
+#define IO_SDMMC_PHYS 0x78000000
+#endif
+#define IO_SDMMC_VIRT 0xFE600000
+#define IO_SDMMC_SIZE SZ_1M
+
+#define IO_HOST1X_PHYS 0x54000000
+#define IO_HOST1X_VIRT 0xFE700000
+#define IO_HOST1X_SIZE SZ_4M
+
#define IO_TO_VIRT_BETWEEN(p, st, sz) ((p) >= (st) && (p) < ((st) + (sz)))
#define IO_TO_VIRT_XLATE(p, pst, vst) (((p) - (pst) + (vst)))
@@ -61,6 +82,12 @@
IO_TO_VIRT_XLATE((n), IO_CPU_PHYS, IO_CPU_VIRT) : \
IO_TO_VIRT_BETWEEN((n), IO_IRAM_PHYS, IO_IRAM_SIZE) ? \
IO_TO_VIRT_XLATE((n), IO_IRAM_PHYS, IO_IRAM_VIRT) : \
+ IO_TO_VIRT_BETWEEN((n), IO_HOST1X_PHYS, IO_HOST1X_SIZE) ? \
+ IO_TO_VIRT_XLATE((n), IO_HOST1X_PHYS, IO_HOST1X_VIRT) : \
+ IO_TO_VIRT_BETWEEN((n), IO_USB_PHYS, IO_USB_SIZE) ? \
+ IO_TO_VIRT_XLATE((n), IO_USB_PHYS, IO_USB_VIRT) : \
+ IO_TO_VIRT_BETWEEN((n), IO_SDMMC_PHYS, IO_SDMMC_SIZE) ? \
+ IO_TO_VIRT_XLATE((n), IO_SDMMC_PHYS, IO_SDMMC_VIRT) : \
0)
#ifndef __ASSEMBLER__
diff --git a/arch/arm/mach-tegra/include/mach/iomap.h b/arch/arm/mach-tegra/include/mach/iomap.h
index 19dec3ac0854..4c42d86d243e 100644
--- a/arch/arm/mach-tegra/include/mach/iomap.h
+++ b/arch/arm/mach-tegra/include/mach/iomap.h
@@ -2,11 +2,14 @@
* arch/arm/mach-tegra/include/mach/iomap.h
*
* Copyright (C) 2010 Google, Inc.
+ * Copyright (C) 2011 NVIDIA Corporation.
*
* Author:
* Colin Cross <ccross@google.com>
* Erik Gilling <konkers@google.com>
*
+ * Copyright (C) 2010-2011 NVIDIA Corporation
+ *
* 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.
@@ -23,9 +26,29 @@
#include <asm/sizes.h>
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
+#define TEGRA_NOR_FLASH_BASE 0xD0000000
+#define TEGRA_NOR_FLASH_SIZE SZ_256M
+#else
+#define TEGRA_NOR_FLASH_BASE 0x48000000
+#define TEGRA_NOR_FLASH_SIZE SZ_128M
+#endif
+
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
+#define TEGRA_DRAM_BASE 0x00000000
+#define TEGRA_DRAM_SIZE SZ_1G /* Maximum size */
+#else
+#define TEGRA_DRAM_BASE 0x80000000
+#define TEGRA_DRAM_SIZE (SZ_2G - SZ_1M) /* Maximum size */
+#endif
+
#define TEGRA_IRAM_BASE 0x40000000
#define TEGRA_IRAM_SIZE SZ_256K
+/* First 1K of IRAM is reserved for cpu reset handler. */
+#define TEGRA_RESET_HANDLER_BASE TEGRA_IRAM_BASE
+#define TEGRA_RESET_HANDLER_SIZE SZ_1K
+
#define TEGRA_HOST1X_BASE 0x50000000
#define TEGRA_HOST1X_SIZE 0x24000
@@ -56,23 +79,53 @@
#define TEGRA_HDMI_BASE 0x54280000
#define TEGRA_HDMI_SIZE SZ_256K
+#define TEGRA_DSI_BASE 0x54300000
+#define TEGRA_DSI_SIZE SZ_256K
+
+#define TEGRA_DSIB_BASE 0x54400000
+#define TEGRA_DSIB_SIZE SZ_256K
+
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
+
#define TEGRA_GART_BASE 0x58000000
#define TEGRA_GART_SIZE SZ_32M
-#define TEGRA_RES_SEMA_BASE 0x60001000
+#else
+
+#define TEGRA_SMMU_BASE_A01 0xe0000000
+#define TEGRA_SMMU_SIZE_A01 SZ_256M
+#define TEGRA_SMMU_BASE 0x00001000
+#define TEGRA_SMMU_SIZE (SZ_1G - SZ_4K * 2)
+
+#endif
+
#define TEGRA_RES_SEMA_SIZE SZ_4K
+#define TEGRA_RES_SEMA_BASE 0x60001000
+
+#define TEGRA_ARB_SEMA_BASE 0x60002000
+#define TEGRA_ARB_SEMA_SIZE SZ_4K
#define TEGRA_PRIMARY_ICTLR_BASE 0x60004000
-#define TEGRA_PRIMARY_ICTLR_SIZE SZ_64
+#define TEGRA_PRIMARY_ICTLR_SIZE 64
+
+#define TEGRA_ARBGNT_ICTLR_BASE 0x60004040
+#define TEGRA_ARBGNT_ICTLR_SIZE 192
#define TEGRA_SECONDARY_ICTLR_BASE 0x60004100
-#define TEGRA_SECONDARY_ICTLR_SIZE SZ_64
+#define TEGRA_SECONDARY_ICTLR_SIZE 64
#define TEGRA_TERTIARY_ICTLR_BASE 0x60004200
-#define TEGRA_TERTIARY_ICTLR_SIZE SZ_64
+#define TEGRA_TERTIARY_ICTLR_SIZE 64
#define TEGRA_QUATERNARY_ICTLR_BASE 0x60004300
-#define TEGRA_QUATERNARY_ICTLR_SIZE SZ_64
+#define TEGRA_QUATERNARY_ICTLR_SIZE 64
+
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+
+#define TEGRA_QUINARY_ICTLR_BASE 0x60004400
+#define TEGRA_QUINARY_ICTLR_SIZE SZ_64
+
+#endif
#define TEGRA_TMR1_BASE 0x60005000
#define TEGRA_TMR1_SIZE SZ_8
@@ -81,7 +134,7 @@
#define TEGRA_TMR2_SIZE SZ_8
#define TEGRA_TMRUS_BASE 0x60005010
-#define TEGRA_TMRUS_SIZE SZ_64
+#define TEGRA_TMRUS_SIZE 64
#define TEGRA_TMR3_BASE 0x60005050
#define TEGRA_TMR3_SIZE SZ_8
@@ -89,6 +142,43 @@
#define TEGRA_TMR4_BASE 0x60005058
#define TEGRA_TMR4_SIZE SZ_8
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+
+#define TEGRA_TMR5_BASE 0x60005060
+#define TEGRA_TMR5_SIZE 8
+
+#define TEGRA_TMR6_BASE 0x60005068
+#define TEGRA_TMR6_SIZE 8
+
+#define TEGRA_TMR7_BASE 0x60005070
+#define TEGRA_TMR7_SIZE 8
+
+#define TEGRA_TMR8_BASE 0x60005078
+#define TEGRA_TMR8_SIZE 8
+
+#define TEGRA_TMR9_BASE 0x60005080
+#define TEGRA_TMR9_SIZE 8
+
+#define TEGRA_TMR10_BASE 0x60005088
+#define TEGRA_TMR10_SIZE 8
+
+#define TEGRA_WDT0_BASE 0x60005100
+#define TEGRA_WDT0_SIZE 32
+
+#define TEGRA_WDT1_BASE 0x60005120
+#define TEGRA_WDT1_SIZE 32
+
+#define TEGRA_WDT2_BASE 0x60005140
+#define TEGRA_WDT2_SIZE 32
+
+#define TEGRA_WDT3_BASE 0x60005160
+#define TEGRA_WDT3_SIZE 32
+
+#define TEGRA_WDT4_BASE 0x60005180
+#define TEGRA_WDT4_SIZE 32
+
+#endif
+
#define TEGRA_CLK_RESET_BASE 0x60006000
#define TEGRA_CLK_RESET_SIZE SZ_4K
@@ -107,24 +197,49 @@
#define TEGRA_APB_DMA_CH0_BASE 0x6000B000
#define TEGRA_APB_DMA_CH0_SIZE 32
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+
+#define TEGRA_AHB_ARB_BASE 0x6000C000
+#define TEGRA_AHB_ARB_SIZE 768 /* Overlaps with GISMO */
+
+#endif
+
#define TEGRA_AHB_GIZMO_BASE 0x6000C004
#define TEGRA_AHB_GIZMO_SIZE 0x10C
+#define TEGRA_SB_BASE 0x6000C200
+#define TEGRA_SB_SIZE 256
+
#define TEGRA_STATMON_BASE 0x6000C400
#define TEGRA_STATMON_SIZE SZ_1K
+#if !defined(CONFIG_ARCH_TEGRA_2x_SOC)
+
+#define TEGRA_ACTMON_BASE 0x6000C800
+#define TEGRA_ACTMON_SIZE SZ_1K
+
+#endif
+
#define TEGRA_GPIO_BASE 0x6000D000
#define TEGRA_GPIO_SIZE SZ_4K
#define TEGRA_EXCEPTION_VECTORS_BASE 0x6000F000
#define TEGRA_EXCEPTION_VECTORS_SIZE SZ_4K
+#define TEGRA_BSEA_BASE 0x60010000
+#define TEGRA_BSEA_SIZE SZ_4K
+
+#define TEGRA_VDE_BASE 0x6001A000
+#define TEGRA_VDE_SIZE 0x3c00
+
#define TEGRA_APB_MISC_BASE 0x70000000
#define TEGRA_APB_MISC_SIZE SZ_4K
#define TEGRA_APB_MISC_DAS_BASE 0x70000c00
#define TEGRA_APB_MISC_DAS_SIZE SZ_128
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
+
#define TEGRA_AC97_BASE 0x70002000
#define TEGRA_AC97_SIZE SZ_512
@@ -137,11 +252,66 @@
#define TEGRA_I2S2_BASE 0x70002A00
#define TEGRA_I2S2_SIZE SZ_256
+#elif defined(CONFIG_ARCH_TEGRA_3x_SOC)
+
+#define TEGRA_TSENSOR_BASE 0x70014000
+#define TEGRA_TSENSOR_SIZE SZ_4K
+
+#define TEGRA_HDA_BASE 0x70030000
+#define TEGRA_HDA_SIZE SZ_64K
+
+#define TEGRA_AUDIO_CLUSTER_BASE 0x70080000
+#define TEGRA_AUDIO_CLUSTER_SIZE SZ_4K
+
+#define TEGRA_APBIF0_BASE TEGRA_AUDIO_CLUSTER_BASE
+#define TEGRA_APBIF0_SIZE 32
+
+#define TEGRA_APBIF1_BASE 0x70080020
+#define TEGRA_APBIF1_SIZE 32
+
+#define TEGRA_APBIF2_BASE 0x70080040
+#define TEGRA_APBIF2_SIZE 32
+
+#define TEGRA_APBIF3_BASE 0x70080060
+#define TEGRA_APBIF3_SIZE 32
+
+#define TEGRA_AHUB_BASE 0x70080200
+#define TEGRA_AHUB_SIZE SZ_256
+
+#define TEGRA_I2S0_BASE 0x70080300
+#define TEGRA_I2S0_SIZE SZ_256
+
+#define TEGRA_I2S1_BASE 0x70080400
+#define TEGRA_I2S1_SIZE SZ_256
+
+#define TEGRA_I2S2_BASE 0x70080500
+#define TEGRA_I2S2_SIZE SZ_256
+
+#define TEGRA_I2S3_BASE 0x70080600
+#define TEGRA_I2S3_SIZE SZ_256
+
+#define TEGRA_I2S4_BASE 0x70080700
+#define TEGRA_I2S4_SIZE SZ_256
+
+#define TEGRA_DAM0_BASE 0x70080800
+#define TEGRA_DAM0_SIZE SZ_256
+
+#define TEGRA_DAM1_BASE 0x70080900
+#define TEGRA_DAM1_SIZE SZ_256
+
+#define TEGRA_DAM2_BASE 0x70080A00
+#define TEGRA_DAM2_SIZE SZ_256
+
+#define TEGRA_SPDIF_BASE 0x70080B00
+#define TEGRA_SPDIF_SIZE SZ_256
+
+#endif
+
#define TEGRA_UARTA_BASE 0x70006000
-#define TEGRA_UARTA_SIZE SZ_64
+#define TEGRA_UARTA_SIZE 64
#define TEGRA_UARTB_BASE 0x70006040
-#define TEGRA_UARTB_SIZE SZ_64
+#define TEGRA_UARTB_SIZE 64
#define TEGRA_UARTC_BASE 0x70006200
#define TEGRA_UARTC_SIZE SZ_256
@@ -185,9 +355,18 @@
#define TEGRA_TWC_BASE 0x7000C100
#define TEGRA_TWC_SIZE SZ_256
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
+
#define TEGRA_SPI_BASE 0x7000C380
#define TEGRA_SPI_SIZE 48
+#else
+
+#define TEGRA_DTV_BASE 0x7000C300
+#define TEGRA_DTV_SIZE SZ_256
+
+#endif
+
#define TEGRA_I2C2_BASE 0x7000C400
#define TEGRA_I2C2_SIZE SZ_256
@@ -197,9 +376,21 @@
#define TEGRA_OWR_BASE 0x7000C600
#define TEGRA_OWR_SIZE 80
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
+
#define TEGRA_DVC_BASE 0x7000D000
#define TEGRA_DVC_SIZE SZ_512
+#else
+
+#define TEGRA_I2C4_BASE 0x7000C700
+#define TEGRA_I2C4_SIZE SZ_512
+
+#define TEGRA_I2C5_BASE 0x7000D000
+#define TEGRA_I2C5_SIZE SZ_512
+
+#endif
+
#define TEGRA_SPI1_BASE 0x7000D400
#define TEGRA_SPI1_SIZE SZ_512
@@ -212,6 +403,16 @@
#define TEGRA_SPI4_BASE 0x7000DA00
#define TEGRA_SPI4_SIZE SZ_512
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+
+#define TEGRA_SPI5_BASE 0x7000DC00
+#define TEGRA_SPI5_SIZE SZ_512
+
+#define TEGRA_SPI6_BASE 0x7000DE00
+#define TEGRA_SPI6_SIZE SZ_512
+
+#endif
+
#define TEGRA_RTC_BASE 0x7000E000
#define TEGRA_RTC_SIZE SZ_256
@@ -236,6 +437,8 @@
#define TEGRA_CSITE_BASE 0x70040000
#define TEGRA_CSITE_SIZE SZ_256K
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
+
#define TEGRA_USB_BASE 0xC5000000
#define TEGRA_USB_SIZE SZ_16K
@@ -257,6 +460,43 @@
#define TEGRA_SDMMC4_BASE 0xC8000600
#define TEGRA_SDMMC4_SIZE SZ_512
+#else
+
+#define TEGRA_SATA_BASE 0x70020000
+#define TEGRA_SATA_SIZE SZ_64K
+
+#define TEGRA_SATA_CONFIG_BASE 0x70021000
+#define TEGRA_SATA_CONFIG_SIZE SZ_4K
+
+#define TEGRA_SATA_BAR5_BASE 0x70027000
+#define TEGRA_SATA_BAR5_SIZE SZ_8K
+
+#define TEGRA_SDMMC1_BASE 0x78000000
+#define TEGRA_SDMMC1_SIZE SZ_512
+
+#define TEGRA_SDMMC2_BASE 0x78000200
+#define TEGRA_SDMMC2_SIZE SZ_512
+
+#define TEGRA_SDMMC3_BASE 0x78000400
+#define TEGRA_SDMMC3_SIZE SZ_512
+
+#define TEGRA_SDMMC4_BASE 0x78000600
+#define TEGRA_SDMMC4_SIZE SZ_512
+
+#define TEGRA_USB_BASE 0x7D000000
+#define TEGRA_USB_SIZE SZ_16K
+
+#define TEGRA_USB2_BASE 0x7D004000
+#define TEGRA_USB2_SIZE SZ_16K
+
+#define TEGRA_USB3_BASE 0x7D008000
+#define TEGRA_USB3_SIZE SZ_16K
+
+#define TEGRA_SE_BASE 0x70012000
+#define TEGRA_SE_SIZE SZ_8K
+
+#endif
+
#if defined(CONFIG_TEGRA_DEBUG_UART_NONE)
# define TEGRA_DEBUG_UART_BASE 0
#elif defined(CONFIG_TEGRA_DEBUG_UARTA)
diff --git a/arch/arm/mach-tegra/include/mach/iovmm.h b/arch/arm/mach-tegra/include/mach/iovmm.h
new file mode 100644
index 000000000000..fd83a326e129
--- /dev/null
+++ b/arch/arm/mach-tegra/include/mach/iovmm.h
@@ -0,0 +1,323 @@
+/*
+ * arch/arm/mach-tegra/include/mach/iovmm.h
+ *
+ * Copyright (c) 2010-2011, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed i the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/list.h>
+#include <linux/platform_device.h>
+#include <linux/miscdevice.h>
+#include <linux/rbtree.h>
+#include <linux/rwsem.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+
+#ifndef _MACH_TEGRA_IOVMM_H_
+#define _MACH_TEGRA_IOVMM_H_
+
+typedef u32 tegra_iovmm_addr_t;
+
+struct tegra_iovmm_device_ops;
+
+/*
+ * each I/O virtual memory manager unit should register a device with
+ * the iovmm system
+ */
+struct tegra_iovmm_device {
+ struct tegra_iovmm_device_ops *ops;
+ const char *name;
+ struct list_head list;
+ int pgsize_bits;
+};
+
+/*
+ * tegra_iovmm_domain serves a purpose analagous to mm_struct as defined in
+ * <linux/mm_types.h> - it defines a virtual address space within which
+ * tegra_iovmm_areas can be created.
+ */
+struct tegra_iovmm_domain {
+ atomic_t clients;
+ atomic_t locks;
+ spinlock_t block_lock; /* RB-tree for iovmm_area blocks */
+ unsigned long flags;
+ wait_queue_head_t delay_lock; /* when lock_client fails */
+ struct rw_semaphore map_lock;
+ struct rb_root all_blocks; /* ordered by address */
+ struct rb_root free_blocks; /* ordered by size */
+ struct tegra_iovmm_device *dev;
+};
+
+/*
+ * tegra_iovmm_client is analagous to an individual task in the task group
+ * which owns an mm_struct.
+ */
+
+struct iovmm_share_group;
+
+struct tegra_iovmm_client {
+ const char *name;
+ unsigned long flags;
+ struct iovmm_share_group *group;
+ struct tegra_iovmm_domain *domain;
+ struct miscdevice *misc_dev;
+ struct list_head list;
+};
+
+/*
+ * tegra_iovmm_area serves a purpose analagous to vm_area_struct as defined
+ * in <linux/mm_types.h> - it defines a virtual memory area which can be
+ * mapped to physical memory by a client-provided mapping function. */
+
+struct tegra_iovmm_area {
+ struct tegra_iovmm_domain *domain;
+ tegra_iovmm_addr_t iovm_start;
+ size_t iovm_length;
+ pgprot_t pgprot;
+ struct tegra_iovmm_area_ops *ops;
+};
+
+struct tegra_iovmm_device_ops {
+ /* maps a VMA using the page residency functions provided by the VMA */
+ int (*map)(struct tegra_iovmm_domain *domain,
+ struct tegra_iovmm_area *io_vma);
+ /* marks all PTEs in a VMA as invalid; decommits the virtual addres
+ * space (potentially freeing PDEs when decommit is true.) */
+ void (*unmap)(struct tegra_iovmm_domain *domain,
+ struct tegra_iovmm_area *io_vma, bool decommit);
+ void (*map_pfn)(struct tegra_iovmm_domain *domain,
+ struct tegra_iovmm_area *io_vma,
+ unsigned long offs, unsigned long pfn);
+ /*
+ * ensures that a domain is resident in the hardware's mapping region
+ * so that it may be used by a client
+ */
+ int (*lock_domain)(struct tegra_iovmm_domain *domain,
+ struct tegra_iovmm_client *client);
+ void (*unlock_domain)(struct tegra_iovmm_domain *domain,
+ struct tegra_iovmm_client *client);
+ /*
+ * allocates a vmm_domain for the specified client; may return the same
+ * domain for multiple clients
+ */
+ struct tegra_iovmm_domain* (*alloc_domain)(
+ struct tegra_iovmm_device *dev,
+ struct tegra_iovmm_client *client);
+ void (*free_domain)(struct tegra_iovmm_domain *domain,
+ struct tegra_iovmm_client *client);
+ int (*suspend)(struct tegra_iovmm_device *dev);
+ void (*resume)(struct tegra_iovmm_device *dev);
+};
+
+struct tegra_iovmm_area_ops {
+ /*
+ * ensures that the page of data starting at the specified offset
+ * from the start of the iovma is resident and pinned for use by
+ * DMA, returns the system pfn, or an invalid pfn if the
+ * operation fails.
+ */
+ unsigned long (*lock_makeresident)(struct tegra_iovmm_area *area,
+ tegra_iovmm_addr_t offs);
+ /* called when the page is unmapped from the I/O VMA */
+ void (*release)(struct tegra_iovmm_area *area, tegra_iovmm_addr_t offs);
+};
+
+#ifdef CONFIG_TEGRA_IOVMM
+/*
+ * called by clients to allocate an I/O VMM client mapping context which
+ * will be shared by all clients in the same share_group
+ */
+struct tegra_iovmm_client *tegra_iovmm_alloc_client(const char *name,
+ const char *share_group, struct miscdevice *misc_dev);
+
+size_t tegra_iovmm_get_vm_size(struct tegra_iovmm_client *client);
+
+void tegra_iovmm_free_client(struct tegra_iovmm_client *client);
+
+/*
+ * called by clients to ensure that their mapping context is resident
+ * before performing any DMA operations addressing I/O VMM regions.
+ * client_lock may return -EINTR.
+ */
+int tegra_iovmm_client_lock(struct tegra_iovmm_client *client);
+int tegra_iovmm_client_trylock(struct tegra_iovmm_client *client);
+
+/* called by clients after DMA operations are complete */
+void tegra_iovmm_client_unlock(struct tegra_iovmm_client *client);
+
+/*
+ * called by clients to allocate a new iovmm_area and reserve I/O virtual
+ * address space for it. if ops is NULL, clients should subsequently call
+ * tegra_iovmm_vm_map_pages and/or tegra_iovmm_vm_insert_pfn to explicitly
+ * map the I/O virtual address to an OS-allocated page or physical address,
+ * respectively. VM operations may be called before this call returns
+ */
+struct tegra_iovmm_area *tegra_iovmm_create_vm(
+ struct tegra_iovmm_client *client, struct tegra_iovmm_area_ops *ops,
+ size_t size, size_t align, pgprot_t pgprot, unsigned long iovm_start);
+
+/*
+ * called by clients to "zap" an iovmm_area, and replace all mappings
+ * in it with invalid ones, without freeing the virtual address range
+ */
+void tegra_iovmm_zap_vm(struct tegra_iovmm_area *vm);
+
+/*
+ * after zapping a demand-loaded iovmm_area, the client should unzap it
+ * to allow the VMM device to remap the page range.
+ */
+void tegra_iovmm_unzap_vm(struct tegra_iovmm_area *vm);
+
+/* called by clients to return an iovmm_area to the free pool for the domain */
+void tegra_iovmm_free_vm(struct tegra_iovmm_area *vm);
+
+/* returns size of largest free iovm block */
+size_t tegra_iovmm_get_max_free(struct tegra_iovmm_client *client);
+
+/*
+ * called by client software to map the page-aligned I/O address vaddr to
+ * a specific physical address pfn. I/O VMA should have been created with
+ * a NULL tegra_iovmm_area_ops structure.
+ */
+void tegra_iovmm_vm_insert_pfn(struct tegra_iovmm_area *area,
+ tegra_iovmm_addr_t vaddr, unsigned long pfn);
+
+/*
+ * called by clients to return the iovmm_area containing addr, or NULL if
+ * addr has not been allocated. caller should call tegra_iovmm_area_put when
+ * finished using the returned pointer
+ */
+struct tegra_iovmm_area *tegra_iovmm_find_area_get(
+ struct tegra_iovmm_client *client, tegra_iovmm_addr_t addr);
+
+struct tegra_iovmm_area *tegra_iovmm_area_get(struct tegra_iovmm_area *vm);
+void tegra_iovmm_area_put(struct tegra_iovmm_area *vm);
+
+/* called by drivers to initialize a tegra_iovmm_domain structure */
+int tegra_iovmm_domain_init(struct tegra_iovmm_domain *domain,
+ struct tegra_iovmm_device *dev, tegra_iovmm_addr_t start,
+ tegra_iovmm_addr_t end);
+
+/* called by drivers to register an I/O VMM device with the system */
+int tegra_iovmm_register(struct tegra_iovmm_device *dev);
+
+/* called by drivers to remove an I/O VMM device from the system */
+int tegra_iovmm_unregister(struct tegra_iovmm_device *dev);
+
+#else /* CONFIG_TEGRA_IOVMM */
+
+static inline struct tegra_iovmm_client *tegra_iovmm_alloc_client(
+ const char *name, const char *share_group, struct miscdevice *misc_dev)
+{
+ return NULL;
+}
+
+static inline size_t tegra_iovmm_get_vm_size(struct tegra_iovmm_client *client)
+{
+ return 0;
+}
+
+static inline void tegra_iovmm_free_client(struct tegra_iovmm_client *client)
+{
+}
+
+static inline int tegra_iovmm_client_lock(struct tegra_iovmm_client *client)
+{
+ return 0;
+}
+
+static inline int tegra_iovmm_client_trylock(struct tegra_iovmm_client *client)
+{
+ return 0;
+}
+
+static inline void tegra_iovmm_client_unlock(struct tegra_iovmm_client *client)
+{
+}
+
+static inline struct tegra_iovmm_area *tegra_iovmm_create_vm(
+ struct tegra_iovmm_client *client, struct tegra_iovmm_area_ops *ops,
+ size_t size, size_t align, pgprot_t pgprot, unsigned long iovm_start)
+{
+ return NULL;
+}
+
+static inline void tegra_iovmm_zap_vm(struct tegra_iovmm_area *vm)
+{
+}
+
+static inline void tegra_iovmm_unzap_vm(struct tegra_iovmm_area *vm)
+{
+}
+
+static inline void tegra_iovmm_free_vm(struct tegra_iovmm_area *vm)
+{
+}
+
+static inline size_t tegra_iovmm_get_max_free(struct tegra_iovmm_client *client)
+{
+ return 0;
+}
+
+static inline void tegra_iovmm_vm_insert_pfn(struct tegra_iovmm_area *area,
+ tegra_iovmm_addr_t vaddr, unsigned long pfn)
+{
+}
+
+static inline struct tegra_iovmm_area *tegra_iovmm_find_area_get(
+ struct tegra_iovmm_client *client, tegra_iovmm_addr_t addr)
+{
+ return NULL;
+}
+
+static inline struct tegra_iovmm_area *tegra_iovmm_area_get(
+ struct tegra_iovmm_area *vm)
+{
+ return NULL;
+}
+
+static inline void tegra_iovmm_area_put(struct tegra_iovmm_area *vm)
+{
+}
+
+static inline int tegra_iovmm_domain_init(struct tegra_iovmm_domain *domain,
+ struct tegra_iovmm_device *dev, tegra_iovmm_addr_t start,
+ tegra_iovmm_addr_t end)
+{
+ return 0;
+}
+
+static inline int tegra_iovmm_register(struct tegra_iovmm_device *dev)
+{
+ return 0;
+}
+
+static inline int tegra_iovmm_unregister(struct tegra_iovmm_device *dev)
+{
+ return 0;
+}
+
+static inline int tegra_iovmm_suspend(void)
+{
+ return 0;
+}
+
+static inline void tegra_iovmm_resume(void)
+{
+}
+
+#endif /* CONFIG_TEGRA_IOVMM */
+#endif /* _MACH_TEGRA_IOVMM_H_*/
diff --git a/arch/arm/mach-tegra/include/mach/irqs.h b/arch/arm/mach-tegra/include/mach/irqs.h
index 73265af4dda3..4640f561c06a 100644
--- a/arch/arm/mach-tegra/include/mach/irqs.h
+++ b/arch/arm/mach-tegra/include/mach/irqs.h
@@ -2,6 +2,7 @@
* arch/arm/mach-tegra/include/mach/irqs.h
*
* Copyright (C) 2010 Google, Inc.
+ * Copyright (C) 2011 NVIDIA Corporation.
*
* Author:
* Colin Cross <ccross@google.com>
@@ -90,7 +91,7 @@
#define INT_CPU0_PMU_INTR (INT_SEC_BASE + 24)
#define INT_CPU1_PMU_INTR (INT_SEC_BASE + 25)
#define INT_SEC_RES_26 (INT_SEC_BASE + 26)
-#define INT_S_LINK1 (INT_SEC_BASE + 27)
+#define INT_SPI_1 (INT_SEC_BASE + 27)
#define INT_APB_DMA_COP (INT_SEC_BASE + 28)
#define INT_AHB_DMA_COP (INT_SEC_BASE + 29)
#define INT_DMA_TX (INT_SEC_BASE + 30)
@@ -166,18 +167,215 @@
#define INT_QUAD_RES_30 (INT_QUAD_BASE + 30)
#define INT_QUAD_RES_31 (INT_QUAD_BASE + 31)
-#define INT_MAIN_NR (INT_QUAD_BASE + 32 - INT_PRI_BASE)
+#define INT_GIC_NR (INT_QUAD_BASE + 32)
-#define INT_GPIO_BASE (INT_PRI_BASE + INT_MAIN_NR)
+#define INT_MAIN_NR (INT_GIC_NR - INT_PRI_BASE)
+#define INT_SYNCPT_THRESH_BASE (INT_QUAD_BASE + 32)
+#define INT_SYNCPT_THRESH_NR 32
+
+#define INT_GPIO_BASE (INT_SYNCPT_THRESH_BASE + \
+ INT_SYNCPT_THRESH_NR)
#define INT_GPIO_NR (28 * 8)
+#elif defined(CONFIG_ARCH_TEGRA_3x_SOC)
+
+/* Primary Interrupt Controller */
+#define INT_PRI_BASE (INT_GIC_BASE + 32)
+#define INT_TMR1 (INT_PRI_BASE + 0)
+#define INT_TMR2 (INT_PRI_BASE + 1)
+#define INT_RTC (INT_PRI_BASE + 2)
+#define INT_CEC (INT_PRI_BASE + 3)
+#define INT_SHR_SEM_INBOX_IBF (INT_PRI_BASE + 4)
+#define INT_SHR_SEM_INBOX_IBE (INT_PRI_BASE + 5)
+#define INT_SHR_SEM_OUTBOX_IBF (INT_PRI_BASE + 6)
+#define INT_SHR_SEM_OUTBOX_IBE (INT_PRI_BASE + 7)
+#define INT_VDE_UCQ_ERROR (INT_PRI_BASE + 8)
+#define INT_VDE_SYNC_TOKEN (INT_PRI_BASE + 9)
+#define INT_VDE_BSE_V (INT_PRI_BASE + 10)
+#define INT_VDE_BSE_A (INT_PRI_BASE + 11)
+#define INT_VDE_SXE (INT_PRI_BASE + 12)
+#define INT_SATA_RX_STAT (INT_PRI_BASE + 13)
+#define INT_SDMMC1 (INT_PRI_BASE + 14)
+#define INT_SDMMC2 (INT_PRI_BASE + 15)
+#define INT_XIO (INT_PRI_BASE + 16)
+#define INT_VDE (INT_PRI_BASE + 17)
+#define INT_AVP_UCQ (INT_PRI_BASE + 18)
+#define INT_SDMMC3 (INT_PRI_BASE + 19)
+#define INT_USB (INT_PRI_BASE + 20)
+#define INT_USB2 (INT_PRI_BASE + 21)
+#define INT_HSMMC (INT_PRI_BASE + 22)
+#define INT_SATA_CTL (INT_PRI_BASE + 23)
+#define INT_NANDFLASH (INT_PRI_BASE + 24)
+#define INT_VCP (INT_PRI_BASE + 25)
+#define INT_APB_DMA (INT_PRI_BASE + 26)
+#define INT_AHB_DMA (INT_PRI_BASE + 27)
+#define INT_GNT_0 (INT_PRI_BASE + 28)
+#define INT_GNT_1 (INT_PRI_BASE + 29)
+#define INT_OWR (INT_PRI_BASE + 30)
+#define INT_SDMMC4 (INT_PRI_BASE + 31)
+
+/* Secondary Interrupt Controller */
+#define INT_SEC_BASE (INT_PRI_BASE + 32)
+#define INT_GPIO1 (INT_SEC_BASE + 0)
+#define INT_GPIO2 (INT_SEC_BASE + 1)
+#define INT_GPIO3 (INT_SEC_BASE + 2)
+#define INT_GPIO4 (INT_SEC_BASE + 3)
+#define INT_UARTA (INT_SEC_BASE + 4)
+#define INT_UARTB (INT_SEC_BASE + 5)
+#define INT_I2C (INT_SEC_BASE + 6)
+#define INT_SPI (INT_SEC_BASE + 7)
+#define INT_TWC (INT_SEC_BASE + 8)
+#define INT_TMR3 (INT_SEC_BASE + 9)
+#define INT_TMR4 (INT_SEC_BASE + 10)
+#define INT_FLOW_RSM0 (INT_SEC_BASE + 11)
+#define INT_FLOW_RSM1 (INT_SEC_BASE + 12)
+#define INT_ACTMON (INT_SEC_BASE + 13)
+#define INT_UARTC (INT_SEC_BASE + 14)
+#define INT_MIPI (INT_SEC_BASE + 15)
+#define INT_EVENTA (INT_SEC_BASE + 16)
+#define INT_EVENTB (INT_SEC_BASE + 17)
+#define INT_EVENTC (INT_SEC_BASE + 18)
+#define INT_EVENTD (INT_SEC_BASE + 19)
+#define INT_VFIR (INT_SEC_BASE + 20)
+#define INT_I2C5 (INT_SEC_BASE + 21)
+#define INT_SYS_STATS_MON (INT_SEC_BASE + 22)
+#define INT_GPIO5 (INT_SEC_BASE + 23)
+#define INT_SPEEDO_PMON_0 (INT_SEC_BASE + 24)
+#define INT_SPEEDO_PMON_1 (INT_SEC_BASE + 25)
+#define INT_SE (INT_SEC_BASE + 26)
+#define INT_SPI_1 (INT_SEC_BASE + 27)
+#define INT_APB_DMA_COP (INT_SEC_BASE + 28)
+#define INT_AHB_DMA_COP (INT_SEC_BASE + 29)
+#define INT_DMA_TX (INT_SEC_BASE + 30)
+#define INT_DMA_RX (INT_SEC_BASE + 31)
+
+/* Tertiary Interrupt Controller */
+#define INT_TRI_BASE (INT_SEC_BASE + 32)
+#define INT_HOST1X_COP_SYNCPT (INT_TRI_BASE + 0)
+#define INT_HOST1X_MPCORE_SYNCPT (INT_TRI_BASE + 1)
+#define INT_HOST1X_COP_GENERAL (INT_TRI_BASE + 2)
+#define INT_HOST1X_MPCORE_GENERAL (INT_TRI_BASE + 3)
+#define INT_MPE_GENERAL (INT_TRI_BASE + 4)
+#define INT_VI_GENERAL (INT_TRI_BASE + 5)
+#define INT_EPP_GENERAL (INT_TRI_BASE + 6)
+#define INT_ISP_GENERAL (INT_TRI_BASE + 7)
+#define INT_2D_GENERAL (INT_TRI_BASE + 8)
+#define INT_DISPLAY_GENERAL (INT_TRI_BASE + 9)
+#define INT_DISPLAY_B_GENERAL (INT_TRI_BASE + 10)
+#define INT_HDMI (INT_TRI_BASE + 11)
+#define INT_TVO_GENERAL (INT_TRI_BASE + 12)
+#define INT_MC_GENERAL (INT_TRI_BASE + 13)
+#define INT_EMC_GENERAL (INT_TRI_BASE + 14)
+#define INT_SPI_6 (INT_SEC_BASE + 15)
+#define INT_NOR_FLASH (INT_TRI_BASE + 16)
+#define INT_HDA (INT_TRI_BASE + 17)
+#define INT_SPI_2 (INT_TRI_BASE + 18)
+#define INT_SPI_3 (INT_TRI_BASE + 19)
+#define INT_I2C2 (INT_TRI_BASE + 20)
+#define INT_KBC (INT_TRI_BASE + 21)
+#define INT_EXTERNAL_PMU (INT_TRI_BASE + 22)
+#define INT_GPIO6 (INT_TRI_BASE + 23)
+#define INT_TVDAC (INT_TRI_BASE + 24)
+#define INT_GPIO7 (INT_TRI_BASE + 25)
+#define INT_UARTD (INT_TRI_BASE + 26)
+#define INT_UARTE (INT_TRI_BASE + 27)
+#define INT_I2C3 (INT_TRI_BASE + 28)
+#define INT_SPI_4 (INT_TRI_BASE + 29)
+#define INT_SPI_5 (INT_TRI_BASE + 30)
+#define INT_SW_RESERVED (INT_TRI_BASE + 31)
+
+/* Quaternary Interrupt Controller */
+#define INT_QUAD_BASE (INT_TRI_BASE + 32)
+#define INT_SNOR (INT_QUAD_BASE + 0)
+#define INT_USB3 (INT_QUAD_BASE + 1)
+#define INT_PCIE_INTR (INT_QUAD_BASE + 2)
+#define INT_PCIE_MSI (INT_QUAD_BASE + 3)
+#define INT_PCIE (INT_QUAD_BASE + 4)
+#define INT_AVP_CACHE (INT_QUAD_BASE + 5)
+#define INT_TSENSOR (INT_QUAD_BASE + 6)
+#define INT_AUDIO_CLUSTER (INT_QUAD_BASE + 7)
+#define INT_APB_DMA_CH0 (INT_QUAD_BASE + 8)
+#define INT_APB_DMA_CH1 (INT_QUAD_BASE + 9)
+#define INT_APB_DMA_CH2 (INT_QUAD_BASE + 10)
+#define INT_APB_DMA_CH3 (INT_QUAD_BASE + 11)
+#define INT_APB_DMA_CH4 (INT_QUAD_BASE + 12)
+#define INT_APB_DMA_CH5 (INT_QUAD_BASE + 13)
+#define INT_APB_DMA_CH6 (INT_QUAD_BASE + 14)
+#define INT_APB_DMA_CH7 (INT_QUAD_BASE + 15)
+#define INT_APB_DMA_CH8 (INT_QUAD_BASE + 16)
+#define INT_APB_DMA_CH9 (INT_QUAD_BASE + 17)
+#define INT_APB_DMA_CH10 (INT_QUAD_BASE + 18)
+#define INT_APB_DMA_CH11 (INT_QUAD_BASE + 19)
+#define INT_APB_DMA_CH12 (INT_QUAD_BASE + 20)
+#define INT_APB_DMA_CH13 (INT_QUAD_BASE + 21)
+#define INT_APB_DMA_CH14 (INT_QUAD_BASE + 22)
+#define INT_APB_DMA_CH15 (INT_QUAD_BASE + 23)
+#define INT_I2C4 (INT_QUAD_BASE + 24)
+#define INT_TMR5 (INT_QUAD_BASE + 25)
+#define INT_TMR_SHARED (INT_QUAD_BASE + 26) /* Deprecated */
+#define INT_WDT_CPU (INT_QUAD_BASE + 27)
+#define INT_WDT_AVP (INT_QUAD_BASE + 28)
+#define INT_GPIO8 (INT_QUAD_BASE + 29)
+#define INT_CAR (INT_QUAD_BASE + 30)
+#define INT_QUAD_RES_31 (INT_QUAD_BASE + 31)
+
+/* Quintary Interrupt Controller */
+#define INT_QUINT_BASE (INT_QUAD_BASE + 32)
+#define INT_APB_DMA_CH16 (INT_QUINT_BASE + 0)
+#define INT_APB_DMA_CH17 (INT_QUINT_BASE + 1)
+#define INT_APB_DMA_CH18 (INT_QUINT_BASE + 2)
+#define INT_APB_DMA_CH19 (INT_QUINT_BASE + 3)
+#define INT_APB_DMA_CH20 (INT_QUINT_BASE + 4)
+#define INT_APB_DMA_CH21 (INT_QUINT_BASE + 5)
+#define INT_APB_DMA_CH22 (INT_QUINT_BASE + 6)
+#define INT_APB_DMA_CH23 (INT_QUINT_BASE + 7)
+#define INT_APB_DMA_CH24 (INT_QUINT_BASE + 8)
+#define INT_APB_DMA_CH25 (INT_QUINT_BASE + 9)
+#define INT_APB_DMA_CH26 (INT_QUINT_BASE + 10)
+#define INT_APB_DMA_CH27 (INT_QUINT_BASE + 11)
+#define INT_APB_DMA_CH28 (INT_QUINT_BASE + 12)
+#define INT_APB_DMA_CH29 (INT_QUINT_BASE + 13)
+#define INT_APB_DMA_CH30 (INT_QUINT_BASE + 14)
+#define INT_APB_DMA_CH31 (INT_QUINT_BASE + 15)
+#define INT_CPU0_PMU_INTR (INT_QUINT_BASE + 16)
+#define INT_CPU1_PMU_INTR (INT_QUINT_BASE + 17)
+#define INT_CPU2_PMU_INTR (INT_QUINT_BASE + 18)
+#define INT_CPU3_PMU_INTR (INT_QUINT_BASE + 19)
+#define INT_CPU4_PMU_INTR (INT_QUINT_BASE + 20)
+#define INT_CPU5_PMU_INTR (INT_QUINT_BASE + 21)
+#define INT_CPU6_PMU_INTR (INT_QUINT_BASE + 22)
+#define INT_CPU7_PMU_INTR (INT_QUINT_BASE + 23)
+#define INT_TMR6 (INT_QUINT_BASE + 24)
+#define INT_TMR7 (INT_QUINT_BASE + 25)
+#define INT_TMR8 (INT_QUINT_BASE + 26)
+#define INT_TMR9 (INT_QUINT_BASE + 27)
+#define INT_TMR10 (INT_QUINT_BASE + 28)
+#define INT_QUINT_RES_29 (INT_QUINT_BASE + 29)
+#define INT_QUINT_RES_30 (INT_QUINT_BASE + 30)
+#define INT_QUINT_RES_31 (INT_QUINT_BASE + 31)
+
+#define INT_GIC_NR (INT_QUINT_BASE + 32)
+
+#define INT_MAIN_NR (INT_GIC_NR - INT_PRI_BASE)
+
+#define INT_SYNCPT_THRESH_BASE (INT_QUINT_BASE + 32)
+#define INT_SYNCPT_THRESH_NR 32
+
+#define INT_GPIO_BASE (INT_SYNCPT_THRESH_BASE + \
+ INT_SYNCPT_THRESH_NR)
+#define INT_GPIO_NR (32 * 8)
+
+#endif
+
+#define FIQ_START INT_GIC_BASE
+
#define TEGRA_NR_IRQS (INT_GPIO_BASE + INT_GPIO_NR)
#define INT_BOARD_BASE TEGRA_NR_IRQS
-#define NR_BOARD_IRQS 32
+
+#define NR_BOARD_IRQS 64
#define NR_IRQS (INT_BOARD_BASE + NR_BOARD_IRQS)
-#endif
#endif
diff --git a/arch/arm/mach-tegra/include/mach/kbc.h b/arch/arm/mach-tegra/include/mach/kbc.h
index 4f3572a1c684..174b7d91f4d5 100644
--- a/arch/arm/mach-tegra/include/mach/kbc.h
+++ b/arch/arm/mach-tegra/include/mach/kbc.h
@@ -24,20 +24,41 @@
#include <linux/types.h>
#include <linux/input/matrix_keypad.h>
-#ifdef CONFIG_ARCH_TEGRA_2x_SOC
#define KBC_MAX_GPIO 24
#define KBC_MAX_KPENT 8
-#else
-#define KBC_MAX_GPIO 20
-#define KBC_MAX_KPENT 7
-#endif
#define KBC_MAX_ROW 16
#define KBC_MAX_COL 8
#define KBC_MAX_KEY (KBC_MAX_ROW * KBC_MAX_COL)
+#define KBC_PIN_GPIO_0 0
+#define KBC_PIN_GPIO_1 1
+#define KBC_PIN_GPIO_2 2
+#define KBC_PIN_GPIO_3 3
+#define KBC_PIN_GPIO_4 4
+#define KBC_PIN_GPIO_5 5
+#define KBC_PIN_GPIO_6 6
+#define KBC_PIN_GPIO_7 7
+#define KBC_PIN_GPIO_8 8
+#define KBC_PIN_GPIO_9 9
+#define KBC_PIN_GPIO_10 10
+#define KBC_PIN_GPIO_11 11
+#define KBC_PIN_GPIO_12 12
+#define KBC_PIN_GPIO_13 13
+#define KBC_PIN_GPIO_14 14
+#define KBC_PIN_GPIO_15 15
+#define KBC_PIN_GPIO_16 16
+#define KBC_PIN_GPIO_17 17
+#define KBC_PIN_GPIO_18 18
+#define KBC_PIN_GPIO_19 19
+#define KBC_PIN_GPIO_20 20
+#define KBC_PIN_GPIO_21 21
+#define KBC_PIN_GPIO_22 22
+#define KBC_PIN_GPIO_23 23
+
struct tegra_kbc_pin_cfg {
bool is_row;
+ bool en;
unsigned char num;
};
@@ -49,6 +70,10 @@ struct tegra_kbc_wake_key {
struct tegra_kbc_platform_data {
unsigned int debounce_cnt;
unsigned int repeat_cnt;
+ unsigned int scan_count;
+
+ unsigned int wake_cnt; /* 0:wake on any key >1:wake on wake_cfg */
+ const struct tegra_kbc_wake_key *wake_cfg;
struct tegra_kbc_pin_cfg pin_cfg[KBC_MAX_GPIO];
const struct matrix_keymap_data *keymap_data;
diff --git a/arch/arm/mach-tegra/include/mach/kfuse.h b/arch/arm/mach-tegra/include/mach/kfuse.h
new file mode 100644
index 000000000000..cfe85cc86ff2
--- /dev/null
+++ b/arch/arm/mach-tegra/include/mach/kfuse.h
@@ -0,0 +1,20 @@
+/*
+ * arch/arm/mach-tegra/kfuse.h
+ *
+ * Copyright (C) 2010-2011 NVIDIA Corporation.
+ *
+ * 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.
+ *
+ */
+
+/* there are 144 32-bit values in total */
+#define KFUSE_DATA_SZ (144 * 4)
+
+int tegra_kfuse_read(void *dest, size_t len);
diff --git a/arch/arm/mach-tegra/include/mach/latency_allowance.h b/arch/arm/mach-tegra/include/mach/latency_allowance.h
new file mode 100644
index 000000000000..f0d27f0b8ba9
--- /dev/null
+++ b/arch/arm/mach-tegra/include/mach/latency_allowance.h
@@ -0,0 +1,121 @@
+/*
+ * arch/arm/mach-tegra/include/mach/latency_allowance.h
+ *
+ * Copyright (C) 2011, NVIDIA Corporation.
+ *
+ * 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 _MACH_TEGRA_LATENCY_ALLOWANCE_H_
+#define _MACH_TEGRA_LATENCY_ALLOWANCE_H_
+
+enum tegra_la_id {
+ TEGRA_LA_AFIR = 0,
+ TEGRA_LA_AFIW,
+ TEGRA_LA_AVPC_ARM7R,
+ TEGRA_LA_AVPC_ARM7W,
+ TEGRA_LA_DISPLAY_0A,
+ TEGRA_LA_DISPLAY_0B,
+ TEGRA_LA_DISPLAY_0C,
+ TEGRA_LA_DISPLAY_1B,
+ TEGRA_LA_DISPLAY_HC,
+ TEGRA_LA_DISPLAY_0AB,
+ TEGRA_LA_DISPLAY_0BB,
+ TEGRA_LA_DISPLAY_0CB,
+ TEGRA_LA_DISPLAY_1BB,
+ TEGRA_LA_DISPLAY_HCB,
+ TEGRA_LA_EPPUP,
+ TEGRA_LA_EPPU,
+ TEGRA_LA_EPPV,
+ TEGRA_LA_EPPY,
+ TEGRA_LA_G2PR,
+ TEGRA_LA_G2SR,
+ TEGRA_LA_G2DR,
+ TEGRA_LA_G2DW,
+ TEGRA_LA_HOST1X_DMAR,
+ TEGRA_LA_HOST1XR,
+ TEGRA_LA_HOST1XW,
+ TEGRA_LA_HDAR,
+ TEGRA_LA_HDAW,
+ TEGRA_LA_ISPW,
+ TEGRA_LA_MPCORER,
+ TEGRA_LA_MPCOREW,
+ TEGRA_LA_MPCORE_LPR,
+ TEGRA_LA_MPCORE_LPW,
+ TEGRA_LA_MPE_UNIFBR,
+ TEGRA_LA_MPE_IPRED,
+ TEGRA_LA_MPE_AMEMRD,
+ TEGRA_LA_MPE_CSRD,
+ TEGRA_LA_MPE_UNIFBW,
+ TEGRA_LA_MPE_CSWR,
+ TEGRA_LA_FDCDRD,
+ TEGRA_LA_IDXSRD,
+ TEGRA_LA_TEXSRD,
+ TEGRA_LA_FDCDWR,
+ TEGRA_LA_FDCDRD2,
+ TEGRA_LA_IDXSRD2,
+ TEGRA_LA_TEXSRD2,
+ TEGRA_LA_FDCDWR2,
+ TEGRA_LA_PPCS_AHBDMAR,
+ TEGRA_LA_PPCS_AHBSLVR,
+ TEGRA_LA_PPCS_AHBDMAW,
+ TEGRA_LA_PPCS_AHBSLVW,
+ TEGRA_LA_PTCR,
+ TEGRA_LA_SATAR,
+ TEGRA_LA_SATAW,
+ TEGRA_LA_VDE_BSEVR,
+ TEGRA_LA_VDE_MBER,
+ TEGRA_LA_VDE_MCER,
+ TEGRA_LA_VDE_TPER,
+ TEGRA_LA_VDE_BSEVW,
+ TEGRA_LA_VDE_DBGW,
+ TEGRA_LA_VDE_MBEW,
+ TEGRA_LA_VDE_TPMW,
+ TEGRA_LA_VI_RUV,
+ TEGRA_LA_VI_WSB,
+ TEGRA_LA_VI_WU,
+ TEGRA_LA_VI_WV,
+ TEGRA_LA_VI_WY,
+ TEGRA_LA_MAX_ID
+};
+
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC) || defined(CONFIG_TEGRA_FPGA_PLATFORM)
+static inline int tegra_set_latency_allowance(enum tegra_la_id id,
+ int bandwidth_in_mbps)
+{
+ return 0;
+}
+
+static inline int tegra_enable_latency_scaling(enum tegra_la_id id,
+ unsigned int threshold_low,
+ unsigned int threshold_mid,
+ unsigned int threshold_high)
+{
+ return 0;
+}
+
+static inline void tegra_disable_latency_scaling(enum tegra_la_id id)
+{
+ return 0;
+}
+#else
+int tegra_set_latency_allowance(enum tegra_la_id id,
+ unsigned int bandwidth_in_mbps);
+
+int tegra_enable_latency_scaling(enum tegra_la_id id,
+ unsigned int threshold_low,
+ unsigned int threshold_mid,
+ unsigned int threshold_high);
+
+void tegra_disable_latency_scaling(enum tegra_la_id id);
+#endif
+
+#endif /* _MACH_TEGRA_LATENCY_ALLOWANCE_H_ */
diff --git a/arch/arm/mach-tegra/include/mach/legacy_irq.h b/arch/arm/mach-tegra/include/mach/legacy_irq.h
new file mode 100644
index 000000000000..86f1ff7d06b0
--- /dev/null
+++ b/arch/arm/mach-tegra/include/mach/legacy_irq.h
@@ -0,0 +1,23 @@
+/*
+ * arch/arm/mach-tegra/include/mach/legacy_irq.h
+ *
+ * Copyright (C) 2010 Google, Inc.
+ * Author: Colin Cross <ccross@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 _ARCH_ARM_MACH_TEGRA_LEGARY_IRQ_H
+#define _ARCH_ARM_MACH_TEGRA_LEGARY_IRQ_H
+
+void tegra_init_legacy_irq_cop(void);
+
+#endif
diff --git a/arch/arm/mach-tegra/include/mach/mc.h b/arch/arm/mach-tegra/include/mach/mc.h
new file mode 100644
index 000000000000..576153ad08d6
--- /dev/null
+++ b/arch/arm/mach-tegra/include/mach/mc.h
@@ -0,0 +1,109 @@
+/*
+ * arch/arm/mach-tegra/include/mach/mc.h
+ *
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * Author:
+ * Erik Gilling <konkers@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.
+ *
+ */
+
+#ifndef __MACH_TEGRA_MC_H
+#define __MACH_TEGRA_MC_H
+
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
+#define TEGRA_MC_FPRI_CTRL_AVPC 0x17c
+#define TEGRA_MC_FPRI_CTRL_DC 0x180
+#define TEGRA_MC_FPRI_CTRL_DCB 0x184
+#define TEGRA_MC_FPRI_CTRL_EPP 0x188
+#define TEGRA_MC_FPRI_CTRL_G2 0x18c
+#define TEGRA_MC_FPRI_CTRL_HC 0x190
+#define TEGRA_MC_FPRI_CTRL_ISP 0x194
+#define TEGRA_MC_FPRI_CTRL_MPCORE 0x198
+#define TEGRA_MC_FPRI_CTRL_MPEA 0x19c
+#define TEGRA_MC_FPRI_CTRL_MPEB 0x1a0
+#define TEGRA_MC_FPRI_CTRL_MPEC 0x1a4
+#define TEGRA_MC_FPRI_CTRL_NV 0x1a8
+#define TEGRA_MC_FPRI_CTRL_PPCS 0x1ac
+#define TEGRA_MC_FPRI_CTRL_VDE 0x1b0
+#define TEGRA_MC_FPRI_CTRL_VI 0x1b4
+
+#define TEGRA_MC_CLIENT_AVPCARM7R ((TEGRA_MC_FPRI_CTRL_AVPC << 8) | 0)
+#define TEGRA_MC_CLIENT_AVPCARM7W ((TEGRA_MC_FPRI_CTRL_AVPC << 8) | 2)
+#define TEGRA_MC_CLIENT_DISPLAY0A ((TEGRA_MC_FPRI_CTRL_DC << 8) | 0)
+#define TEGRA_MC_CLIENT_DISPLAY0B ((TEGRA_MC_FPRI_CTRL_DC << 8) | 2)
+#define TEGRA_MC_CLIENT_DISPLAY0C ((TEGRA_MC_FPRI_CTRL_DC << 8) | 4)
+#define TEGRA_MC_CLIENT_DISPLAY1B ((TEGRA_MC_FPRI_CTRL_DC << 8) | 6)
+#define TEGRA_MC_CLIENT_DISPLAYHC ((TEGRA_MC_FPRI_CTRL_DC << 8) | 8)
+#define TEGRA_MC_CLIENT_DISPLAY0AB ((TEGRA_MC_FPRI_CTRL_DCB << 8) | 0)
+#define TEGRA_MC_CLIENT_DISPLAY0BB ((TEGRA_MC_FPRI_CTRL_DCB << 8) | 2)
+#define TEGRA_MC_CLIENT_DISPLAY0CB ((TEGRA_MC_FPRI_CTRL_DCB << 8) | 4)
+#define TEGRA_MC_CLIENT_DISPLAY1BB ((TEGRA_MC_FPRI_CTRL_DCB << 8) | 6)
+#define TEGRA_MC_CLIENT_DISPLAYHCB ((TEGRA_MC_FPRI_CTRL_DCB << 8) | 8)
+#define TEGRA_MC_CLIENT_EPPUP ((TEGRA_MC_FPRI_CTRL_EPP << 8) | 0)
+#define TEGRA_MC_CLIENT_EPPU ((TEGRA_MC_FPRI_CTRL_EPP << 8) | 2)
+#define TEGRA_MC_CLIENT_EPPV ((TEGRA_MC_FPRI_CTRL_EPP << 8) | 4)
+#define TEGRA_MC_CLIENT_EPPY ((TEGRA_MC_FPRI_CTRL_EPP << 8) | 6)
+#define TEGRA_MC_CLIENT_G2PR ((TEGRA_MC_FPRI_CTRL_G2 << 8) | 0)
+#define TEGRA_MC_CLIENT_G2SR ((TEGRA_MC_FPRI_CTRL_G2 << 8) | 2)
+#define TEGRA_MC_CLIENT_G2DR ((TEGRA_MC_FPRI_CTRL_G2 << 8) | 4)
+#define TEGRA_MC_CLIENT_G2DW ((TEGRA_MC_FPRI_CTRL_G2 << 8) | 6)
+#define TEGRA_MC_CLIENT_HOST1XDMAR ((TEGRA_MC_FPRI_CTRL_HC << 8) | 0)
+#define TEGRA_MC_CLIENT_HOST1XR ((TEGRA_MC_FPRI_CTRL_HC << 8) | 2)
+#define TEGRA_MC_CLIENT_HOST1XW ((TEGRA_MC_FPRI_CTRL_HC << 8) | 4)
+#define TEGRA_MC_CLIENT_ISPW ((TEGRA_MC_FPRI_CTRL_ISP << 8) | 0)
+#define TEGRA_MC_CLIENT_MPCORER ((TEGRA_MC_FPRI_CTRL_MPCORE << 8) | 0)
+#define TEGRA_MC_CLIENT_MPCOREW ((TEGRA_MC_FPRI_CTRL_MPCORE << 8) | 2)
+#define TEGRA_MC_CLIENT_MPEAMEMRD ((TEGRA_MC_FPRI_CTRL_MPEA << 8) | 0)
+#define TEGRA_MC_CLIENT_MPEUNIFBR ((TEGRA_MC_FPRI_CTRL_MPEB << 8) | 0)
+#define TEGRA_MC_CLIENT_MPE_IPRED ((TEGRA_MC_FPRI_CTRL_MPEB << 8) | 2)
+#define TEGRA_MC_CLIENT_MPEUNIFBW ((TEGRA_MC_FPRI_CTRL_MPEB << 8) | 4)
+#define TEGRA_MC_CLIENT_MPECSRD ((TEGRA_MC_FPRI_CTRL_MPEC << 8) | 0)
+#define TEGRA_MC_CLIENT_MPECSWR ((TEGRA_MC_FPRI_CTRL_MPEC << 8) | 2)
+#define TEGRA_MC_CLIENT_FDCDRD ((TEGRA_MC_FPRI_CTRL_NV << 8) | 0)
+#define TEGRA_MC_CLIENT_IDXSRD ((TEGRA_MC_FPRI_CTRL_NV << 8) | 2)
+#define TEGRA_MC_CLIENT_TEXSRD ((TEGRA_MC_FPRI_CTRL_NV << 8) | 4)
+#define TEGRA_MC_CLIENT_FDCDWR ((TEGRA_MC_FPRI_CTRL_NV << 8) | 6)
+#define TEGRA_MC_CLIENT_PPCSAHBDMAR ((TEGRA_MC_FPRI_CTRL_PPCS << 8) | 0)
+#define TEGRA_MC_CLIENT_PPCSAHBSLVR ((TEGRA_MC_FPRI_CTRL_PPCS << 8) | 2)
+#define TEGRA_MC_CLIENT_PPCSAHBDMAW ((TEGRA_MC_FPRI_CTRL_PPCS << 8) | 4)
+#define TEGRA_MC_CLIENT_PPCSAHBSLVW ((TEGRA_MC_FPRI_CTRL_PPCS << 8) | 6)
+#define TEGRA_MC_CLIENT_VDEBSEVR ((TEGRA_MC_FPRI_CTRL_VDE << 8) | 0)
+#define TEGRA_MC_CLIENT_VDEMBER ((TEGRA_MC_FPRI_CTRL_VDE << 8) | 2)
+#define TEGRA_MC_CLIENT_VDEMCER ((TEGRA_MC_FPRI_CTRL_VDE << 8) | 4)
+#define TEGRA_MC_CLIENT_VDETPER ((TEGRA_MC_FPRI_CTRL_VDE << 8) | 6)
+#define TEGRA_MC_CLIENT_VDEBSEVW ((TEGRA_MC_FPRI_CTRL_VDE << 8) | 8)
+#define TEGRA_MC_CLIENT_VDEMBEW ((TEGRA_MC_FPRI_CTRL_VDE << 8) | 10)
+#define TEGRA_MC_CLIENT_VDETPMW ((TEGRA_MC_FPRI_CTRL_VDE << 8) | 12)
+#define TEGRA_MC_CLIENT_VIRUV ((TEGRA_MC_FPRI_CTRL_VI << 8) | 0)
+#define TEGRA_MC_CLIENT_VIWSB ((TEGRA_MC_FPRI_CTRL_VI << 8) | 2)
+#define TEGRA_MC_CLIENT_VIWU ((TEGRA_MC_FPRI_CTRL_VI << 8) | 4)
+#define TEGRA_MC_CLIENT_VIWV ((TEGRA_MC_FPRI_CTRL_VI << 8) | 6)
+#define TEGRA_MC_CLIENT_VIWY ((TEGRA_MC_FPRI_CTRL_VI << 8) | 8)
+
+#define TEGRA_MC_PRIO_LOWEST 0
+#define TEGRA_MC_PRIO_LOW 1
+#define TEGRA_MC_PRIO_MED 2
+#define TEGRA_MC_PRIO_HIGH 3
+#define TEGRA_MC_PRIO_MASK 3
+
+void tegra_mc_set_priority(unsigned long client, unsigned long prio);
+
+#else
+ /* !!!FIXME!!! IMPLEMENT ME */
+#define tegra_mc_set_priority(client, prio) \
+ do { /* nothing for now */ } while (0)
+#endif
+
+int tegra_mc_get_tiled_memory_bandwidth_multiplier(void);
+
+#endif
diff --git a/arch/arm/mach-tegra/include/mach/memory.h b/arch/arm/mach-tegra/include/mach/memory.h
index 537db3aa81a7..fb7a1f2877a4 100644
--- a/arch/arm/mach-tegra/include/mach/memory.h
+++ b/arch/arm/mach-tegra/include/mach/memory.h
@@ -2,6 +2,7 @@
* arch/arm/mach-tegra/include/mach/memory.h
*
* Copyright (C) 2010 Google, Inc.
+ * Copyright (C) 2011 NVIDIA Corporation.
*
* Author:
* Colin Cross <ccross@google.com>
@@ -22,7 +23,21 @@
#define __MACH_TEGRA_MEMORY_H
/* physical offset of RAM */
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
#define PLAT_PHYS_OFFSET UL(0)
+#else
+#define PLAT_PHYS_OFFSET UL(0x80000000)
+#endif
+
+/*
+ * Unaligned DMA causes tegra dma to place data on 4-byte boundary after
+ * expected address. Call to skb_reserve(skb, NET_IP_ALIGN) was causing skb
+ * buffers in usbnet.c to become unaligned.
+ */
+#define NET_IP_ALIGN 0
+#define NET_SKB_PAD L1_CACHE_BYTES
+
+#define CONSISTENT_DMA_SIZE (14 * SZ_1M)
#endif
diff --git a/arch/arm/mach-tegra/include/mach/nand.h b/arch/arm/mach-tegra/include/mach/nand.h
new file mode 100644
index 000000000000..91ad7d1c9ae5
--- /dev/null
+++ b/arch/arm/mach-tegra/include/mach/nand.h
@@ -0,0 +1,55 @@
+/*
+ * arch/arm/mach-tegra/include/mach/nand.h
+ *
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * Author:
+ * Colin Cross <ccross@google.com>
+ * Dima Zavin <dmitriyz@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.
+ *
+ */
+
+#ifndef __MACH_TEGRA_NAND_H
+#define __MACH_TEGRA_NAND_H
+
+struct tegra_nand_chip_parms {
+ uint8_t vendor_id;
+ uint8_t device_id;
+ uint32_t flags;
+ uint8_t read_id_fourth_byte;
+ uint32_t capacity;
+
+ /* all timing info is in nanoseconds */
+ struct {
+ uint32_t trp;
+ uint32_t trh;
+ uint32_t twp;
+ uint32_t twh;
+ uint32_t tcs;
+ uint32_t twhr;
+ uint32_t tcr_tar_trr;
+ uint32_t twb;
+ uint32_t trp_resp;
+ uint32_t tadl;
+ } timing;
+};
+
+struct tegra_nand_platform {
+ uint8_t max_chips;
+ struct tegra_nand_chip_parms *chip_parms;
+ unsigned int nr_chip_parms;
+ struct mtd_partition *parts;
+ unsigned int nr_parts;
+ int wp_gpio;
+};
+
+#endif
diff --git a/arch/arm/mach-tegra/include/mach/nvmap.h b/arch/arm/mach-tegra/include/mach/nvmap.h
new file mode 100644
index 000000000000..cb5375226bc8
--- /dev/null
+++ b/arch/arm/mach-tegra/include/mach/nvmap.h
@@ -0,0 +1,134 @@
+/*
+ * arch/arm/mach-tegra/include/mach/nvmap.h
+ *
+ * structure declarations for nvmem and nvmap user-space ioctls
+ *
+ * Copyright (c) 2009-2011, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/ioctl.h>
+#include <linux/file.h>
+#include <linux/rbtree.h>
+
+#if !defined(__KERNEL__)
+#define __user
+#endif
+
+#ifndef __NVMAP_H
+#define __NVMAP_H
+
+#define NVMAP_HEAP_SYSMEM (1ul<<31)
+#define NVMAP_HEAP_IOVMM (1ul<<30)
+
+/* common carveout heaps */
+#define NVMAP_HEAP_CARVEOUT_IRAM (1ul<<29)
+#define NVMAP_HEAP_CARVEOUT_VPR (1ul<<28)
+#define NVMAP_HEAP_CARVEOUT_GENERIC (1ul<<0)
+
+#define NVMAP_HEAP_CARVEOUT_MASK (NVMAP_HEAP_IOVMM - 1)
+
+/* allocation flags */
+#define NVMAP_HANDLE_UNCACHEABLE (0x0ul << 0)
+#define NVMAP_HANDLE_WRITE_COMBINE (0x1ul << 0)
+#define NVMAP_HANDLE_INNER_CACHEABLE (0x2ul << 0)
+#define NVMAP_HANDLE_CACHEABLE (0x3ul << 0)
+#define NVMAP_HANDLE_CACHE_FLAG (0x3ul << 0)
+
+#define NVMAP_HANDLE_SECURE (0x1ul << 2)
+
+
+#if defined(__KERNEL__)
+
+struct nvmap_handle;
+struct nvmap_client;
+struct nvmap_device;
+
+#define nvmap_ref_to_handle(_ref) (*(struct nvmap_handle **)(_ref))
+#define nvmap_id_to_handle(_id) ((struct nvmap_handle *)(_id))
+
+struct nvmap_pinarray_elem {
+ __u32 patch_mem;
+ __u32 patch_offset;
+ __u32 pin_mem;
+ __u32 pin_offset;
+ __u32 reloc_shift;
+};
+
+/* handle_ref objects are client-local references to an nvmap_handle;
+ * they are distinct objects so that handles can be unpinned and
+ * unreferenced the correct number of times when a client abnormally
+ * terminates */
+struct nvmap_handle_ref {
+ struct nvmap_handle *handle;
+ struct rb_node node;
+ atomic_t dupes; /* number of times to free on file close */
+ atomic_t pin; /* number of times to unpin on free */
+};
+
+struct nvmap_client *nvmap_create_client(struct nvmap_device *dev,
+ const char *name);
+
+struct nvmap_handle_ref *nvmap_alloc(struct nvmap_client *client, size_t size,
+ size_t align, unsigned int flags);
+
+void nvmap_free(struct nvmap_client *client, struct nvmap_handle_ref *r);
+
+void *nvmap_mmap(struct nvmap_handle_ref *r);
+
+void nvmap_munmap(struct nvmap_handle_ref *r, void *addr);
+
+struct nvmap_client *nvmap_client_get_file(int fd);
+
+struct nvmap_client *nvmap_client_get(struct nvmap_client *client);
+
+void nvmap_client_put(struct nvmap_client *c);
+
+phys_addr_t nvmap_pin(struct nvmap_client *c, struct nvmap_handle_ref *r);
+
+phys_addr_t nvmap_handle_address(struct nvmap_client *c, unsigned long id);
+
+void nvmap_unpin(struct nvmap_client *client, struct nvmap_handle_ref *r);
+
+int nvmap_pin_array(struct nvmap_client *client, struct nvmap_handle *gather,
+ const struct nvmap_pinarray_elem *arr, int nr,
+ struct nvmap_handle **unique);
+
+void nvmap_unpin_handles(struct nvmap_client *client,
+ struct nvmap_handle **h, int nr);
+
+int nvmap_patch_word(struct nvmap_client *client,
+ struct nvmap_handle *patch,
+ u32 patch_offset, u32 patch_value);
+
+struct nvmap_platform_carveout {
+ const char *name;
+ unsigned int usage_mask;
+ phys_addr_t base;
+ size_t size;
+ size_t buddy_size;
+};
+
+struct nvmap_platform_data {
+ const struct nvmap_platform_carveout *carveouts;
+ unsigned int nr_carveouts;
+};
+
+extern struct nvmap_device *nvmap_dev;
+
+#endif
+
+#endif
diff --git a/arch/arm/mach-tegra/include/mach/pinmux-t3.h b/arch/arm/mach-tegra/include/mach/pinmux-t3.h
new file mode 100644
index 000000000000..12fd5e8ffc1d
--- /dev/null
+++ b/arch/arm/mach-tegra/include/mach/pinmux-t3.h
@@ -0,0 +1,321 @@
+/*
+ * linux/arch/arm/mach-tegra/include/mach/pinmux-t3.h
+ *
+ * Copyright (C) 2010 Google, Inc.
+ * Copyright (C) 2011 NVIDIA Corporation.
+ *
+ * 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 __MACH_TEGRA_PINMUX_T3_H
+#define __MACH_TEGRA_PINMUX_T3_H
+
+#define TEGRA_PINMUX_HAS_IO_DIRECTION 1
+
+enum tegra_pingroup {
+ TEGRA_PINGROUP_ULPI_DATA0 = 0,
+ TEGRA_PINGROUP_ULPI_DATA1,
+ TEGRA_PINGROUP_ULPI_DATA2,
+ TEGRA_PINGROUP_ULPI_DATA3,
+ TEGRA_PINGROUP_ULPI_DATA4,
+ TEGRA_PINGROUP_ULPI_DATA5,
+ TEGRA_PINGROUP_ULPI_DATA6,
+ TEGRA_PINGROUP_ULPI_DATA7,
+ TEGRA_PINGROUP_ULPI_CLK,
+ TEGRA_PINGROUP_ULPI_DIR,
+ TEGRA_PINGROUP_ULPI_NXT,
+ TEGRA_PINGROUP_ULPI_STP,
+ TEGRA_PINGROUP_DAP3_FS,
+ TEGRA_PINGROUP_DAP3_DIN,
+ TEGRA_PINGROUP_DAP3_DOUT,
+ TEGRA_PINGROUP_DAP3_SCLK,
+ TEGRA_PINGROUP_GPIO_PV0,
+ TEGRA_PINGROUP_GPIO_PV1,
+ TEGRA_PINGROUP_SDMMC1_CLK,
+ TEGRA_PINGROUP_SDMMC1_CMD,
+ TEGRA_PINGROUP_SDMMC1_DAT3,
+ TEGRA_PINGROUP_SDMMC1_DAT2,
+ TEGRA_PINGROUP_SDMMC1_DAT1,
+ TEGRA_PINGROUP_SDMMC1_DAT0,
+ TEGRA_PINGROUP_GPIO_PV2,
+ TEGRA_PINGROUP_GPIO_PV3,
+ TEGRA_PINGROUP_CLK2_OUT,
+ TEGRA_PINGROUP_CLK2_REQ,
+ TEGRA_PINGROUP_LCD_PWR1,
+ TEGRA_PINGROUP_LCD_PWR2,
+ TEGRA_PINGROUP_LCD_SDIN,
+ TEGRA_PINGROUP_LCD_SDOUT,
+ TEGRA_PINGROUP_LCD_WR_N,
+ TEGRA_PINGROUP_LCD_CS0_N,
+ TEGRA_PINGROUP_LCD_DC0,
+ TEGRA_PINGROUP_LCD_SCK,
+ TEGRA_PINGROUP_LCD_PWR0,
+ TEGRA_PINGROUP_LCD_PCLK,
+ TEGRA_PINGROUP_LCD_DE,
+ TEGRA_PINGROUP_LCD_HSYNC,
+ TEGRA_PINGROUP_LCD_VSYNC,
+ TEGRA_PINGROUP_LCD_D0,
+ TEGRA_PINGROUP_LCD_D1,
+ TEGRA_PINGROUP_LCD_D2,
+ TEGRA_PINGROUP_LCD_D3,
+ TEGRA_PINGROUP_LCD_D4,
+ TEGRA_PINGROUP_LCD_D5,
+ TEGRA_PINGROUP_LCD_D6,
+ TEGRA_PINGROUP_LCD_D7,
+ TEGRA_PINGROUP_LCD_D8,
+ TEGRA_PINGROUP_LCD_D9,
+ TEGRA_PINGROUP_LCD_D10,
+ TEGRA_PINGROUP_LCD_D11,
+ TEGRA_PINGROUP_LCD_D12,
+ TEGRA_PINGROUP_LCD_D13,
+ TEGRA_PINGROUP_LCD_D14,
+ TEGRA_PINGROUP_LCD_D15,
+ TEGRA_PINGROUP_LCD_D16,
+ TEGRA_PINGROUP_LCD_D17,
+ TEGRA_PINGROUP_LCD_D18,
+ TEGRA_PINGROUP_LCD_D19,
+ TEGRA_PINGROUP_LCD_D20,
+ TEGRA_PINGROUP_LCD_D21,
+ TEGRA_PINGROUP_LCD_D22,
+ TEGRA_PINGROUP_LCD_D23,
+ TEGRA_PINGROUP_LCD_CS1_N,
+ TEGRA_PINGROUP_LCD_M1,
+ TEGRA_PINGROUP_LCD_DC1,
+ TEGRA_PINGROUP_HDMI_INT,
+ TEGRA_PINGROUP_DDC_SCL,
+ TEGRA_PINGROUP_DDC_SDA,
+ TEGRA_PINGROUP_CRT_HSYNC,
+ TEGRA_PINGROUP_CRT_VSYNC,
+ TEGRA_PINGROUP_VI_D0,
+ TEGRA_PINGROUP_VI_D1,
+ TEGRA_PINGROUP_VI_D2,
+ TEGRA_PINGROUP_VI_D3,
+ TEGRA_PINGROUP_VI_D4,
+ TEGRA_PINGROUP_VI_D5,
+ TEGRA_PINGROUP_VI_D6,
+ TEGRA_PINGROUP_VI_D7,
+ TEGRA_PINGROUP_VI_D8,
+ TEGRA_PINGROUP_VI_D9,
+ TEGRA_PINGROUP_VI_D10,
+ TEGRA_PINGROUP_VI_D11,
+ TEGRA_PINGROUP_VI_PCLK,
+ TEGRA_PINGROUP_VI_MCLK,
+ TEGRA_PINGROUP_VI_VSYNC,
+ TEGRA_PINGROUP_VI_HSYNC,
+ TEGRA_PINGROUP_UART2_RXD,
+ TEGRA_PINGROUP_UART2_TXD,
+ TEGRA_PINGROUP_UART2_RTS_N,
+ TEGRA_PINGROUP_UART2_CTS_N,
+ TEGRA_PINGROUP_UART3_TXD,
+ TEGRA_PINGROUP_UART3_RXD,
+ TEGRA_PINGROUP_UART3_CTS_N,
+ TEGRA_PINGROUP_UART3_RTS_N,
+ TEGRA_PINGROUP_GPIO_PU0,
+ TEGRA_PINGROUP_GPIO_PU1,
+ TEGRA_PINGROUP_GPIO_PU2,
+ TEGRA_PINGROUP_GPIO_PU3,
+ TEGRA_PINGROUP_GPIO_PU4,
+ TEGRA_PINGROUP_GPIO_PU5,
+ TEGRA_PINGROUP_GPIO_PU6,
+ TEGRA_PINGROUP_GEN1_I2C_SDA,
+ TEGRA_PINGROUP_GEN1_I2C_SCL,
+ TEGRA_PINGROUP_DAP4_FS,
+ TEGRA_PINGROUP_DAP4_DIN,
+ TEGRA_PINGROUP_DAP4_DOUT,
+ TEGRA_PINGROUP_DAP4_SCLK,
+ TEGRA_PINGROUP_CLK3_OUT,
+ TEGRA_PINGROUP_CLK3_REQ,
+ TEGRA_PINGROUP_GMI_WP_N,
+ TEGRA_PINGROUP_GMI_IORDY,
+ TEGRA_PINGROUP_GMI_WAIT,
+ TEGRA_PINGROUP_GMI_ADV_N,
+ TEGRA_PINGROUP_GMI_CLK,
+ TEGRA_PINGROUP_GMI_CS0_N,
+ TEGRA_PINGROUP_GMI_CS1_N,
+ TEGRA_PINGROUP_GMI_CS2_N,
+ TEGRA_PINGROUP_GMI_CS3_N,
+ TEGRA_PINGROUP_GMI_CS4_N,
+ TEGRA_PINGROUP_GMI_CS6_N,
+ TEGRA_PINGROUP_GMI_CS7_N,
+ TEGRA_PINGROUP_GMI_AD0,
+ TEGRA_PINGROUP_GMI_AD1,
+ TEGRA_PINGROUP_GMI_AD2,
+ TEGRA_PINGROUP_GMI_AD3,
+ TEGRA_PINGROUP_GMI_AD4,
+ TEGRA_PINGROUP_GMI_AD5,
+ TEGRA_PINGROUP_GMI_AD6,
+ TEGRA_PINGROUP_GMI_AD7,
+ TEGRA_PINGROUP_GMI_AD8,
+ TEGRA_PINGROUP_GMI_AD9,
+ TEGRA_PINGROUP_GMI_AD10,
+ TEGRA_PINGROUP_GMI_AD11,
+ TEGRA_PINGROUP_GMI_AD12,
+ TEGRA_PINGROUP_GMI_AD13,
+ TEGRA_PINGROUP_GMI_AD14,
+ TEGRA_PINGROUP_GMI_AD15,
+ TEGRA_PINGROUP_GMI_A16,
+ TEGRA_PINGROUP_GMI_A17,
+ TEGRA_PINGROUP_GMI_A18,
+ TEGRA_PINGROUP_GMI_A19,
+ TEGRA_PINGROUP_GMI_WR_N,
+ TEGRA_PINGROUP_GMI_OE_N,
+ TEGRA_PINGROUP_GMI_DQS,
+ TEGRA_PINGROUP_GMI_RST_N,
+ TEGRA_PINGROUP_GEN2_I2C_SCL,
+ TEGRA_PINGROUP_GEN2_I2C_SDA,
+ TEGRA_PINGROUP_SDMMC4_CLK,
+ TEGRA_PINGROUP_SDMMC4_CMD,
+ TEGRA_PINGROUP_SDMMC4_DAT0,
+ TEGRA_PINGROUP_SDMMC4_DAT1,
+ TEGRA_PINGROUP_SDMMC4_DAT2,
+ TEGRA_PINGROUP_SDMMC4_DAT3,
+ TEGRA_PINGROUP_SDMMC4_DAT4,
+ TEGRA_PINGROUP_SDMMC4_DAT5,
+ TEGRA_PINGROUP_SDMMC4_DAT6,
+ TEGRA_PINGROUP_SDMMC4_DAT7,
+ TEGRA_PINGROUP_SDMMC4_RST_N,
+ TEGRA_PINGROUP_CAM_MCLK,
+ TEGRA_PINGROUP_GPIO_PCC1,
+ TEGRA_PINGROUP_GPIO_PBB0,
+ TEGRA_PINGROUP_CAM_I2C_SCL,
+ TEGRA_PINGROUP_CAM_I2C_SDA,
+ TEGRA_PINGROUP_GPIO_PBB3,
+ TEGRA_PINGROUP_GPIO_PBB4,
+ TEGRA_PINGROUP_GPIO_PBB5,
+ TEGRA_PINGROUP_GPIO_PBB6,
+ TEGRA_PINGROUP_GPIO_PBB7,
+ TEGRA_PINGROUP_GPIO_PCC2,
+ TEGRA_PINGROUP_JTAG_RTCK,
+ TEGRA_PINGROUP_PWR_I2C_SCL,
+ TEGRA_PINGROUP_PWR_I2C_SDA,
+ TEGRA_PINGROUP_KB_ROW0,
+ TEGRA_PINGROUP_KB_ROW1,
+ TEGRA_PINGROUP_KB_ROW2,
+ TEGRA_PINGROUP_KB_ROW3,
+ TEGRA_PINGROUP_KB_ROW4,
+ TEGRA_PINGROUP_KB_ROW5,
+ TEGRA_PINGROUP_KB_ROW6,
+ TEGRA_PINGROUP_KB_ROW7,
+ TEGRA_PINGROUP_KB_ROW8,
+ TEGRA_PINGROUP_KB_ROW9,
+ TEGRA_PINGROUP_KB_ROW10,
+ TEGRA_PINGROUP_KB_ROW11,
+ TEGRA_PINGROUP_KB_ROW12,
+ TEGRA_PINGROUP_KB_ROW13,
+ TEGRA_PINGROUP_KB_ROW14,
+ TEGRA_PINGROUP_KB_ROW15,
+ TEGRA_PINGROUP_KB_COL0,
+ TEGRA_PINGROUP_KB_COL1,
+ TEGRA_PINGROUP_KB_COL2,
+ TEGRA_PINGROUP_KB_COL3,
+ TEGRA_PINGROUP_KB_COL4,
+ TEGRA_PINGROUP_KB_COL5,
+ TEGRA_PINGROUP_KB_COL6,
+ TEGRA_PINGROUP_KB_COL7,
+ TEGRA_PINGROUP_CLK_32K_OUT,
+ TEGRA_PINGROUP_SYS_CLK_REQ,
+ TEGRA_PINGROUP_CORE_PWR_REQ,
+ TEGRA_PINGROUP_CPU_PWR_REQ,
+ TEGRA_PINGROUP_PWR_INT_N,
+ TEGRA_PINGROUP_CLK_32K_IN,
+ TEGRA_PINGROUP_OWR,
+ TEGRA_PINGROUP_DAP1_FS,
+ TEGRA_PINGROUP_DAP1_DIN,
+ TEGRA_PINGROUP_DAP1_DOUT,
+ TEGRA_PINGROUP_DAP1_SCLK,
+ TEGRA_PINGROUP_CLK1_REQ,
+ TEGRA_PINGROUP_CLK1_OUT,
+ TEGRA_PINGROUP_SPDIF_IN,
+ TEGRA_PINGROUP_SPDIF_OUT,
+ TEGRA_PINGROUP_DAP2_FS,
+ TEGRA_PINGROUP_DAP2_DIN,
+ TEGRA_PINGROUP_DAP2_DOUT,
+ TEGRA_PINGROUP_DAP2_SCLK,
+ TEGRA_PINGROUP_SPI2_MOSI,
+ TEGRA_PINGROUP_SPI2_MISO,
+ TEGRA_PINGROUP_SPI2_CS0_N,
+ TEGRA_PINGROUP_SPI2_SCK,
+ TEGRA_PINGROUP_SPI1_MOSI,
+ TEGRA_PINGROUP_SPI1_SCK,
+ TEGRA_PINGROUP_SPI1_CS0_N,
+ TEGRA_PINGROUP_SPI1_MISO,
+ TEGRA_PINGROUP_SPI2_CS1_N,
+ TEGRA_PINGROUP_SPI2_CS2_N,
+ TEGRA_PINGROUP_SDMMC3_CLK,
+ TEGRA_PINGROUP_SDMMC3_CMD,
+ TEGRA_PINGROUP_SDMMC3_DAT0,
+ TEGRA_PINGROUP_SDMMC3_DAT1,
+ TEGRA_PINGROUP_SDMMC3_DAT2,
+ TEGRA_PINGROUP_SDMMC3_DAT3,
+ TEGRA_PINGROUP_SDMMC3_DAT4,
+ TEGRA_PINGROUP_SDMMC3_DAT5,
+ TEGRA_PINGROUP_SDMMC3_DAT6,
+ TEGRA_PINGROUP_SDMMC3_DAT7,
+ TEGRA_PINGROUP_PEX_L0_PRSNT_N,
+ TEGRA_PINGROUP_PEX_L0_RST_N,
+ TEGRA_PINGROUP_PEX_L0_CLKREQ_N,
+ TEGRA_PINGROUP_PEX_WAKE_N,
+ TEGRA_PINGROUP_PEX_L1_PRSNT_N,
+ TEGRA_PINGROUP_PEX_L1_RST_N,
+ TEGRA_PINGROUP_PEX_L1_CLKREQ_N,
+ TEGRA_PINGROUP_PEX_L2_PRSNT_N,
+ TEGRA_PINGROUP_PEX_L2_RST_N,
+ TEGRA_PINGROUP_PEX_L2_CLKREQ_N,
+ TEGRA_PINGROUP_HDMI_CEC,
+ TEGRA_MAX_PINGROUP,
+};
+
+enum tegra_drive_pingroup {
+ TEGRA_DRIVE_PINGROUP_AO1 = 0,
+ TEGRA_DRIVE_PINGROUP_AO2,
+ TEGRA_DRIVE_PINGROUP_AT1,
+ TEGRA_DRIVE_PINGROUP_AT2,
+ TEGRA_DRIVE_PINGROUP_AT3,
+ TEGRA_DRIVE_PINGROUP_AT4,
+ TEGRA_DRIVE_PINGROUP_AT5,
+ TEGRA_DRIVE_PINGROUP_CDEV1,
+ TEGRA_DRIVE_PINGROUP_CDEV2,
+ TEGRA_DRIVE_PINGROUP_CSUS,
+ TEGRA_DRIVE_PINGROUP_DAP1,
+ TEGRA_DRIVE_PINGROUP_DAP2,
+ TEGRA_DRIVE_PINGROUP_DAP3,
+ TEGRA_DRIVE_PINGROUP_DAP4,
+ TEGRA_DRIVE_PINGROUP_DBG,
+ TEGRA_DRIVE_PINGROUP_LCD1,
+ TEGRA_DRIVE_PINGROUP_LCD2,
+ TEGRA_DRIVE_PINGROUP_SDIO2,
+ TEGRA_DRIVE_PINGROUP_SDIO3,
+ TEGRA_DRIVE_PINGROUP_SPI,
+ TEGRA_DRIVE_PINGROUP_UAA,
+ TEGRA_DRIVE_PINGROUP_UAB,
+ TEGRA_DRIVE_PINGROUP_UART2,
+ TEGRA_DRIVE_PINGROUP_UART3,
+ TEGRA_DRIVE_PINGROUP_VI1,
+ TEGRA_DRIVE_PINGROUP_SDIO1,
+ TEGRA_DRIVE_PINGROUP_CRT,
+ TEGRA_DRIVE_PINGROUP_DDC,
+ TEGRA_DRIVE_PINGROUP_GMA,
+ TEGRA_DRIVE_PINGROUP_GMB,
+ TEGRA_DRIVE_PINGROUP_GMC,
+ TEGRA_DRIVE_PINGROUP_GMD,
+ TEGRA_DRIVE_PINGROUP_GME,
+ TEGRA_DRIVE_PINGROUP_GMF,
+ TEGRA_DRIVE_PINGROUP_GMG,
+ TEGRA_DRIVE_PINGROUP_GMH,
+ TEGRA_DRIVE_PINGROUP_OWR,
+ TEGRA_DRIVE_PINGROUP_UAD,
+ TEGRA_DRIVE_PINGROUP_GPV,
+ TEGRA_DRIVE_PINGROUP_DEV3,
+ TEGRA_DRIVE_PINGROUP_CEC,
+ TEGRA_MAX_DRIVE_PINGROUP,
+};
+
+#endif
diff --git a/arch/arm/mach-tegra/include/mach/pinmux.h b/arch/arm/mach-tegra/include/mach/pinmux.h
index defd8775defa..6a2c18c52faa 100644
--- a/arch/arm/mach-tegra/include/mach/pinmux.h
+++ b/arch/arm/mach-tegra/include/mach/pinmux.h
@@ -2,6 +2,7 @@
* linux/arch/arm/mach-tegra/include/mach/pinmux.h
*
* Copyright (C) 2010 Google, Inc.
+ * Copyright (C) 2011 NVIDIA Corporation.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@@ -19,17 +20,21 @@
#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
#include "pinmux-t2.h"
+#elif defined(CONFIG_ARCH_TEGRA_3x_SOC)
+#include "pinmux-t3.h"
#else
#error "Undefined Tegra architecture"
#endif
enum tegra_mux_func {
TEGRA_MUX_RSVD = 0x8000,
+ TEGRA_MUX_RSVD0 = TEGRA_MUX_RSVD,
TEGRA_MUX_RSVD1 = 0x8000,
TEGRA_MUX_RSVD2 = 0x8001,
TEGRA_MUX_RSVD3 = 0x8002,
TEGRA_MUX_RSVD4 = 0x8003,
- TEGRA_MUX_NONE = -1,
+ TEGRA_MUX_INVALID = 0x4000,
+ TEGRA_MUX_NONE = 0,
TEGRA_MUX_AHB_CLK,
TEGRA_MUX_APB_CLK,
TEGRA_MUX_AUDIO_SYNC,
@@ -47,6 +52,7 @@ enum tegra_mux_func {
TEGRA_MUX_GMI_INT,
TEGRA_MUX_HDMI,
TEGRA_MUX_I2C,
+ TEGRA_MUX_I2C1 = TEGRA_MUX_I2C,
TEGRA_MUX_I2C2,
TEGRA_MUX_I2C3,
TEGRA_MUX_IDE,
@@ -69,9 +75,13 @@ enum tegra_mux_func {
TEGRA_MUX_PWR_ON,
TEGRA_MUX_RTCK,
TEGRA_MUX_SDIO1,
+ TEGRA_MUX_SDMMC1 = TEGRA_MUX_SDIO1,
TEGRA_MUX_SDIO2,
+ TEGRA_MUX_SDMMC2 = TEGRA_MUX_SDIO2,
TEGRA_MUX_SDIO3,
+ TEGRA_MUX_SDMMC3 = TEGRA_MUX_SDIO3,
TEGRA_MUX_SDIO4,
+ TEGRA_MUX_SDMMC4 = TEGRA_MUX_SDIO4,
TEGRA_MUX_SFLASH,
TEGRA_MUX_SPDIF,
TEGRA_MUX_SPI1,
@@ -90,6 +100,51 @@ enum tegra_mux_func {
TEGRA_MUX_VI,
TEGRA_MUX_VI_SENSOR_CLK,
TEGRA_MUX_XIO,
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+ TEGRA_MUX_BLINK,
+ TEGRA_MUX_CEC,
+ TEGRA_MUX_CLK12,
+ TEGRA_MUX_DAP,
+ TEGRA_MUX_DAPSDMMC2,
+ TEGRA_MUX_DDR,
+ TEGRA_MUX_DEV3,
+ TEGRA_MUX_DTV,
+ TEGRA_MUX_VI_ALT1,
+ TEGRA_MUX_VI_ALT2,
+ TEGRA_MUX_VI_ALT3,
+ TEGRA_MUX_EMC_DLL,
+ TEGRA_MUX_EXTPERIPH1,
+ TEGRA_MUX_EXTPERIPH2,
+ TEGRA_MUX_EXTPERIPH3,
+ TEGRA_MUX_GMI_ALT,
+ TEGRA_MUX_HDA,
+ TEGRA_MUX_HSI,
+ TEGRA_MUX_I2C4,
+ TEGRA_MUX_I2C5,
+ TEGRA_MUX_I2CPWR,
+ TEGRA_MUX_I2S0,
+ TEGRA_MUX_I2S1,
+ TEGRA_MUX_I2S2,
+ TEGRA_MUX_I2S3,
+ TEGRA_MUX_I2S4,
+ TEGRA_MUX_NAND_ALT,
+ TEGRA_MUX_POPSDIO4,
+ TEGRA_MUX_POPSDMMC4,
+ TEGRA_MUX_PWM0,
+ TEGRA_MUX_PWM1,
+ TEGRA_MUX_PWM2,
+ TEGRA_MUX_PWM3,
+ TEGRA_MUX_SATA,
+ TEGRA_MUX_SPI5,
+ TEGRA_MUX_SPI6,
+ TEGRA_MUX_SYSCLK,
+ TEGRA_MUX_VGP1,
+ TEGRA_MUX_VGP2,
+ TEGRA_MUX_VGP3,
+ TEGRA_MUX_VGP4,
+ TEGRA_MUX_VGP5,
+ TEGRA_MUX_VGP6,
+#endif
TEGRA_MUX_SAFE,
TEGRA_MAX_MUX,
};
@@ -105,6 +160,29 @@ enum tegra_tristate {
TEGRA_TRI_TRISTATE = 1,
};
+enum tegra_pin_io {
+ TEGRA_PIN_OUTPUT = 0,
+ TEGRA_PIN_INPUT = 1,
+};
+
+enum tegra_pin_lock {
+ TEGRA_PIN_LOCK_DEFAULT = 0,
+ TEGRA_PIN_LOCK_DISABLE,
+ TEGRA_PIN_LOCK_ENABLE,
+};
+
+enum tegra_pin_od {
+ TEGRA_PIN_OD_DEFAULT = 0,
+ TEGRA_PIN_OD_DISABLE,
+ TEGRA_PIN_OD_ENABLE,
+};
+
+enum tegra_pin_ioreset {
+ TEGRA_PIN_IO_RESET_DEFAULT = 0,
+ TEGRA_PIN_IO_RESET_DISABLE,
+ TEGRA_PIN_IO_RESET_ENABLE,
+};
+
enum tegra_vddio {
TEGRA_VDDIO_BB = 0,
TEGRA_VDDIO_LCD,
@@ -115,6 +193,14 @@ enum tegra_vddio {
TEGRA_VDDIO_SYS,
TEGRA_VDDIO_AUDIO,
TEGRA_VDDIO_SD,
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+ TEGRA_VDDIO_CAM,
+ TEGRA_VDDIO_GMI,
+ TEGRA_VDDIO_PEXCTL,
+ TEGRA_VDDIO_SDMMC1,
+ TEGRA_VDDIO_SDMMC3,
+ TEGRA_VDDIO_SDMMC4,
+#endif
};
struct tegra_pingroup_config {
@@ -122,6 +208,10 @@ struct tegra_pingroup_config {
enum tegra_mux_func func;
enum tegra_pullupdown pupd;
enum tegra_tristate tristate;
+ enum tegra_pin_io io;
+ enum tegra_pin_lock lock;
+ enum tegra_pin_od od;
+ enum tegra_pin_ioreset ioreset;
};
enum tegra_slew {
@@ -165,6 +255,21 @@ enum tegra_pull_strength {
TEGRA_PULL_29,
TEGRA_PULL_30,
TEGRA_PULL_31,
+ TEGRA_PULL_32,
+ TEGRA_PULL_33,
+ TEGRA_PULL_34,
+ TEGRA_PULL_35,
+ TEGRA_PULL_36,
+ TEGRA_PULL_37,
+ TEGRA_PULL_38,
+ TEGRA_PULL_39,
+ TEGRA_PULL_40,
+ TEGRA_PULL_41,
+ TEGRA_PULL_42,
+ TEGRA_PULL_43,
+ TEGRA_PULL_44,
+ TEGRA_PULL_45,
+ TEGRA_PULL_46,
TEGRA_MAX_PULL,
};
@@ -200,6 +305,14 @@ struct tegra_drive_pingroup_config {
struct tegra_drive_pingroup_desc {
const char *name;
s16 reg;
+ u8 drvup_offset;
+ u16 drvup_mask;
+ u8 drvdown_offset;
+ u16 drvdown_mask;
+ u8 slewrise_offset;
+ u16 slewrise_mask;
+ u8 slewfall_offset;
+ u16 slewfall_mask;
};
struct tegra_pingroup_desc {
@@ -207,19 +320,27 @@ struct tegra_pingroup_desc {
int funcs[4];
int func_safe;
int vddio;
- s16 tri_reg; /* offset into the TRISTATE_REG_* register bank */
+ s16 tri_reg; /* offset into the TRISTATE_REG_* register bank */
s16 mux_reg; /* offset into the PIN_MUX_CTL_* register bank */
s16 pupd_reg; /* offset into the PULL_UPDOWN_REG_* register bank */
- s8 tri_bit; /* offset into the TRISTATE_REG_* register bit */
+ s8 tri_bit; /* offset into the TRISTATE_REG_* register bit */
s8 mux_bit; /* offset into the PIN_MUX_CTL_* register bit */
s8 pupd_bit; /* offset into the PULL_UPDOWN_REG_* register bit */
+ s8 lock_bit; /* offser of the LOCK bit into mux register bit */
+ s8 od_bit; /* offset of the OD bit into mux register bit */
+ s8 ioreset_bit; /* offset of the IO_RESET bit into mux register bit */
+ s8 io_default;
+ int gpionr;
};
extern const struct tegra_pingroup_desc tegra_soc_pingroups[];
extern const struct tegra_drive_pingroup_desc tegra_soc_drive_pingroups[];
+extern const int gpio_to_pingroup[];
+int tegra_pinmux_get_func(enum tegra_pingroup pg);
int tegra_pinmux_set_tristate(enum tegra_pingroup pg,
enum tegra_tristate tristate);
+int tegra_pinmux_get_pingroup(int gpio_nr);
int tegra_pinmux_set_pullupdown(enum tegra_pingroup pg,
enum tegra_pullupdown pupd);
@@ -236,5 +357,6 @@ void tegra_pinmux_config_tristate_table(const struct tegra_pingroup_config *conf
int len, enum tegra_tristate tristate);
void tegra_pinmux_config_pullupdown_table(const struct tegra_pingroup_config *config,
int len, enum tegra_pullupdown pupd);
-#endif
+void __init tegra_init_pinmux(void);
+#endif
diff --git a/arch/arm/mach-tegra/include/mach/powergate.h b/arch/arm/mach-tegra/include/mach/powergate.h
index 401d1b725291..a8587ec2d360 100644
--- a/arch/arm/mach-tegra/include/mach/powergate.h
+++ b/arch/arm/mach-tegra/include/mach/powergate.h
@@ -2,6 +2,7 @@
* drivers/regulator/tegra-regulator.c
*
* Copyright (c) 2010 Google, Inc
+ * Copyright (C) 2011 NVIDIA Corporation.
*
* Author:
* Colin Cross <ccross@google.com>
@@ -21,20 +22,76 @@
#define _MACH_TEGRA_POWERGATE_H_
#define TEGRA_POWERGATE_CPU 0
+#define TEGRA_POWERGATE_CPU0 TEGRA_POWERGATE_CPU
#define TEGRA_POWERGATE_3D 1
+#define TEGRA_POWERGATE_3D0 TEGRA_POWERGATE_3D
#define TEGRA_POWERGATE_VENC 2
#define TEGRA_POWERGATE_PCIE 3
#define TEGRA_POWERGATE_VDEC 4
#define TEGRA_POWERGATE_L2 5
#define TEGRA_POWERGATE_MPE 6
+#define TEGRA_POWERGATE_HEG 7
+#define TEGRA_POWERGATE_SATA 8
+#define TEGRA_POWERGATE_CPU1 9
+#define TEGRA_POWERGATE_CPU2 10
+#define TEGRA_POWERGATE_CPU3 11
+#define TEGRA_POWERGATE_CELP 12
+#define TEGRA_POWERGATE_3D1 13
+
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
#define TEGRA_NUM_POWERGATE 7
+#define TEGRA_CPU_POWERGATE_ID(cpu) (TEGRA_POWERGATE_CPU)
+#define TEGRA_IS_CPU_POWERGATE_ID(id) ((id) == TEGRA_POWERGATE_CPU)
+#else
+#define TEGRA_NUM_POWERGATE 14
+#define TEGRA_CPU_POWERGATE_ID(cpu) ((cpu == 0) ? TEGRA_POWERGATE_CPU0 : \
+ (cpu + TEGRA_POWERGATE_CPU1 - 1))
+#define TEGRA_IS_CPU_POWERGATE_ID(id) (((id) == TEGRA_POWERGATE_CPU0) || \
+ ((id) == TEGRA_POWERGATE_CPU1) || \
+ ((id) == TEGRA_POWERGATE_CPU2) || \
+ ((id) == TEGRA_POWERGATE_CPU3))
+#endif
+
+struct clk;
-int tegra_powergate_power_on(int id);
-int tegra_powergate_power_off(int id);
bool tegra_powergate_is_powered(int id);
+int tegra_powergate_mc_disable(int id);
+int tegra_powergate_mc_enable(int id);
+int tegra_powergate_mc_flush(int id);
+int tegra_powergate_mc_flush_done(int id);
int tegra_powergate_remove_clamping(int id);
+const char *tegra_powergate_get_name(int id);
+
+/*
+ * Functions to powergate/un-powergate partitions.
+ * Handle clk management in the API's.
+ *
+ * tegra_powergate_partition_with_clk_off() can be called with
+ * clks ON. It disables all required clks.
+ *
+ * tegra_unpowergate_partition_with_clk_on() can be called with
+ * all required clks OFF. Returns with all clks ON.
+ *
+ * Warning: In general drivers should take care of the module
+ * clks and use tegra_powergate_partition() &
+ * tegra_unpowergate_partition() API's.
+ */
+int tegra_powergate_partition_with_clk_off(int id);
+int tegra_unpowergate_partition_with_clk_on(int id);
-/* Must be called with clk disabled, and returns with clk enabled */
-int tegra_powergate_sequence_power_up(int id, struct clk *clk);
+/*
+ * Functions to powergate un-powergate partitions.
+ * Drivers are responsible for clk enable-disable
+ *
+ * tegra_powergate_partition() should be called with all
+ * required clks OFF. Drivers should disable clks BEFORE
+ * calling this fucntion
+ *
+ * tegra_unpowergate_partition should be called with all
+ * required clks OFF. Returns with all clks OFF. Drivers
+ * should enable all clks AFTER this function
+ */
+int tegra_powergate_partition(int id);
+int tegra_unpowergate_partition(int id);
#endif /* _MACH_TEGRA_POWERGATE_H_ */
diff --git a/arch/arm/mach-tegra/include/mach/sdhci.h b/arch/arm/mach-tegra/include/mach/sdhci.h
index 4231bc7b8652..0b690025e537 100644
--- a/arch/arm/mach-tegra/include/mach/sdhci.h
+++ b/arch/arm/mach-tegra/include/mach/sdhci.h
@@ -18,6 +18,7 @@
#define __ASM_ARM_ARCH_TEGRA_SDHCI_H
#include <linux/mmc/host.h>
+#include <asm/mach/mmc.h>
struct tegra_sdhci_platform_data {
int cd_gpio;
@@ -25,6 +26,8 @@ struct tegra_sdhci_platform_data {
int power_gpio;
int is_8bit;
int pm_flags;
+ unsigned int max_clk_limit;
+ struct mmc_platform_data mmc_data;
};
#endif
diff --git a/arch/arm/mach-tegra/include/mach/spdif.h b/arch/arm/mach-tegra/include/mach/spdif.h
new file mode 100644
index 000000000000..96103fae91b1
--- /dev/null
+++ b/arch/arm/mach-tegra/include/mach/spdif.h
@@ -0,0 +1,392 @@
+/*
+ * arch/arm/mach-tegra/include/mach/spdif.h
+ *
+ *
+ * Copyright (c) 2008-2009, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ */
+
+
+#ifndef __ARCH_ARM_MACH_TEGRA_SPDIF_H
+#define __ARCH_ARM_MACH_TEGRA_SPDIF_H
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/platform_device.h>
+
+/* Offsets from TEGRA_SPDIF_BASE */
+
+#define SPDIF_CTRL_0 0x0
+#define SPDIF_STATUS_0 0x4
+#define SPDIF_STROBE_CTRL_0 0x8
+#define SPDIF_DATA_FIFO_CSR_0 0x0C
+#define SPDIF_DATA_OUT_0 0x40
+#define SPDIF_DATA_IN_0 0x80
+#define SPDIF_CH_STA_RX_A_0 0x100
+#define SPDIF_CH_STA_RX_B_0 0x104
+#define SPDIF_CH_STA_RX_C_0 0x108
+#define SPDIF_CH_STA_RX_D_0 0x10C
+#define SPDIF_CH_STA_RX_E_0 0x110
+#define SPDIF_CH_STA_RX_F_0 0x114
+#define SPDIF_CH_STA_TX_A_0 0x140
+#define SPDIF_CH_STA_TX_B_0 0x144
+#define SPDIF_CH_STA_TX_C_0 0x148
+#define SPDIF_CH_STA_TX_D_0 0x14C
+#define SPDIF_CH_STA_TX_E_0 0x150
+#define SPDIF_CH_STA_TX_F_0 0x154
+#define SPDIF_USR_STA_RX_A_0 0x180
+#define SPDIF_USR_DAT_TX_A_0 0x1C0
+
+/*
+ * Register SPDIF_CTRL_0
+ */
+
+/*
+ * 1=start capturing from left channel,0=start
+ * capturing from right channel.
+ */
+#define SPDIF_CTRL_0_CAP_LC (1<<30)
+
+/* SPDIF receiver(RX): 1=enable, 0=disable. */
+#define SPDIF_CTRL_0_RX_EN (1<<29)
+
+/* SPDIF Transmitter(TX): 1=enable, 0=disable. */
+#define SPDIF_CTRL_0_TX_EN (1<<28)
+
+/* Transmit Channel status: 1=enable, 0=disable. */
+#define SPDIF_CTRL_0_TC_EN (1<<27)
+
+/* Transmit user Data: 1=enable, 0=disable. */
+#define SPDIF_CTRL_0_TU_EN (1<<26)
+
+/* Interrupt on transmit error: 1=enable, 0=disable. */
+#define SPDIF_CTRL_0_IE_TXE (1<<25)
+
+/* Interrupt on receive error: 1=enable, 0=disable. */
+#define SPDIF_CTRL_0_IE_RXE (1<<24)
+
+/* Interrupt on invalid preamble: 1=enable, 0=disable. */
+#define SPDIF_CTRL_0_IE_P (1<<23)
+
+/* Interrupt on "B" preamble: 1=enable, 0=disable. */
+#define SPDIF_CTRL_0_IE_B (1<<22)
+
+/*
+ * Interrupt when block of channel status received:
+ * 1=enable, 0=disable.
+ */
+#define SPDIF_CTRL_0_IE_C (1<<21)
+
+/*
+ * Interrupt when a valid information unit (IU) recieve:
+ * 1=enable, 0=disable.
+ */
+#define SPDIF_CTRL_0_IE_U (1<<20)
+
+/*
+ * Interrupt when RX user FIFO attn. level is reached:
+ * 1=enable, 0=disable.
+ */
+#define SPDIF_CTRL_0_QE_RU (1<<19)
+
+/*
+ * Interrupt when TX user FIFO attn. level is reached:
+ * 1=enable, 0=disable.
+ */
+#define SPDIF_CTRL_0_QE_TU (1<<18)
+
+/*
+ * Interrupt when RX data FIFO attn. level is reached:
+ * 1=enable, 0=disable.
+ */
+#define SPDIF_CTRL_0_QE_RX (1<<17)
+
+/*
+ * Interrupt when TX data FIFO attn. level is reached:
+ * 1=enable, 0=disable.
+ */
+#define SPDIF_CTRL_0_QE_TX (1<<16)
+
+/* Loopback test mode: 1=enable internal loopback, 0=Normal mode. */
+#define SPDIF_CTRL_0_LBK_EN (1<<15)
+
+/*
+ * Pack data mode:
+ * 1=Packeted left/right channel data into a single word,
+ * 0=Single data (16 bit needs to be padded to match the
+ * interface data bit size)
+ */
+#define SPDIF_CTRL_0_PACK (1<<14)
+
+/*
+ * 00=16bit data
+ * 01=20bit data
+ * 10=24bit data
+ * 11=raw data
+ */
+#define SPDIF_BIT_MODE_MODE16BIT (0)
+#define SPDIF_BIT_MODE_MODE20BIT (1)
+#define SPDIF_BIT_MODE_MODE24BIT (2)
+#define SPDIF_BIT_MODE_MODERAW (3)
+#define SPDIF_CTRL_0_BIT_MODE_SHIFT (12)
+
+#define SPDIF_CTRL_0_BIT_MODE_MASK \
+ ((0x3) << SPDIF_CTRL_0_BIT_MODE_SHIFT)
+#define SPDIF_CTRL_0_BIT_MODE_MODE16BIT \
+ (SPDIF_BIT_MODE_MODE16BIT << SPDIF_CTRL_0_BIT_MODE_SHIFT)
+#define SPDIF_CTRL_0_BIT_MODE_MODE20BIT \
+ (SPDIF_BIT_MODE_MODE20BIT << SPDIF_CTRL_0_BIT_MODE_SHIFT)
+#define SPDIF_CTRL_0_BIT_MODE_MODE24BIT \
+ (SPDIF_BIT_MODE_MODE24BIT << SPDIF_CTRL_0_BIT_MODE_SHIFT)
+#define SPDIF_CTRL_0_BIT_MODE_MODERAW \
+ (SPDIF_BIT_MODE_MODERAW << SPDIF_CTRL_0_BIT_MODE_SHIFT)
+
+
+/*
+ * SPDIF Status Register
+ * -------------------------
+ * Note: IS_P, IS_B, IS_C, and IS_U are sticky bits.
+ * Software must write a 1 to the corresponding bit location
+ * to clear the status.
+ */
+
+/* Register SPDIF_STATUS_0 */
+
+/*
+ * Receiver(RX) shifter is busy receiving data. 1=busy, 0=not busy.
+ * This bit is asserted when the receiver first locked onto the
+ * preamble of the data stream after RX_EN is asserted. This bit is
+ * deasserted when either,
+ * (a) the end of a frame is reached after RX_EN is deeasserted, or
+ * (b) the SPDIF data stream becomes inactive.
+ */
+#define SPDIF_STATUS_0_RX_BSY (1<<29)
+
+
+/*
+ * Transmitter(TX) shifter is busy transmitting data.
+ * 1=busy, 0=not busy.
+ * This bit is asserted when TX_EN is asserted.
+ * This bit is deasserted when the end of a frame is reached after
+ * TX_EN is deasserted.
+ */
+#define SPDIF_STATUS_0_TX_BSY (1<<28)
+
+/*
+ * TX is busy shifting out channel status. 1=busy, 0=not busy.
+ * This bit is asserted when both TX_EN and TC_EN are asserted and
+ * data from CH_STA_TX_A register is loaded into the internal shifter.
+ * This bit is deasserted when either,
+ * (a) the end of a frame is reached after TX_EN is deasserted, or
+ * (b) CH_STA_TX_F register is loaded into the internal shifter.
+ */
+#define SPDIF_STATUS_0_TC_BSY (1<<27)
+
+/*
+ * TX User data FIFO busy. 1=busy, 0=not busy.
+ * This bit is asserted when TX_EN and TXU_EN are asserted and
+ * there's data in the TX user FIFO. This bit is deassert when either,
+ * (a) the end of a frame is reached after TX_EN is deasserted, or
+ * (b) there's no data left in the TX user FIFO.
+ */
+#define SPDIF_STATUS_0_TU_BSY (1<<26)
+
+/* Tx FIFO Underrun error status: 1=error, 0=no error */
+#define SPDIF_STATUS_0_TX_ERR (1<<25)
+
+/* Rx FIFO Overrun error status: 1=error, 0=no error */
+#define SPDIF_STATUS_0_RX_ERR (1<<24)
+
+/* Preamble status: 1=bad/missing preamble, 0=Preamble ok */
+#define SPDIF_STATUS_0_IS_P (1<<23)
+
+/* B-preamble detection status: 0=not detected, 1=B-preamble detected */
+#define SPDIF_STATUS_0_IS_B (1<<22)
+
+/*
+ * RX channel block data receive status:
+ * 1=received entire block of channel status,
+ * 0=entire block not recieved yet.
+ */
+#define SPDIF_STATUS_0_IS_C (1<<21)
+
+/* RX User Data Valid flag: 1=valid IU detected, 0 = no IU detected. */
+#define SPDIF_STATUS_0_IS_U (1<<20)
+
+/*
+ * RX User FIFO Status:
+ * 1=attention level reached, 0=attention level not reached.
+ */
+#define SPDIF_STATUS_0_QS_RU (1<<19)
+
+/*
+ * TX User FIFO Status:
+ * 1=attention level reached, 0=attention level not reached.
+ */
+#define SPDIF_STATUS_0_QS_TU (1<<18)
+
+/*
+ * RX Data FIFO Status:
+ * 1=attention level reached, 0=attention level not reached.
+ */
+#define SPDIF_STATUS_0_QS_RX (1<<17)
+
+/*
+ * TX Data FIFO Status:
+ * 1=attention level reached, 0=attention level not reached.
+ */
+#define SPDIF_STATUS_0_QS_TX (1<<16)
+
+
+/* SPDIF FIFO Configuration and Status Register */
+
+/* Register SPDIF_DATA_FIFO_CSR_0 */
+
+#define SPDIF_FIFO_ATN_LVL_ONE_SLOT 0
+#define SPDIF_FIFO_ATN_LVL_FOUR_SLOTS 1
+#define SPDIF_FIFO_ATN_LVL_EIGHT_SLOTS 2
+#define SPDIF_FIFO_ATN_LVL_TWELVE_SLOTS 3
+
+
+/* Clear Receiver User FIFO (RX USR.FIFO) */
+#define SPDIF_DATA_FIFO_CSR_0_RU_CLR (1<<31)
+
+/*
+ * RX USR.FIFO Attention Level:
+ * 00=1-slot-full, 01=2-slots-full, 10=3-slots-full, 11=4-slots-full.
+ */
+
+#define SPDIF_DATA_FIFO_CSR_0_RU_ATN_LVL_RU1 (0)
+#define SPDIF_DATA_FIFO_CSR_0_RU_ATN_LVL_RU2 (1)
+#define SPDIF_DATA_FIFO_CSR_0_RU_ATN_LVL_RU3 (2)
+#define SPDIF_DATA_FIFO_CSR_0_RU_ATN_LVL_RU4 (3)
+
+#define SPDIF_DATA_FIFO_CSR_0_RU_ATN_LVL_SHIFT (29)
+#define SPDIF_DATA_FIFO_CSR_0_RU_ATN_LVL_MASK \
+ (0x3 << SPDIF_DATA_FIFO_CSR_0_RU_ATN_LVL_SHIFT)
+#define SPDIF_DATA_FIFO_CSR_0_RU_ATN_LVL_RU1_WORD_FULL \
+ (SPDIF_DATA_FIFO_CSR_0_RU_ATN_LVL_RU1 << \
+ SPDIF_DATA_FIFO_CSR_0_RU_ATN_LVL_SHIF)
+#define SPDIF_DATA_FIFO_CSR_0_RU_ATN_LVL_RU2_WORD_FULL \
+ (SPDIF_DATA_FIFO_CSR_0_RU_ATN_LVL_RU2 << \
+ SPDIF_DATA_FIFO_CSR_0_RU_ATN_LVL_SHIF)
+#define SPDIF_DATA_FIFO_CSR_0_RU_ATN_LVL_RU3_WORD_FULL \
+ (SPDIF_DATA_FIFO_CSR_0_RU_ATN_LVL_RU3 << \
+ SPDIF_DATA_FIFO_CSR_0_RU_ATN_LVL_SHIF)
+#define SPDIF_DATA_FIFO_CSR_0_RU_ATN_LVL_RU4_WORD_FULL \
+ (SPDIF_DATA_FIFO_CSR_0_RU_ATN_LVL_RU4 << \
+ SPDIF_DATA_FIFO_CSR_0_RU_ATN_LVL_SHIF)
+
+/* Number of RX USR.FIFO levels with valid data. */
+#define SPDIF_DATA_FIFO_CSR_0_FULL_COUNT_SHIFT (24)
+#define SPDIF_DATA_FIFO_CSR_0_FULL_COUNT_MASK \
+ (0x1f << SPDIF_DATA_FIFO_CSR_0_FULL_COUNT_SHIFT)
+
+/* Clear Transmitter User FIFO (TX USR.FIFO) */
+#define SPDIF_DATA_FIFO_CSR_0_TU_CLR (1<<23)
+
+/*
+ * TxUSR.FIFO Attention Level:
+ * 11=4-slots-empty, 10=3-slots-empty, 01=2-slots-empty, 00=1-slot-empty.
+ */
+
+#define SPDIF_DATA_FIFO_CSR_0_TU_ATN_LVL_TU1 (0)
+#define SPDIF_DATA_FIFO_CSR_0_TU_ATN_LVL_TU2 (1)
+#define SPDIF_DATA_FIFO_CSR_0_TU_ATN_LVL_TU3 (2)
+#define SPDIF_DATA_FIFO_CSR_0_TU_ATN_LVL_TU4 (3)
+
+#define SPDIF_DATA_FIFO_CSR_0_TU_ATN_LVL_SHIFT (21)
+#define SPDIF_DATA_FIFO_CSR_0_TU_ATN_LVL_MASK \
+ (0x3 << SPDIF_DATA_FIFO_CSR_0_TU_ATN_LVL_SHIFT)
+#define SPDIF_DATA_FIFO_CSR_0_TU_ATN_LVL_TU1_WORD_EMPTY \
+ (SPDIF_DATA_FIFO_CSR_0_TU_ATN_LVL_TU1 << \
+ SPDIF_DATA_FIFO_CSR_0_TU_ATN_LVL_SHIFT)
+#define SPDIF_DATA_FIFO_CSR_0_TU_ATN_LVL_TU2_WORD_EMPTY \
+ (SPDIF_DATA_FIFO_CSR_0_TU_ATN_LVL_TU2 << \
+ SPDIF_DATA_FIFO_CSR_0_TU_ATN_LVL_SHIFT)
+#define SPDIF_DATA_FIFO_CSR_0_TU_ATN_LVL_TU3_WORD_EMPTY \
+ (SPDIF_DATA_FIFO_CSR_0_TU_ATN_LVL_TU3 << \
+ SPDIF_DATA_FIFO_CSR_0_TU_ATN_LVL_SHIFT)
+#define SPDIF_DATA_FIFO_CSR_0_TU_ATN_LVL_TU4_WORD_EMPTY \
+ (SPDIF_DATA_FIFO_CSR_0_TU_ATN_LVL_TU4 << \
+ SPDIF_DATA_FIFO_CSR_0_TU_ATN_LVL_SHIFT)
+
+/* Number of Tx USR.FIFO levels that could be filled. */
+#define SPDIF_DATA_FIFO_CSR_0_TU_EMPTY_COUNT_SHIFT (16)
+#define SPDIF_DATA_FIFO_CSR_0_TU_EMPTY_COUNT_FIELD \
+ ((0x1f) << SPDIF_DATA_FIFO_CSR_0_TU_EMPTY_COUNT_SHIFT)
+
+/* Clear Receiver Data FIFO (RX DATA.FIFO). */
+#define SPDIF_DATA_FIFO_CSR_0_RX_CLR (1<<15)
+
+/*
+ * Rx FIFO Attention Level:
+ * 11=12-slots-full, 10=8-slots-full, 01=4-slots-full, 00=1-slot-full.
+ */
+#define SPDIF_DATA_FIFO_CSR_0_RX_ATN_LVL_SHIFT (13)
+#define SPDIF_DATA_FIFO_CSR_0_RX_ATN_LVL_MASK \
+ (0x3 << SPDIF_DATA_FIFO_CSR_0_RX_ATN_LVL_SHIFT)
+#define SPDIF_DATA_FIFO_CSR_0_RX_ATN_LVL_RX1_WORD_FULL \
+ (SPDIF_FIFO_ATN_LVL_ONE_SLOT << \
+ SPDIF_DATA_FIFO_CSR_0_RX_ATN_LVL_SHIFT)
+#define SPDIF_DATA_FIFO_CSR_0_RX_ATN_LVL_RX4_WORD_FULL \
+ (SPDIF_FIFO_ATN_LVL_FOUR_SLOTS << \
+ SPDIF_DATA_FIFO_CSR_0_RX_ATN_LVL_SHIFT)
+#define SPDIF_DATA_FIFO_CSR_0_RX_ATN_LVL_RX8_WORD_FULL \
+ (SPDIF_FIFO_ATN_LVL_EIGHT_SLOTS << \
+ SPDIF_DATA_FIFO_CSR_0_RX_ATN_LVL_SHIFT)
+#define SPDIF_DATA_FIFO_CSR_0_RX_ATN_LVL_RX12_WORD_FULL \
+ (SPDIF_FIFO_ATN_LVL_TWELVE_SLOTS << \
+ SPDIF_DATA_FIFO_CSR_0_RX_ATN_LVL_SHIFT)
+
+
+/* Number of RX DATA.FIFO levels with valid data */
+#define SPDIF_DATA_FIFO_CSR_0_RX_DATA_FIFO_FULL_COUNT_SHIFT (8)
+#define SPDIF_DATA_FIFO_CSR_0_RX_DATA_FIFO_FULL_COUNT_FIELD \
+ ((0x1f) << SPDIF_DATA_FIFO_CSR_0_RX_DATA_FIFO_FULL_COUNT_SHIFT)
+
+/* Clear Transmitter Data FIFO (TX DATA.FIFO) */
+#define SPDIF_DATA_FIFO_CSR_0_TX_CLR (1<<7)
+
+/*
+ * Tx FIFO Attention Level:
+ * 11=12-slots-empty, 10=8-slots-empty, 01=4-slots-empty, 00=1-slot-empty
+ */
+#define SPDIF_DATA_FIFO_CSR_0_TX_ATN_LVL_SHIFT (5)
+#define SPDIF_DATA_FIFO_CSR_0_TX_ATN_LVL_MASK \
+ (0x3 << SPDIF_DATA_FIFO_CSR_0_TX_ATN_LVL_SHIFT)
+#define SPDIF_DATA_FIFO_CSR_0_TX_ATN_LVL_TX1_WORD_FULL \
+ (SPDIF_FIFO_ATN_LVL_ONE_SLOT << \
+ SPDIF_DATA_FIFO_CSR_0_TX_ATN_LVL_SHIFT)
+#define SPDIF_DATA_FIFO_CSR_0_TX_ATN_LVL_TX4_WORD_FULL \
+ (SPDIF_FIFO_ATN_LVL_FOUR_SLOTS << \
+ SPDIF_DATA_FIFO_CSR_0_TX_ATN_LVL_SHIFT)
+#define SPDIF_DATA_FIFO_CSR_0_TX_ATN_LVL_TX8_WORD_FULL \
+ (SPDIF_FIFO_ATN_LVL_EIGHT_SLOTS << \
+ SPDIF_DATA_FIFO_CSR_0_TX_ATN_LVL_SHIFT)
+#define SPDIF_DATA_FIFO_CSR_0_TX_ATN_LVL_TX12_WORD_FULL \
+ (SPDIF_FIFO_ATN_LVL_TWELVE_SLOTS << \
+ SPDIF_DATA_FIFO_CSR_0_TX_ATN_LVL_SHIFT)
+
+
+/* Number of Tx DATA.FIFO levels that could be filled. */
+#define SPDIF_DATA_FIFO_CSR_0_TD_EMPTY_COUNT_SHIFT (0)
+#define SPDIF_DATA_FIFO_CSR_0_TD_EMPTY_COUNT_MASK \
+ ((0x1f) << SPDIF_DATA_FIFO_CSR_0_TD_EMPTY_COUNT_SHIFT)
+
+
+#endif /* __ARCH_ARM_MACH_TEGRA_SPDIF_H */
diff --git a/arch/arm/mach-tegra/include/mach/spi.h b/arch/arm/mach-tegra/include/mach/spi.h
new file mode 100644
index 000000000000..171e4007b4bc
--- /dev/null
+++ b/arch/arm/mach-tegra/include/mach/spi.h
@@ -0,0 +1,42 @@
+/*
+ * arch/arm/mach-tegra/include/mach/spi.h
+ *
+ * Copyright (c) 2010, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __MACH_TEGRA_SPI_H
+#define __MACH_TEGRA_SPI_H
+
+#include <linux/types.h>
+#include <linux/spi/spi.h>
+
+typedef int (*callback)(void *client_data);
+
+/**
+ * register_spi_slave_callback - registers notification callback provided by
+ * the client.
+ * This callback indicate that the controller is all set to receive/transfer
+ * data.
+ * @spi: struct spi_device - refer to linux/spi/spi.h
+ * @func: Callback function
+ * @client_data: Data to be passed in callback
+ * Context: can not sleep
+ */
+int spi_tegra_register_callback(struct spi_device *spi, callback func,
+ void *client_data);
+
+#endif
diff --git a/arch/arm/mach-tegra/include/mach/system.h b/arch/arm/mach-tegra/include/mach/system.h
index 027c4215d313..7bc605e5dc84 100644
--- a/arch/arm/mach-tegra/include/mach/system.h
+++ b/arch/arm/mach-tegra/include/mach/system.h
@@ -7,6 +7,8 @@
* Colin Cross <ccross@google.com>
* Erik Gilling <konkers@google.com>
*
+ * Copyright (C) 2010-2011 NVIDIA Corporation
+ *
* 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.
diff --git a/arch/arm/mach-tegra/include/mach/tegra-bb-power.h b/arch/arm/mach-tegra/include/mach/tegra-bb-power.h
new file mode 100644
index 000000000000..893cc9120494
--- /dev/null
+++ b/arch/arm/mach-tegra/include/mach/tegra-bb-power.h
@@ -0,0 +1,56 @@
+/*
+ * arch/arm/mach-tegra/include/mach/tegra-bb-power.h
+ *
+ * Copyright (C) 2011 NVIDIA Corporation
+ *
+ * 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 GPIO_INVALID UINT_MAX
+
+union tegra_bb_gpio_id {
+ struct {
+ int mdm_reset;
+ int mdm_on;
+ int ap2mdm_ack;
+ int mdm2ap_ack;
+ int ap2mdm_ack2;
+ int mdm2ap_ack2;
+ int rsvd1;
+ int rsvd2;
+ } generic;
+ struct {
+ int bb_rst;
+ int bb_on;
+ int ipc_bb_wake;
+ int ipc_ap_wake;
+ int ipc_hsic_active;
+ int ipc_hsic_sus_req;
+ int rsvd1;
+ int rsvd2;
+ } xmm;
+ struct {
+ int pwr_status;
+ int pwr_on;
+ int uart_awr;
+ int uart_cwr;
+ int usb_awr;
+ int usb_cwr;
+ int service;
+ int resout2;
+ } m7400;
+};
+
+struct tegra_bb_pdata {
+ union tegra_bb_gpio_id *id;
+ struct platform_device *device;
+ int bb_id;
+};
diff --git a/arch/arm/mach-tegra/include/mach/tegra_aic326x_pdata.h b/arch/arm/mach-tegra/include/mach/tegra_aic326x_pdata.h
new file mode 100644
index 000000000000..a47ef1982e4d
--- /dev/null
+++ b/arch/arm/mach-tegra/include/mach/tegra_aic326x_pdata.h
@@ -0,0 +1,39 @@
+/*
+ * arch/arm/mach-tegra/include/mach/tegra_aic326x_pdata.h
+ *
+ * Copyright 2011 NVIDIA, 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 __MACH_TEGRA_TLVAIC326X_H
+#define __MACH_TEGRA_TLVAIC326X_H
+
+#define HIFI_CODEC 0
+#define BASEBAND 1
+#define BT_SCO 2
+#define NUM_I2S_DEVICES 3
+
+struct baseband_config {
+ int rate;
+ int channels;
+};
+
+struct tegra_aic326x_platform_data {
+ int gpio_spkr_en;
+ int gpio_hp_det;
+ int gpio_hp_mute;
+ int gpio_int_mic_en;
+ int gpio_ext_mic_en;
+ int audio_port_id[NUM_I2S_DEVICES];
+ struct baseband_config baseband_param;
+};
+#endif
+
diff --git a/arch/arm/mach-tegra/include/mach/tegra_dc_ext.h b/arch/arm/mach-tegra/include/mach/tegra_dc_ext.h
new file mode 100644
index 000000000000..521039283d8c
--- /dev/null
+++ b/arch/arm/mach-tegra/include/mach/tegra_dc_ext.h
@@ -0,0 +1,77 @@
+/*
+ * arch/arm/mach-tegra/include/mach/tegra_dc_ext.h
+ *
+ * Copyright (C) 2011, NVIDIA Corporation
+ *
+ * Author: Robert Morell <rmorell@nvidia.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.
+ */
+
+#ifndef __MACH_TEGRA_DC_EXT_H
+#define __MACH_TEGRA_DC_EXT_H
+
+#include <linux/nvhost.h>
+
+struct tegra_dc_ext;
+
+#ifdef CONFIG_TEGRA_DC_EXTENSIONS
+int __init tegra_dc_ext_module_init(void);
+void __exit tegra_dc_ext_module_exit(void);
+
+struct tegra_dc_ext *tegra_dc_ext_register(struct nvhost_device *ndev,
+ struct tegra_dc *dc);
+void tegra_dc_ext_unregister(struct tegra_dc_ext *dc_ext);
+
+/* called by display controller on enable/disable */
+void tegra_dc_ext_enable(struct tegra_dc_ext *dc_ext);
+void tegra_dc_ext_disable(struct tegra_dc_ext *dc_ext);
+
+int tegra_dc_ext_process_hotplug(int output);
+
+#else /* CONFIG_TEGRA_DC_EXTENSIONS */
+
+static inline
+int tegra_dc_ext_module_init(void)
+{
+ return 0;
+}
+static inline
+void tegra_dc_ext_module_exit(void)
+{
+}
+
+static inline
+struct tegra_dc_ext *tegra_dc_ext_register(struct nvhost_device *ndev,
+ struct tegra_dc *dc)
+{
+ return NULL;
+}
+static inline
+void tegra_dc_ext_unregister(struct tegra_dc_ext *dc_ext)
+{
+}
+static inline
+void tegra_dc_ext_enable(struct tegra_dc_ext *dc_ext)
+{
+}
+static inline
+void tegra_dc_ext_disable(struct tegra_dc_ext *dc_ext)
+{
+}
+static inline
+int tegra_dc_ext_process_hotplug(int output)
+{
+ return 0;
+}
+#endif /* CONFIG_TEGRA_DC_EXTENSIONS */
+
+#endif /* __MACH_TEGRA_DC_EXT_H */
diff --git a/arch/arm/mach-tegra/include/mach/tegra_fb.h b/arch/arm/mach-tegra/include/mach/tegra_fb.h
new file mode 100644
index 000000000000..84ae8869b247
--- /dev/null
+++ b/arch/arm/mach-tegra/include/mach/tegra_fb.h
@@ -0,0 +1,27 @@
+/*
+ * arch/arm/mach-tegra/include/mach/tegra_fb.h
+ *
+ * Copyright (C) 2010 Google, Inc.
+ * Author: Colin Cross <ccross@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.
+ *
+ */
+
+/* Platform data structure to be passed to the driver */
+struct tegra_fb_lcd_data {
+ int fb_xres;
+ int fb_yres;
+ /* Resolution of the output to the LCD. If different from the
+ framebuffer resolution, the Tegra display block will scale it */
+ int lcd_xres;
+ int lcd_yres;
+ int bits_per_pixel;
+};
diff --git a/arch/arm/mach-tegra/include/mach/tegra_fiq_debugger.h b/arch/arm/mach-tegra/include/mach/tegra_fiq_debugger.h
new file mode 100644
index 000000000000..4d1a0b54f2ae
--- /dev/null
+++ b/arch/arm/mach-tegra/include/mach/tegra_fiq_debugger.h
@@ -0,0 +1,30 @@
+/*
+ * linux/arch/arm/mach-tegra/include/mach/tegra_fiq_debugger.h
+ *
+ * Copyright (C) 2010 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 __MACH_TEGRA_FIQ_DEBUGGER_H
+#define __MACH_TEGRA_FIQ_DEBUGGER_H
+
+#ifdef CONFIG_TEGRA_FIQ_DEBUGGER
+void tegra_serial_debug_init(unsigned int base, int irq,
+ struct clk *clk, int signal_irq, int wakeup_irq);
+#else
+static inline void tegra_serial_debug_init(unsigned int base, int irq,
+ struct clk *clk, int signal_irq, int wakeup_irq)
+{
+}
+#endif
+
+#endif
diff --git a/arch/arm/mach-tegra/include/mach/tegra_fuse.h b/arch/arm/mach-tegra/include/mach/tegra_fuse.h
new file mode 100644
index 000000000000..d264745c70c0
--- /dev/null
+++ b/arch/arm/mach-tegra/include/mach/tegra_fuse.h
@@ -0,0 +1,27 @@
+/*
+ * arch/arm/mach-tegra/include/mach/tegra_fuse.h
+ *
+ * Tegra Public Fuse header file
+ *
+ * Copyright (c) 2011, NVIDIA Corporation.
+ *
+ * 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 _MACH_TEGRA_PUBLIC_FUSE_H_
+#define _MACH_TEGRA_PUBLIC_FUSE_H_
+
+int tegra_fuse_get_revision(u32 *rev);
+int tegra_fuse_get_tsensor_calibration_data(u32 *calib);
+int tegra_fuse_get_tsensor_spare_bits(u32 *spare_bits);
+
+#endif /* _MACH_TEGRA_PUBLIC_FUSE_H_*/
+
diff --git a/arch/arm/mach-tegra/include/mach/tegra_max98088_pdata.h b/arch/arm/mach-tegra/include/mach/tegra_max98088_pdata.h
new file mode 100644
index 000000000000..eb59cf0962bd
--- /dev/null
+++ b/arch/arm/mach-tegra/include/mach/tegra_max98088_pdata.h
@@ -0,0 +1,35 @@
+/*
+ * arch/arm/mach-tegra/include/mach/tegra_max98088_pdata.h
+ *
+ * Copyright 2011 NVIDIA, 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.
+ *
+ */
+
+#define HIFI_CODEC 0
+#define BASEBAND 1
+#define BT_SCO 2
+#define NUM_I2S_DEVICES 3
+
+struct baseband_config {
+ int rate;
+ int channels;
+};
+
+struct tegra_max98088_platform_data {
+ int gpio_spkr_en;
+ int gpio_hp_det;
+ int gpio_hp_mute;
+ int gpio_int_mic_en;
+ int gpio_ext_mic_en;
+ int audio_port_id[NUM_I2S_DEVICES];
+ struct baseband_config baseband_param;
+};
diff --git a/arch/arm/mach-tegra/include/mach/tegra_odm_fuses.h b/arch/arm/mach-tegra/include/mach/tegra_odm_fuses.h
new file mode 100644
index 000000000000..4ab8433dbbff
--- /dev/null
+++ b/arch/arm/mach-tegra/include/mach/tegra_odm_fuses.h
@@ -0,0 +1,107 @@
+/*
+ * arch/arm/mach-tegra/include/mach/tegra_odm_fuses.h
+ *
+ * Copyright (c) 2011, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __MACH_TEGRA_ODM_FUSES_H
+#define __MACH_TEGRA_ODM_FUSES_H
+
+#define SBK_DEVKEY_STATUS_SZ sizeof(u32)
+
+/*
+ * fuse io parameters: params with sizes less than a byte are
+ * explicitly mentioned
+ */
+enum fuse_io_param {
+ DEVKEY,
+ JTAG_DIS, /* 1 bit long */
+ /*
+ * Programming the odm production fuse at the same
+ * time as the sbk or dev_key is not allowed as it is not possible to
+ * verify that the sbk or dev_key were programmed correctly.
+ */
+ ODM_PROD_MODE, /* 1 bit long */
+ SEC_BOOT_DEV_CFG,
+ SEC_BOOT_DEV_SEL, /* 3 bits long */
+ SBK,
+ SW_RSVD, /* 4 bits long */
+ IGNORE_DEV_SEL_STRAPS, /* 1 bit long */
+ ODM_RSVD,
+ SBK_DEVKEY_STATUS,
+ _PARAMS_U32 = 0x7FFFFFFF
+};
+
+#define MAX_PARAMS SBK_DEVKEY_STATUS
+
+/* the order of the members is pre-decided. please do not change */
+struct fuse_data {
+ u32 devkey;
+ u32 jtag_dis;
+ u32 odm_prod_mode;
+ u32 bootdev_cfg;
+ u32 bootdev_sel;
+ u32 sbk[4];
+ u32 sw_rsvd;
+ u32 ignore_devsel_straps;
+ u32 odm_rsvd[8];
+};
+
+/* secondary boot device options */
+enum {
+ SECBOOTDEV_SDMMC,
+ SECBOOTDEV_NOR,
+ SECBOOTDEV_SPI,
+ SECBOOTDEV_NAND,
+ SECBOOTDEV_LBANAND,
+ SECBOOTDEV_MUXONENAND,
+ _SECBOOTDEV_MAX,
+ _SECBOOTDEV_U32 = 0x7FFFFFFF
+};
+
+/*
+ * read the fuse settings
+ * @param: io_param_type - param type enum
+ * @param: size - read size in bytes
+ */
+int tegra_fuse_read(enum fuse_io_param io_param_type, u32 *data, int size);
+
+#define FLAGS_DEVKEY BIT(DEVKEY)
+#define FLAGS_JTAG_DIS BIT(JTAG_DIS)
+#define FLAGS_SBK_DEVKEY_STATUS BIT(SBK_DEVKEY_STATUS)
+#define FLAGS_ODM_PROD_MODE BIT(ODM_PROD_MODE)
+#define FLAGS_SEC_BOOT_DEV_CFG BIT(SEC_BOOT_DEV_CFG)
+#define FLAGS_SEC_BOOT_DEV_SEL BIT(SEC_BOOT_DEV_SEL)
+#define FLAGS_SBK BIT(SBK)
+#define FLAGS_SW_RSVD BIT(SW_RSVD)
+#define FLAGS_IGNORE_DEV_SEL_STRAPS BIT(IGNORE_DEV_SEL_STRAPS)
+#define FLAGS_ODMRSVD BIT(ODM_RSVD)
+
+/*
+ * Prior to invoking this routine, the caller is responsible for supplying
+ * valid fuse programming voltage.
+ *
+ * @param: pgm_data - entire data to be programmed
+ * @flags: program flags (e.g. FLAGS_DEVKEY)
+ */
+int tegra_fuse_program(struct fuse_data *pgm_data, u32 flags);
+
+/* Disables the fuse programming until the next system reset */
+void tegra_fuse_program_disable(void);
+
+extern int (*tegra_fuse_regulator_en)(int);
+#endif
diff --git a/arch/arm/mach-tegra/include/mach/tegra_usb_modem_power.h b/arch/arm/mach-tegra/include/mach/tegra_usb_modem_power.h
new file mode 100644
index 000000000000..0ce7fa40eb2e
--- /dev/null
+++ b/arch/arm/mach-tegra/include/mach/tegra_usb_modem_power.h
@@ -0,0 +1,47 @@
+/*
+ * arch/arm/mach-tegra/include/mach/tegra_usb_modem_power.c
+ *
+ * Copyright (c) 2011, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __MACH_TEGRA_USB_MODEM_POWER_H
+#define __MACH_TEGRA_USB_MODEM_POWER_H
+
+#include <linux/interrupt.h>
+
+/* modem capabilities */
+#define TEGRA_MODEM_AUTOSUSPEND 0x01
+#define TEGRA_MODEM_RECOVERY 0x02
+
+/* modem operations */
+struct tegra_modem_operations {
+ int (*init) (void); /* modem init */
+ void (*start) (void); /* modem start */
+ void (*stop) (void); /* modem stop */
+ void (*suspend) (void); /* send L3 hint during system suspend */
+ void (*resume) (void); /* send L3->0 hint during system resume */
+ void (*reset) (void); /* modem reset */
+};
+
+/* tegra usb modem power platform data */
+struct tegra_usb_modem_power_platform_data {
+ const struct tegra_modem_operations *ops;
+ unsigned int wake_gpio; /* remote wakeup gpio */
+ unsigned int flags; /* remote wakeup irq flags */
+};
+
+#endif /* __MACH_TEGRA_USB_MODEM_POWER_H */
diff --git a/arch/arm/mach-tegra/include/mach/tegra_wm8753_pdata.h b/arch/arm/mach-tegra/include/mach/tegra_wm8753_pdata.h
new file mode 100644
index 000000000000..944e410b4aec
--- /dev/null
+++ b/arch/arm/mach-tegra/include/mach/tegra_wm8753_pdata.h
@@ -0,0 +1,24 @@
+/*
+ * arch/arm/mach-tegra/include/mach/tegra_wm8903_pdata.h
+ *
+ * Copyright 2011 NVIDIA, 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.
+ *
+ */
+
+struct tegra_wm8753_platform_data {
+ int gpio_spkr_en;
+ int gpio_hp_det;
+ int gpio_hp_mute;
+ int gpio_int_mic_en;
+ int gpio_ext_mic_en;
+ unsigned int debounce_time_hp;
+};
diff --git a/arch/arm/mach-tegra/include/mach/thermal.h b/arch/arm/mach-tegra/include/mach/thermal.h
new file mode 100644
index 000000000000..714b63370b06
--- /dev/null
+++ b/arch/arm/mach-tegra/include/mach/thermal.h
@@ -0,0 +1,60 @@
+/*
+ * arch/arm/mach-tegra/thermal.h
+ *
+ * Copyright (C) 2010-2011 NVIDIA Corporation.
+ *
+ * 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 __MACH_THERMAL_H
+#define __MACH_THERMAL_H
+
+/* All units in millicelsius */
+struct tegra_thermal_data {
+ long temp_throttle;
+ long temp_shutdown;
+ long temp_offset;
+#ifdef CONFIG_TEGRA_EDP_LIMITS
+ long edp_offset;
+ long hysteresis_edp;
+#endif
+#ifdef CONFIG_TEGRA_THERMAL_SYSFS
+ int tc1;
+ int tc2;
+ long passive_delay;
+#else
+ long hysteresis_throttle;
+#endif
+};
+
+struct tegra_thermal_device {
+ void *data;
+ long offset;
+ int (*get_temp) (void *, long *);
+ int (*set_limits) (void *, long, long);
+ int (*set_alert)(void *, void (*)(void *), void *);
+ int (*set_shutdown_temp)(void *, long);
+};
+
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+int tegra_thermal_init(struct tegra_thermal_data *data);
+int tegra_thermal_set_device(struct tegra_thermal_device *device);
+int tegra_thermal_exit(void);
+#else
+static inline int tegra_thermal_init(struct tegra_thermal_data *data)
+{ return 0; }
+static inline int tegra_thermal_set_device(struct tegra_thermal_device *dev)
+{ return 0; }
+static inline int tegra_thermal_exit(void)
+{ return 0; }
+#endif
+
+#endif /* __MACH_THERMAL_H */
diff --git a/arch/arm/mach-tegra/include/mach/tsensor.h b/arch/arm/mach-tegra/include/mach/tsensor.h
new file mode 100644
index 000000000000..63a9c9676ebd
--- /dev/null
+++ b/arch/arm/mach-tegra/include/mach/tsensor.h
@@ -0,0 +1,53 @@
+/*
+ * arch/arm/mach-tegra/include/mach/tsensor.h
+ *
+ * Tegra tsensor header file
+ *
+ * Copyright (c) 2011, NVIDIA Corporation.
+ *
+ * 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 __MACH_TEGRA_TSENSOR_H
+#define __MACH_TEGRA_TSENSOR_H
+
+#include <linux/types.h>
+
+#include <mach/edp.h>
+
+#define MAX_ZONES 16
+
+struct tegra_tsensor_pmu_data {
+ u8 poweroff_reg_data;
+ u8 poweroff_reg_addr;
+ u8 reset_tegra;
+ u8 controller_type;
+ u8 i2c_controller_id;
+ u8 pinmux;
+ u8 pmu_16bit_ops;
+ u8 pmu_i2c_addr;
+};
+
+struct tegra_tsensor_platform_data {
+ int sw_intr_temperature;
+ int hw_clk_div_temperature;
+ int hw_reset_temperature;
+ int hysteresis;
+ u8 throttling_ext_limit;
+ u8 thermal_zones[MAX_ZONES];
+ u8 thermal_zones_sz;
+ void (*alarm_fn)(bool raised);
+};
+
+void __init tegra3_tsensor_init(struct tegra_tsensor_pmu_data *);
+
+#endif /* __MACH_TEGRA_TSENSOR_H */
+
diff --git a/arch/arm/mach-tegra/include/mach/uncompress.h b/arch/arm/mach-tegra/include/mach/uncompress.h
index 4e8323770c79..9665858ab11d 100644
--- a/arch/arm/mach-tegra/include/mach/uncompress.h
+++ b/arch/arm/mach-tegra/include/mach/uncompress.h
@@ -7,6 +7,8 @@
* Colin Cross <ccross@google.com>
* Erik Gilling <konkers@google.com>
*
+ * Copyright (C) 2010-2011 NVIDIA Corporation
+ *
* 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.
@@ -26,6 +28,50 @@
#include <mach/iomap.h>
+#if defined(CONFIG_TEGRA_DEBUG_UARTA)
+#define DEBUG_UART_CLK_SRC (TEGRA_CLK_RESET_BASE + 0x178)
+#define DEBUG_UART_CLK_ENB_SET_REG (TEGRA_CLK_RESET_BASE + 0x320)
+#define DEBUG_UART_CLK_ENB_SET_BIT (1 << 6)
+#define DEBUG_UART_RST_CLR_REG (TEGRA_CLK_RESET_BASE + 0x304)
+#define DEBUG_UART_RST_CLR_BIT (1 << 6)
+#elif defined(CONFIG_TEGRA_DEBUG_UARTB)
+#define DEBUG_UART_CLK_SRC (TEGRA_CLK_RESET_BASE + 0x17c)
+#define DEBUG_UART_CLK_ENB_SET_REG (TEGRA_CLK_RESET_BASE + 0x320)
+#define DEBUG_UART_CLK_ENB_SET_BIT (1 << 7)
+#define DEBUG_UART_RST_CLR_REG (TEGRA_CLK_RESET_BASE + 0x304)
+#define DEBUG_UART_RST_CLR_BIT (1 << 7)
+#elif defined(CONFIG_TEGRA_DEBUG_UARTC)
+#define DEBUG_UART_CLK_SRC (TEGRA_CLK_RESET_BASE + 0x1a0)
+#define DEBUG_UART_CLK_ENB_SET_REG (TEGRA_CLK_RESET_BASE + 0x328)
+#define DEBUG_UART_CLK_ENB_SET_BIT (1 << 23)
+#define DEBUG_UART_RST_CLR_REG (TEGRA_CLK_RESET_BASE + 0x30C)
+#define DEBUG_UART_RST_CLR_BIT (1 << 23)
+#elif defined(CONFIG_TEGRA_DEBUG_UARTD)
+#define DEBUG_UART_CLK_SRC (TEGRA_CLK_RESET_BASE + 0x1c0)
+#define DEBUG_UART_CLK_ENB_SET_REG (TEGRA_CLK_RESET_BASE + 0x330)
+#define DEBUG_UART_CLK_ENB_SET_BIT (1 << 1)
+#define DEBUG_UART_RST_CLR_REG (TEGRA_CLK_RESET_BASE + 0x314)
+#define DEBUG_UART_RST_CLR_BIT (1 << 1)
+#elif defined(CONFIG_TEGRA_DEBUG_UARTE)
+#define DEBUG_UART_CLK_SRC (TEGRA_CLK_RESET_BASE + 0x1c4)
+#define DEBUG_UART_CLK_ENB_SET_REG (TEGRA_CLK_RESET_BASE + 0x330)
+#define DEBUG_UART_CLK_ENB_SET_BIT (1 << 2)
+#define DEBUG_UART_RST_CLR_REG (TEGRA_CLK_RESET_BASE + 0x314)
+#define DEBUG_UART_RST_CLR_BIT (1 << 2)
+#else
+#define DEBUG_UART_CLK_SRC 0
+#define DEBUG_UART_CLK_ENB_SET_REG 0
+#define DEBUG_UART_CLK_ENB_SET_BIT 0
+#define DEBUG_UART_RST_CLR_REG 0
+#define DEBUG_UART_RST_CLR_BIT 0
+#endif
+
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+#define DEBUG_UART_DLL 0x75
+#else
+#define DEBUG_UART_DLL 0xdd
+#endif
+
static void putc(int c)
{
volatile u8 *uart = (volatile u8 *)TEGRA_DEBUG_UART_BASE;
@@ -43,16 +89,44 @@ static inline void flush(void)
{
}
+static inline void konk_delay(int delay)
+{
+ int i;
+
+ for (i = 0; i < (1000 * delay); i++) {
+ barrier();
+ }
+}
+
+
static inline void arch_decomp_setup(void)
{
volatile u8 *uart = (volatile u8 *)TEGRA_DEBUG_UART_BASE;
int shift = 2;
+ volatile u32 *addr;
if (uart == NULL)
return;
+ /* Debug UART clock source is PLLP_OUT0. */
+ addr = (volatile u32 *)DEBUG_UART_CLK_SRC;
+ *addr = 0;
+
+ /* Enable clock to debug UART. */
+ addr = (volatile u32 *)DEBUG_UART_CLK_ENB_SET_REG;
+ *addr = DEBUG_UART_CLK_ENB_SET_BIT;
+
+ konk_delay(5);
+
+ /* Deassert reset to debug UART. */
+ addr = (volatile u32 *)DEBUG_UART_RST_CLR_REG;
+ *addr = DEBUG_UART_RST_CLR_BIT;
+
+ konk_delay(5);
+
+ /* Set up debug UART. */
uart[UART_LCR << shift] |= UART_LCR_DLAB;
- uart[UART_DLL << shift] = 0x75;
+ uart[UART_DLL << shift] = DEBUG_UART_DLL;
uart[UART_DLM << shift] = 0x0;
uart[UART_LCR << shift] = 3;
}
diff --git a/arch/arm/mach-tegra/include/mach/usb_phy.h b/arch/arm/mach-tegra/include/mach/usb_phy.h
index d4b8f9e298a8..64ba2e83d2e8 100644
--- a/arch/arm/mach-tegra/include/mach/usb_phy.h
+++ b/arch/arm/mach-tegra/include/mach/usb_phy.h
@@ -2,6 +2,7 @@
* arch/arm/mach-tegra/include/mach/usb_phy.h
*
* Copyright (C) 2010 Google, Inc.
+ * Copyright (C) 2011 NVIDIA Corporation.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@@ -18,7 +19,9 @@
#define __MACH_USB_PHY_H
#include <linux/clk.h>
+#include <linux/regulator/consumer.h>
#include <linux/usb/otg.h>
+#include <linux/platform_data/tegra_usb.h>
struct tegra_utmip_config {
u8 hssync_start_delay;
@@ -26,13 +29,44 @@ struct tegra_utmip_config {
u8 idle_wait_delay;
u8 term_range_adj;
u8 xcvr_setup;
+ u8 xcvr_setup_offset;
+ u8 xcvr_use_fuses;
u8 xcvr_lsfslew;
u8 xcvr_lsrslew;
};
+struct tegra_ulpi_trimmer {
+ u8 shadow_clk_delay; /* 0 ~ 31 */
+ u8 clock_out_delay; /* 0 ~ 31 */
+ u8 data_trimmer; /* 0 ~ 7 */
+ u8 stpdirnxt_trimmer; /* 0 ~ 7 */
+};
+
struct tegra_ulpi_config {
+ int enable_gpio;
int reset_gpio;
const char *clk;
+ const struct tegra_ulpi_trimmer *trimmer;
+ int (*pre_phy_on)(void);
+ int (*post_phy_on)(void);
+ int (*pre_phy_off)(void);
+ int (*post_phy_off)(void);
+ void (*phy_restore_start)(void);
+ void (*phy_restore_end)(void);
+};
+
+struct tegra_uhsic_config {
+ int enable_gpio;
+ int reset_gpio;
+ u8 sync_start_delay;
+ u8 idle_wait_delay;
+ u8 term_range_adj;
+ u8 elastic_underrun_limit;
+ u8 elastic_overrun_limit;
+ int (*postsuspend)(void);
+ int (*preresume)(void);
+ int (*usb_phy_ready)(void);
+ int (*post_phy_off)(void);
};
enum tegra_usb_phy_port_speed {
@@ -46,6 +80,13 @@ enum tegra_usb_phy_mode {
TEGRA_USB_PHY_MODE_HOST,
};
+struct usb_phy_plat_data {
+ int instance;
+ int vbus_irq;
+ int vbus_gpio;
+ char * vbus_reg_supply;
+};
+
struct tegra_xtal_freq;
struct tegra_usb_phy {
@@ -58,23 +99,44 @@ struct tegra_usb_phy {
struct clk *pad_clk;
enum tegra_usb_phy_mode mode;
void *config;
+ struct regulator *reg_vdd;
+ struct regulator *reg_vbus;
+ enum tegra_usb_phy_type usb_phy_type;
+ bool regulator_on;
struct otg_transceiver *ulpi;
+ int initialized;
+ bool power_on;
+ bool remote_wakeup;
+ int hotplug;
+ unsigned int xcvr_setup_value;
};
+typedef int (*tegra_phy_fp)(struct tegra_usb_phy *phy, bool is_dpd);
+typedef void (*tegra_phy_restore_start_fp)(struct tegra_usb_phy *phy,
+ enum tegra_usb_phy_port_speed);
+typedef void (*tegra_phy_restore_end_fp)(struct tegra_usb_phy *phy);
+
struct tegra_usb_phy *tegra_usb_phy_open(int instance, void __iomem *regs,
- void *config, enum tegra_usb_phy_mode phy_mode);
+ void *config, enum tegra_usb_phy_mode phy_mode,
+ enum tegra_usb_phy_type usb_phy_type);
-int tegra_usb_phy_power_on(struct tegra_usb_phy *phy);
+int tegra_usb_phy_power_on(struct tegra_usb_phy *phy, bool is_dpd);
void tegra_usb_phy_clk_disable(struct tegra_usb_phy *phy);
void tegra_usb_phy_clk_enable(struct tegra_usb_phy *phy);
-void tegra_usb_phy_power_off(struct tegra_usb_phy *phy);
+void tegra_usb_phy_power_off(struct tegra_usb_phy *phy, bool is_dpd);
-void tegra_usb_phy_preresume(struct tegra_usb_phy *phy);
+void tegra_usb_phy_postsuspend(struct tegra_usb_phy *phy, bool is_dpd);
-void tegra_usb_phy_postresume(struct tegra_usb_phy *phy);
+void tegra_usb_phy_preresume(struct tegra_usb_phy *phy, bool is_dpd);
+
+void tegra_usb_phy_postresume(struct tegra_usb_phy *phy, bool is_dpd);
+
+void tegra_ehci_pre_reset(struct tegra_usb_phy *phy, bool is_dpd);
+
+void tegra_ehci_post_reset(struct tegra_usb_phy *phy, bool is_dpd);
void tegra_ehci_phy_restore_start(struct tegra_usb_phy *phy,
enum tegra_usb_phy_port_speed port_speed);
@@ -83,4 +145,18 @@ void tegra_ehci_phy_restore_end(struct tegra_usb_phy *phy);
void tegra_usb_phy_close(struct tegra_usb_phy *phy);
+int tegra_usb_phy_bus_connect(struct tegra_usb_phy *phy);
+
+int tegra_usb_phy_bus_reset(struct tegra_usb_phy *phy);
+
+int tegra_usb_phy_bus_idle(struct tegra_usb_phy *phy);
+
+bool tegra_usb_phy_is_device_connected(struct tegra_usb_phy *phy);
+
+bool tegra_usb_phy_charger_detect(struct tegra_usb_phy *phy);
+
+int __init tegra_usb_phy_init(struct usb_phy_plat_data *pdata, int size);
+
+bool tegra_usb_phy_is_remotewake_detected(struct tegra_usb_phy *phy);
+
#endif /* __MACH_USB_PHY_H */
diff --git a/arch/arm/mach-tegra/include/mach/vmalloc.h b/arch/arm/mach-tegra/include/mach/vmalloc.h
index fd6aa65b2dc6..db488e890b9e 100644
--- a/arch/arm/mach-tegra/include/mach/vmalloc.h
+++ b/arch/arm/mach-tegra/include/mach/vmalloc.h
@@ -23,6 +23,6 @@
#include <asm/sizes.h>
-#define VMALLOC_END 0xFE000000UL
+#define VMALLOC_END 0xF8000000UL
#endif
diff --git a/arch/arm/mach-tegra/include/mach/w1.h b/arch/arm/mach-tegra/include/mach/w1.h
new file mode 100644
index 000000000000..c96df7abdf96
--- /dev/null
+++ b/arch/arm/mach-tegra/include/mach/w1.h
@@ -0,0 +1,84 @@
+/*
+ * include/mach/w1.h
+ *
+ * Copyright (C) 2010 Motorola, Inc
+ * Author: Andrei Warkentin <andreiw@motorola.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 __ASM_ARM_ARCH_TEGRA_W1_H
+#define __ASM_ARM_ARCH_TEGRA_W1_H
+
+struct tegra_w1_timings {
+
+ /* tsu, trelease, trdv, tlow0, tlow1 and tslot are formed
+ into the value written into OWR_RST_PRESENCE_TCTL_0 register. */
+
+ /* Read data setup, Tsu = N owr clks, Range = tsu < 1,
+ Typical value = 0x1 */
+ uint32_t tsu;
+
+ /* Release 1-wire time, Trelease = N owr clks,
+ Range = 0 <= trelease < 45, Typical value = 0xf */
+ uint32_t trelease;
+
+ /* Read data valid time, Trdv = N+1 owr clks, Range = Exactly 15 */
+ uint32_t trdv;
+
+ /* Write zero time low, Tlow0 = N+1 owr clks,
+ Range = 60 <= tlow0 < tslot < 120, typical value = 0x3c. */
+ uint32_t tlow0;
+
+ /* Write one time low, or TLOWR both are same Tlow1 = N+1 owr clks,
+ Range = 1 <= tlow1 < 15 TlowR = N+1 owr clks,
+ Range = 1 <= tlowR < 15, Typical value = 0x1. */
+ uint32_t tlow1;
+
+ /* Active time slot for write or read data, Tslot = N+1 owr clks,
+ Range = 60 <= tslot < 120, Typical value = 0x77. */
+ uint32_t tslot;
+
+ /* tpdl, tpdh, trstl, trsth are formed in the the value written
+ into the OWR_RST_PRESENCE_TCTL_0 register. */
+
+ /* Tpdl = N owr clks, Range = 60 <= tpdl < 240,
+ Typical value = 0x78. */
+ uint32_t tpdl;
+
+ /* Tpdh = N+1 owr clks, Range = 15 <= tpdh < 60.
+ Typical value = 0x1e. */
+ uint32_t tpdh;
+
+ /* Trstl = N+1 owr clks, Range = 480 <= trstl < infinity,
+ Typical value = 0x1df. */
+ uint32_t trstl;
+
+ /* Trsth = N+1 owr clks, Range = 480 <= trsth < infinity,
+ Typical value = 0x1df. */
+ uint32_t trsth;
+
+ /* Read data sample clock. Should be <= to (tlow1 - 6) clks,
+ 6 clks are used for deglitch. If deglitch bypassed it
+ is 3 clks, Typical value = 0x7. */
+ uint32_t rdsclk;
+
+ /* Presence sample clock. Should be <= to (tpdl - 6) clks,
+ 6 clks are used for deglitch. If deglitch bypassed it is 3 clks,
+ Typical value = 0x50. */
+ uint32_t psclk;
+};
+
+struct tegra_w1_platform_data {
+ const char *clk_id;
+ struct tegra_w1_timings *timings;
+};
+
+#endif
diff --git a/arch/arm/mach-tegra/io.c b/arch/arm/mach-tegra/io.c
index ea50fe28cf6a..a6dd33bca170 100644
--- a/arch/arm/mach-tegra/io.c
+++ b/arch/arm/mach-tegra/io.c
@@ -7,6 +7,8 @@
* Colin Cross <ccross@google.com>
* Erik Gilling <konkers@google.com>
*
+ * Copyright (C) 2010-2011 NVIDIA Corporation
+ *
* 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.
@@ -24,6 +26,7 @@
#include <linux/mm.h>
#include <linux/io.h>
+#include <mach/iomap.h>
#include <asm/page.h>
#include <asm/mach/map.h>
@@ -54,6 +57,24 @@ static struct map_desc tegra_io_desc[] __initdata = {
.length = IO_IRAM_SIZE,
.type = MT_DEVICE,
},
+ {
+ .virtual = IO_HOST1X_VIRT,
+ .pfn = __phys_to_pfn(IO_HOST1X_PHYS),
+ .length = IO_HOST1X_SIZE,
+ .type = MT_DEVICE,
+ },
+ {
+ .virtual = IO_USB_VIRT,
+ .pfn = __phys_to_pfn(IO_USB_PHYS),
+ .length = IO_USB_SIZE,
+ .type = MT_DEVICE,
+ },
+ {
+ .virtual = IO_SDMMC_VIRT,
+ .pfn = __phys_to_pfn(IO_SDMMC_PHYS),
+ .length = IO_SDMMC_SIZE,
+ .type = MT_DEVICE,
+ },
};
void __init tegra_map_common_io(void)
@@ -67,8 +88,25 @@ void __init tegra_map_common_io(void)
void __iomem *tegra_ioremap(unsigned long p, size_t size, unsigned int type)
{
void __iomem *v = IO_ADDRESS(p);
- if (v == NULL)
- v = __arm_ioremap(p, size, type);
+
+ /*
+ * __arm_ioremap fails to set the domain of ioremapped memory
+ * correctly, only use it on physical memory.
+ */
+ if (v == NULL) {
+ if ((p >= TEGRA_DRAM_BASE &&
+ (p + size) <= (TEGRA_DRAM_BASE + TEGRA_DRAM_SIZE)) ||
+ (p >= TEGRA_NOR_FLASH_BASE &&
+ (p + size) <= (TEGRA_NOR_FLASH_BASE + TEGRA_NOR_FLASH_SIZE)))
+ v = __arm_ioremap(p, size, type);
+ }
+
+ /*
+ * If the physical address was not physical memory or statically
+ * mapped, there's nothing we can do to map it safely.
+ */
+ BUG_ON(v == NULL);
+
return v;
}
EXPORT_SYMBOL(tegra_ioremap);
diff --git a/arch/arm/mach-tegra/iovmm-gart.c b/arch/arm/mach-tegra/iovmm-gart.c
new file mode 100644
index 000000000000..954b49552634
--- /dev/null
+++ b/arch/arm/mach-tegra/iovmm-gart.c
@@ -0,0 +1,352 @@
+/*
+ * arch/arm/mach-tegra/iovmm-gart.c
+ *
+ * Tegra I/O VMM implementation for GART devices in Tegra and Tegra 2 series
+ * systems-on-a-chip.
+ *
+ * Copyright (c) 2010-2011, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/mm.h>
+#include <linux/io.h>
+
+#include <asm/cacheflush.h>
+
+#include <mach/iovmm.h>
+
+#define GART_CONFIG 0x24
+#define GART_ENTRY_ADDR 0x28
+#define GART_ENTRY_DATA 0x2c
+
+#define VMM_NAME "iovmm-gart"
+#define DRIVER_NAME "tegra_gart"
+
+#define GART_PAGE_SHIFT (12)
+#define GART_PAGE_MASK (~((1<<GART_PAGE_SHIFT)-1))
+
+struct gart_device {
+ void __iomem *regs;
+ u32 *savedata;
+ u32 page_count; /* total remappable size */
+ tegra_iovmm_addr_t iovmm_base; /* offset to apply to vmm_area */
+ spinlock_t pte_lock;
+ struct tegra_iovmm_device iovmm;
+ struct tegra_iovmm_domain domain;
+ bool enable;
+};
+
+static int gart_map(struct tegra_iovmm_domain *, struct tegra_iovmm_area *);
+static void gart_unmap(struct tegra_iovmm_domain *,
+ struct tegra_iovmm_area *, bool);
+static void gart_map_pfn(struct tegra_iovmm_domain *,
+ struct tegra_iovmm_area *, tegra_iovmm_addr_t, unsigned long);
+static struct tegra_iovmm_domain *gart_alloc_domain(
+ struct tegra_iovmm_device *, struct tegra_iovmm_client *);
+
+static int gart_probe(struct platform_device *);
+static int gart_remove(struct platform_device *);
+static int gart_suspend(struct tegra_iovmm_device *dev);
+static void gart_resume(struct tegra_iovmm_device *dev);
+
+
+static struct tegra_iovmm_device_ops tegra_iovmm_gart_ops = {
+ .map = gart_map,
+ .unmap = gart_unmap,
+ .map_pfn = gart_map_pfn,
+ .alloc_domain = gart_alloc_domain,
+ .suspend = gart_suspend,
+ .resume = gart_resume,
+};
+
+static struct platform_driver tegra_iovmm_gart_drv = {
+ .probe = gart_probe,
+ .remove = gart_remove,
+ .driver = {
+ .name = DRIVER_NAME,
+ },
+};
+
+static int gart_suspend(struct tegra_iovmm_device *dev)
+{
+ struct gart_device *gart = container_of(dev, struct gart_device, iovmm);
+ unsigned int i;
+ unsigned long reg;
+
+ if (!gart)
+ return -ENODEV;
+
+ if (!gart->enable)
+ return 0;
+
+ spin_lock(&gart->pte_lock);
+ reg = gart->iovmm_base;
+ for (i = 0; i < gart->page_count; i++) {
+ writel(reg, gart->regs + GART_ENTRY_ADDR);
+ gart->savedata[i] = readl(gart->regs + GART_ENTRY_DATA);
+ dmb();
+ reg += 1 << GART_PAGE_SHIFT;
+ }
+ spin_unlock(&gart->pte_lock);
+ return 0;
+}
+
+static void do_gart_setup(struct gart_device *gart, const u32 *data)
+{
+ unsigned long reg;
+ unsigned int i;
+
+ writel(1, gart->regs + GART_CONFIG);
+
+ reg = gart->iovmm_base;
+ for (i = 0; i < gart->page_count; i++) {
+ writel(reg, gart->regs + GART_ENTRY_ADDR);
+ writel((data) ? data[i] : 0, gart->regs + GART_ENTRY_DATA);
+ wmb();
+ reg += 1 << GART_PAGE_SHIFT;
+ }
+ wmb();
+}
+
+static void gart_resume(struct tegra_iovmm_device *dev)
+{
+ struct gart_device *gart = container_of(dev, struct gart_device, iovmm);
+
+ if (!gart || !gart->enable || (gart->enable && !gart->savedata))
+ return;
+
+ spin_lock(&gart->pte_lock);
+ do_gart_setup(gart, gart->savedata);
+ spin_unlock(&gart->pte_lock);
+}
+
+static int gart_remove(struct platform_device *pdev)
+{
+ struct gart_device *gart = platform_get_drvdata(pdev);
+
+ if (!gart)
+ return 0;
+
+ if (gart->enable)
+ writel(0, gart->regs + GART_CONFIG);
+
+ gart->enable = 0;
+ platform_set_drvdata(pdev, NULL);
+ tegra_iovmm_unregister(&gart->iovmm);
+ if (gart->savedata)
+ vfree(gart->savedata);
+ if (gart->regs)
+ iounmap(gart->regs);
+ kfree(gart);
+ return 0;
+}
+
+static int gart_probe(struct platform_device *pdev)
+{
+ struct gart_device *gart;
+ struct resource *res, *res_remap;
+ void __iomem *gart_regs;
+ int e;
+
+ if (!pdev) {
+ pr_err(DRIVER_NAME ": platform_device required\n");
+ return -ENODEV;
+ }
+
+ if (PAGE_SHIFT != GART_PAGE_SHIFT) {
+ pr_err(DRIVER_NAME ": GART and CPU page size must match\n");
+ return -ENXIO;
+ }
+
+ /* the GART memory aperture is required */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ res_remap = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+
+ if (!res || !res_remap) {
+ pr_err(DRIVER_NAME ": GART memory aperture expected\n");
+ return -ENXIO;
+ }
+ gart = kzalloc(sizeof(*gart), GFP_KERNEL);
+ if (!gart) {
+ pr_err(DRIVER_NAME ": failed to allocate tegra_iovmm_device\n");
+ return -ENOMEM;
+ }
+
+ gart_regs = ioremap_wc(res->start, res->end - res->start + 1);
+ if (!gart_regs) {
+ pr_err(DRIVER_NAME ": failed to remap GART registers\n");
+ e = -ENXIO;
+ goto fail;
+ }
+
+ gart->iovmm.name = VMM_NAME;
+ gart->iovmm.ops = &tegra_iovmm_gart_ops;
+ gart->iovmm.pgsize_bits = GART_PAGE_SHIFT;
+ spin_lock_init(&gart->pte_lock);
+
+ platform_set_drvdata(pdev, gart);
+
+ e = tegra_iovmm_register(&gart->iovmm);
+ if (e)
+ goto fail;
+
+ e = tegra_iovmm_domain_init(&gart->domain, &gart->iovmm,
+ (tegra_iovmm_addr_t)res_remap->start,
+ (tegra_iovmm_addr_t)res_remap->end+1);
+ if (e)
+ goto fail;
+
+ gart->regs = gart_regs;
+ gart->iovmm_base = (tegra_iovmm_addr_t)res_remap->start;
+ gart->page_count = resource_size(res_remap);
+ gart->page_count >>= GART_PAGE_SHIFT;
+
+ gart->savedata = vmalloc(sizeof(u32) * gart->page_count);
+ if (!gart->savedata) {
+ pr_err(DRIVER_NAME ": failed to allocate context save area\n");
+ e = -ENOMEM;
+ goto fail;
+ }
+
+ spin_lock(&gart->pte_lock);
+
+ do_gart_setup(gart, NULL);
+ gart->enable = 1;
+
+ spin_unlock(&gart->pte_lock);
+ return 0;
+
+fail:
+ if (gart_regs)
+ iounmap(gart_regs);
+ if (gart && gart->savedata)
+ vfree(gart->savedata);
+ kfree(gart);
+ return e;
+}
+
+static int __devinit gart_init(void)
+{
+ return platform_driver_register(&tegra_iovmm_gart_drv);
+}
+
+static void __exit gart_exit(void)
+{
+ platform_driver_unregister(&tegra_iovmm_gart_drv);
+}
+
+#define GART_PTE(_pfn) (0x80000000ul | ((_pfn)<<PAGE_SHIFT))
+
+
+static int gart_map(struct tegra_iovmm_domain *domain,
+ struct tegra_iovmm_area *iovma)
+{
+ struct gart_device *gart =
+ container_of(domain, struct gart_device, domain);
+ unsigned long gart_page, count;
+ unsigned int i;
+
+ gart_page = iovma->iovm_start;
+ count = iovma->iovm_length >> GART_PAGE_SHIFT;
+
+ for (i = 0; i < count; i++) {
+ unsigned long pfn;
+
+ pfn = iovma->ops->lock_makeresident(iovma, i<<PAGE_SHIFT);
+ if (!pfn_valid(pfn))
+ goto fail;
+
+ spin_lock(&gart->pte_lock);
+
+ writel(gart_page, gart->regs + GART_ENTRY_ADDR);
+ writel(GART_PTE(pfn), gart->regs + GART_ENTRY_DATA);
+ wmb();
+ gart_page += 1 << GART_PAGE_SHIFT;
+
+ spin_unlock(&gart->pte_lock);
+ }
+ wmb();
+ return 0;
+
+fail:
+ spin_lock(&gart->pte_lock);
+ while (i--) {
+ iovma->ops->release(iovma, i << PAGE_SHIFT);
+ gart_page -= 1 << GART_PAGE_SHIFT;
+ writel(gart_page, gart->regs + GART_ENTRY_ADDR);
+ writel(0, gart->regs + GART_ENTRY_DATA);
+ wmb();
+ }
+ spin_unlock(&gart->pte_lock);
+ wmb();
+ return -ENOMEM;
+}
+
+static void gart_unmap(struct tegra_iovmm_domain *domain,
+ struct tegra_iovmm_area *iovma, bool decommit)
+{
+ struct gart_device *gart =
+ container_of(domain, struct gart_device, domain);
+ unsigned long gart_page, count;
+ unsigned int i;
+
+ count = iovma->iovm_length >> GART_PAGE_SHIFT;
+ gart_page = iovma->iovm_start;
+
+ spin_lock(&gart->pte_lock);
+ for (i = 0; i < count; i++) {
+ if (iovma->ops && iovma->ops->release)
+ iovma->ops->release(iovma, i << PAGE_SHIFT);
+
+ writel(gart_page, gart->regs + GART_ENTRY_ADDR);
+ writel(0, gart->regs + GART_ENTRY_DATA);
+ wmb();
+ gart_page += 1 << GART_PAGE_SHIFT;
+ }
+ spin_unlock(&gart->pte_lock);
+ wmb();
+}
+
+static void gart_map_pfn(struct tegra_iovmm_domain *domain,
+ struct tegra_iovmm_area *iovma, tegra_iovmm_addr_t offs,
+ unsigned long pfn)
+{
+ struct gart_device *gart =
+ container_of(domain, struct gart_device, domain);
+
+ BUG_ON(!pfn_valid(pfn));
+ spin_lock(&gart->pte_lock);
+ writel(offs, gart->regs + GART_ENTRY_ADDR);
+ writel(GART_PTE(pfn), gart->regs + GART_ENTRY_DATA);
+ wmb();
+ spin_unlock(&gart->pte_lock);
+ wmb();
+}
+
+static struct tegra_iovmm_domain *gart_alloc_domain(
+ struct tegra_iovmm_device *dev, struct tegra_iovmm_client *client)
+{
+ struct gart_device *gart = container_of(dev, struct gart_device, iovmm);
+ return &gart->domain;
+}
+
+subsys_initcall(gart_init);
+module_exit(gart_exit);
diff --git a/arch/arm/mach-tegra/iovmm-smmu.c b/arch/arm/mach-tegra/iovmm-smmu.c
new file mode 100644
index 000000000000..b1ecdd0e83e1
--- /dev/null
+++ b/arch/arm/mach-tegra/iovmm-smmu.c
@@ -0,0 +1,1359 @@
+/*
+ * arch/arm/mach-tegra/iovmm-smmu.c
+ *
+ * Tegra I/O VMM implementation for SMMU devices for Tegra 3 series
+ * systems-on-a-chip.
+ *
+ * Copyright (c) 2010-2011, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+#include <linux/sysfs.h>
+#include <linux/device.h>
+#include <linux/sched.h>
+#include <linux/io.h>
+
+#include <asm/page.h>
+#include <asm/cacheflush.h>
+
+#include <mach/iovmm.h>
+#include <mach/iomap.h>
+
+#include "tegra_smmu.h"
+
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+/*
+ * ALL-CAP macros copied from armc.h
+ */
+#define MC_SMMU_CONFIG_0 0x10
+#define MC_SMMU_CONFIG_0_SMMU_ENABLE_DISABLE 0
+#define MC_SMMU_CONFIG_0_SMMU_ENABLE_ENABLE 1
+
+#define MC_SMMU_TLB_CONFIG_0 0x14
+#define MC_SMMU_TLB_CONFIG_0_TLB_STATS__MASK (1 << 31)
+#define MC_SMMU_TLB_CONFIG_0_TLB_STATS__ENABLE (1 << 31)
+#define MC_SMMU_TLB_CONFIG_0_TLB_HIT_UNDER_MISS__ENABLE (1 << 29)
+#define MC_SMMU_TLB_CONFIG_0_TLB_ACTIVE_LINES__VALUE 0x10
+#define MC_SMMU_TLB_CONFIG_0_RESET_VAL 0x20000010
+
+#define MC_SMMU_PTC_CONFIG_0 0x18
+#define MC_SMMU_PTC_CONFIG_0_PTC_STATS__MASK (1 << 31)
+#define MC_SMMU_PTC_CONFIG_0_PTC_STATS__ENABLE (1 << 31)
+#define MC_SMMU_PTC_CONFIG_0_PTC_CACHE__ENABLE (1 << 29)
+#define MC_SMMU_PTC_CONFIG_0_PTC_INDEX_MAP__PATTERN 0x3f
+#define MC_SMMU_PTC_CONFIG_0_RESET_VAL 0x2000003f
+
+#define MC_SMMU_PTB_ASID_0 0x1c
+#define MC_SMMU_PTB_ASID_0_CURRENT_ASID_SHIFT 0
+
+#define MC_SMMU_PTB_DATA_0 0x20
+#define MC_SMMU_PTB_DATA_0_RESET_VAL 0
+#define MC_SMMU_PTB_DATA_0_ASID_NONSECURE_SHIFT 29
+#define MC_SMMU_PTB_DATA_0_ASID_WRITABLE_SHIFT 30
+#define MC_SMMU_PTB_DATA_0_ASID_READABLE_SHIFT 31
+
+#define MC_SMMU_TLB_FLUSH_0 0x30
+#define MC_SMMU_TLB_FLUSH_0_TLB_FLUSH_VA_MATCH_ALL 0
+#define MC_SMMU_TLB_FLUSH_0_TLB_FLUSH_VA_MATCH_SECTION 2
+#define MC_SMMU_TLB_FLUSH_0_TLB_FLUSH_VA_MATCH_GROUP 3
+#define MC_SMMU_TLB_FLUSH_0_TLB_FLUSH_ASID_SHIFT 29
+#define MC_SMMU_TLB_FLUSH_0_TLB_FLUSH_ASID_MATCH_DISABLE 0
+#define MC_SMMU_TLB_FLUSH_0_TLB_FLUSH_ASID_MATCH_ENABLE 1
+#define MC_SMMU_TLB_FLUSH_0_TLB_FLUSH_ASID_MATCH_SHIFT 31
+
+#define MC_SMMU_PTC_FLUSH_0 0x34
+#define MC_SMMU_PTC_FLUSH_0_PTC_FLUSH_TYPE_ALL 0
+#define MC_SMMU_PTC_FLUSH_0_PTC_FLUSH_TYPE_ADR 1
+#define MC_SMMU_PTC_FLUSH_0_PTC_FLUSH_ADR_SHIFT 4
+
+#define MC_SMMU_ASID_SECURITY_0 0x38
+
+#define MC_SMMU_STATS_TLB_HIT_COUNT_0 0x1f0
+#define MC_SMMU_STATS_TLB_MISS_COUNT_0 0x1f4
+#define MC_SMMU_STATS_PTC_HIT_COUNT_0 0x1f8
+#define MC_SMMU_STATS_PTC_MISS_COUNT_0 0x1fc
+
+#define MC_SMMU_TRANSLATION_ENABLE_0_0 0x228
+#define MC_SMMU_TRANSLATION_ENABLE_1_0 0x22c
+#define MC_SMMU_TRANSLATION_ENABLE_2_0 0x230
+
+#define MC_SMMU_AFI_ASID_0 0x238 /* PCIE */
+#define MC_SMMU_AVPC_ASID_0 0x23c /* AVP */
+#define MC_SMMU_DC_ASID_0 0x240 /* Display controller */
+#define MC_SMMU_DCB_ASID_0 0x244 /* Display controller B */
+#define MC_SMMU_EPP_ASID_0 0x248 /* Encoder pre-processor */
+#define MC_SMMU_G2_ASID_0 0x24c /* 2D engine */
+#define MC_SMMU_HC_ASID_0 0x250 /* Host1x */
+#define MC_SMMU_HDA_ASID_0 0x254 /* High-def audio */
+#define MC_SMMU_ISP_ASID_0 0x258 /* Image signal processor */
+#define MC_SMMU_MPE_ASID_0 0x264 /* MPEG encoder */
+#define MC_SMMU_NV_ASID_0 0x268 /* (3D) */
+#define MC_SMMU_NV2_ASID_0 0x26c /* (3D) */
+#define MC_SMMU_PPCS_ASID_0 0x270 /* AHB */
+#define MC_SMMU_SATA_ASID_0 0x278 /* SATA */
+#define MC_SMMU_VDE_ASID_0 0x27c /* Video decoder */
+#define MC_SMMU_VI_ASID_0 0x280 /* Video input */
+
+#define SMMU_PDE_NEXT_SHIFT 28
+
+/* Copied from arahb_arbc.h */
+#define AHB_ARBITRATION_XBAR_CTRL_0 0xe0
+#define AHB_ARBITRATION_XBAR_CTRL_0_SMMU_INIT_DONE_DONE 1
+#define AHB_ARBITRATION_XBAR_CTRL_0_SMMU_INIT_DONE_SHIFT 17
+
+#endif
+
+#define MC_SMMU_NUM_ASIDS 4
+#define MC_SMMU_TLB_FLUSH_0_TLB_FLUSH_VA_SECTION__MASK 0xffc00000
+#define MC_SMMU_TLB_FLUSH_0_TLB_FLUSH_VA_SECTION__SHIFT 12 /* right shift */
+#define MC_SMMU_TLB_FLUSH_0_TLB_FLUSH_VA_GROUP__MASK 0xffffc000
+#define MC_SMMU_TLB_FLUSH_0_TLB_FLUSH_VA_GROUP__SHIFT 12 /* right shift */
+#define MC_SMMU_TLB_FLUSH_0_TLB_FLUSH_VA(iova, which) \
+ ((((iova) & MC_SMMU_TLB_FLUSH_0_TLB_FLUSH_VA_##which##__MASK) >> \
+ MC_SMMU_TLB_FLUSH_0_TLB_FLUSH_VA_##which##__SHIFT) | \
+ MC_SMMU_TLB_FLUSH_0_TLB_FLUSH_VA_MATCH_##which)
+#define MC_SMMU_PTB_ASID_0_CURRENT_ASID(n) \
+ ((n) << MC_SMMU_PTB_ASID_0_CURRENT_ASID_SHIFT)
+#define MC_SMMU_TLB_FLUSH_0_TLB_FLUSH_ASID_MATCH_disable \
+ (MC_SMMU_TLB_FLUSH_0_TLB_FLUSH_ASID_MATCH_DISABLE << \
+ MC_SMMU_TLB_FLUSH_0_TLB_FLUSH_ASID_MATCH_SHIFT)
+#define MC_SMMU_TLB_FLUSH_0_TLB_FLUSH_ASID_MATCH__ENABLE \
+ (MC_SMMU_TLB_FLUSH_0_TLB_FLUSH_ASID_MATCH_ENABLE << \
+ MC_SMMU_TLB_FLUSH_0_TLB_FLUSH_ASID_MATCH_SHIFT)
+
+#define VMM_NAME "iovmm-smmu"
+#define DRIVER_NAME "tegra_smmu"
+
+#define SMMU_PAGE_SHIFT 12
+#define SMMU_PAGE_SIZE (1 << SMMU_PAGE_SHIFT)
+
+#define SMMU_PDIR_COUNT 1024
+#define SMMU_PDIR_SIZE (sizeof(unsigned long) * SMMU_PDIR_COUNT)
+#define SMMU_PTBL_COUNT 1024
+#define SMMU_PTBL_SIZE (sizeof(unsigned long) * SMMU_PTBL_COUNT)
+#define SMMU_PDIR_SHIFT 12
+#define SMMU_PDE_SHIFT 12
+#define SMMU_PTE_SHIFT 12
+#define SMMU_PFN_MASK 0x000fffff
+
+#define SMMU_ADDR_TO_PFN(addr) ((addr) >> 12)
+#define SMMU_ADDR_TO_PDN(addr) ((addr) >> 22)
+#define SMMU_PDN_TO_ADDR(addr) ((pdn) << 22)
+
+#define _READABLE (1 << MC_SMMU_PTB_DATA_0_ASID_READABLE_SHIFT)
+#define _WRITABLE (1 << MC_SMMU_PTB_DATA_0_ASID_WRITABLE_SHIFT)
+#define _NONSECURE (1 << MC_SMMU_PTB_DATA_0_ASID_NONSECURE_SHIFT)
+#define _PDE_NEXT (1 << SMMU_PDE_NEXT_SHIFT)
+#define _MASK_ATTR (_READABLE | _WRITABLE | _NONSECURE)
+
+#define _PDIR_ATTR (_READABLE | _WRITABLE | _NONSECURE)
+
+#define _PDE_ATTR (_READABLE | _WRITABLE | _NONSECURE)
+#define _PDE_ATTR_N (_PDE_ATTR | _PDE_NEXT)
+#define _PDE_VACANT(pdn) (((pdn) << 10) | _PDE_ATTR)
+
+#define _PTE_ATTR (_READABLE | _WRITABLE | _NONSECURE)
+#define _PTE_VACANT(addr) (((addr) >> SMMU_PAGE_SHIFT) | _PTE_ATTR)
+
+#define SMMU_MK_PDIR(page, attr) \
+ ((page_to_phys(page) >> SMMU_PDIR_SHIFT) | (attr))
+#define SMMU_MK_PDE(page, attr) \
+ (unsigned long)((page_to_phys(page) >> SMMU_PDE_SHIFT) | (attr))
+#define SMMU_EX_PTBL_PAGE(pde) \
+ pfn_to_page((unsigned long)(pde) & SMMU_PFN_MASK)
+#define SMMU_PFN_TO_PTE(pfn, attr) (unsigned long)((pfn) | (attr))
+
+#define SMMU_ASID_ENABLE(asid) ((asid) | (1 << 31))
+#define SMMU_ASID_DISABLE 0
+#define SMMU_ASID_ASID(n) ((n) & ~SMMU_ASID_ENABLE(0))
+
+/* Keep this as a "natural" enumeration (no assignments) */
+enum smmu_hwclient {
+ HWC_AFI,
+ HWC_AVPC,
+ HWC_DC,
+ HWC_DCB,
+ HWC_EPP,
+ HWC_G2,
+ HWC_HC,
+ HWC_HDA,
+ HWC_ISP,
+ HWC_MPE,
+ HWC_NV,
+ HWC_NV2,
+ HWC_PPCS,
+ HWC_SATA,
+ HWC_VDE,
+ HWC_VI,
+
+ HWC_COUNT
+};
+
+struct smmu_hwc_state {
+ unsigned long reg;
+ unsigned long enable_disable;
+};
+
+/* Hardware client mapping initializer */
+#define HWC_INIT(client) \
+ [HWC_##client] = {MC_SMMU_##client##_ASID_0, SMMU_ASID_DISABLE},
+
+static const struct smmu_hwc_state smmu_hwc_state_init[] = {
+ HWC_INIT(AFI)
+ HWC_INIT(AVPC)
+ HWC_INIT(DC)
+ HWC_INIT(DCB)
+ HWC_INIT(EPP)
+ HWC_INIT(G2)
+ HWC_INIT(HC)
+ HWC_INIT(HDA)
+ HWC_INIT(ISP)
+ HWC_INIT(MPE)
+ HWC_INIT(NV)
+ HWC_INIT(NV2)
+ HWC_INIT(PPCS)
+ HWC_INIT(SATA)
+ HWC_INIT(VDE)
+ HWC_INIT(VI)
+};
+
+
+struct domain_hwc_map {
+ const char *dev_name;
+ const enum smmu_hwclient *hwcs;
+ const unsigned int nr_hwcs;
+};
+
+/* Enable all hardware clients for SMMU translation */
+static const enum smmu_hwclient nvmap_hwcs[] = {
+ HWC_AFI,
+ HWC_AVPC,
+ HWC_DC,
+ HWC_DCB,
+ HWC_EPP,
+ HWC_G2,
+ HWC_HC,
+ HWC_HDA,
+ HWC_ISP,
+ HWC_MPE,
+ HWC_NV,
+ HWC_NV2,
+ HWC_PPCS,
+ HWC_SATA,
+ HWC_VDE,
+ HWC_VI
+};
+
+static const struct domain_hwc_map smmu_hwc_map[] = {
+ {
+ .dev_name = "nvmap",
+ .hwcs = nvmap_hwcs,
+ .nr_hwcs = ARRAY_SIZE(nvmap_hwcs),
+ },
+};
+
+/*
+ * Per address space
+ */
+struct smmu_as {
+ struct smmu_device *smmu; /* back pointer to container */
+ unsigned int asid;
+ const struct domain_hwc_map *hwclients;
+ struct mutex lock; /* for pagetable */
+ struct tegra_iovmm_domain domain;
+ struct page *pdir_page;
+ unsigned long pdir_attr;
+ unsigned long pde_attr;
+ unsigned long pte_attr;
+ unsigned int *pte_count;
+ struct device sysfs_dev;
+ int sysfs_use_count;
+};
+
+/*
+ * Per SMMU device
+ */
+struct smmu_device {
+ void __iomem *regs, *regs_ahbarb;
+ tegra_iovmm_addr_t iovmm_base; /* remappable base address */
+ unsigned long page_count; /* total remappable size */
+ spinlock_t lock;
+ char *name;
+ struct tegra_iovmm_device iovmm_dev;
+ int num_ases;
+ struct smmu_as *as; /* Run-time allocated array */
+ struct smmu_hwc_state hwc_state[HWC_COUNT];
+ struct device sysfs_dev;
+ int sysfs_use_count;
+ bool enable;
+ struct page *avp_vector_page; /* dummy page shared by all AS's */
+
+ /*
+ * Register image savers for suspend/resume
+ */
+ unsigned long translation_enable_0_0;
+ unsigned long translation_enable_1_0;
+ unsigned long translation_enable_2_0;
+ unsigned long asid_security_0;
+
+ unsigned long lowest_asid; /* Variables for hardware testing */
+ unsigned long debug_asid;
+ unsigned long signature_pid; /* For debugging aid */
+};
+
+#define VA_PAGE_TO_PA(va, page) \
+ (page_to_phys(page) + ((unsigned long)(va) & ~PAGE_MASK))
+
+#define FLUSH_CPU_DCACHE(va, page, size) \
+ do { \
+ unsigned long _pa_ = VA_PAGE_TO_PA(va, page); \
+ __cpuc_flush_dcache_area((void *)(va), (size_t)(size)); \
+ outer_flush_range(_pa_, _pa_+(size_t)(size)); \
+ } while (0)
+
+#define FLUSH_SMMU_REGS(smmu) \
+ do { wmb(); (void)readl((smmu)->regs + MC_SMMU_CONFIG_0); } while (0)
+
+/*
+ * Flush all TLB entries and all PTC entries
+ * Caller must lock smmu
+ */
+static void smmu_flush_regs(struct smmu_device *smmu, int enable)
+{
+ writel(MC_SMMU_PTC_FLUSH_0_PTC_FLUSH_TYPE_ALL,
+ smmu->regs + MC_SMMU_PTC_FLUSH_0);
+ FLUSH_SMMU_REGS(smmu);
+ writel(MC_SMMU_TLB_FLUSH_0_TLB_FLUSH_VA_MATCH_ALL |
+ MC_SMMU_TLB_FLUSH_0_TLB_FLUSH_ASID_MATCH_disable,
+ smmu->regs + MC_SMMU_TLB_FLUSH_0);
+
+ if (enable)
+ writel(MC_SMMU_CONFIG_0_SMMU_ENABLE_ENABLE,
+ smmu->regs + MC_SMMU_CONFIG_0);
+ FLUSH_SMMU_REGS(smmu);
+}
+
+static void smmu_setup_regs(struct smmu_device *smmu)
+{
+ int i;
+
+ if (smmu->as) {
+ int asid;
+
+ /* Set/restore page directory for each AS */
+ for (asid = 0; asid < smmu->num_ases; asid++) {
+ struct smmu_as *as = &smmu->as[asid];
+
+ writel(MC_SMMU_PTB_ASID_0_CURRENT_ASID(as->asid),
+ as->smmu->regs + MC_SMMU_PTB_ASID_0);
+ writel(as->pdir_page
+ ? SMMU_MK_PDIR(as->pdir_page, as->pdir_attr)
+ : MC_SMMU_PTB_DATA_0_RESET_VAL,
+ as->smmu->regs + MC_SMMU_PTB_DATA_0);
+ }
+ }
+
+ /* Set/restore ASID for each hardware client */
+ for (i = 0; i < HWC_COUNT; i++) {
+ struct smmu_hwc_state *hwcst = &smmu->hwc_state[i];
+ writel(hwcst->enable_disable, smmu->regs + hwcst->reg);
+ }
+
+ writel(smmu->translation_enable_0_0,
+ smmu->regs + MC_SMMU_TRANSLATION_ENABLE_0_0);
+ writel(smmu->translation_enable_1_0,
+ smmu->regs + MC_SMMU_TRANSLATION_ENABLE_1_0);
+ writel(smmu->translation_enable_2_0,
+ smmu->regs + MC_SMMU_TRANSLATION_ENABLE_2_0);
+ writel(smmu->asid_security_0,
+ smmu->regs + MC_SMMU_ASID_SECURITY_0);
+ writel(MC_SMMU_TLB_CONFIG_0_RESET_VAL,
+ smmu->regs + MC_SMMU_TLB_CONFIG_0);
+ writel(MC_SMMU_PTC_CONFIG_0_RESET_VAL,
+ smmu->regs + MC_SMMU_PTC_CONFIG_0);
+
+ smmu_flush_regs(smmu, 1);
+ writel(
+ readl(smmu->regs_ahbarb + AHB_ARBITRATION_XBAR_CTRL_0) |
+ (AHB_ARBITRATION_XBAR_CTRL_0_SMMU_INIT_DONE_DONE <<
+ AHB_ARBITRATION_XBAR_CTRL_0_SMMU_INIT_DONE_SHIFT),
+ smmu->regs_ahbarb + AHB_ARBITRATION_XBAR_CTRL_0);
+}
+
+static int smmu_suspend(struct tegra_iovmm_device *dev)
+{
+ struct smmu_device *smmu =
+ container_of(dev, struct smmu_device, iovmm_dev);
+
+ smmu->translation_enable_0_0 =
+ readl(smmu->regs + MC_SMMU_TRANSLATION_ENABLE_0_0);
+ smmu->translation_enable_1_0 =
+ readl(smmu->regs + MC_SMMU_TRANSLATION_ENABLE_1_0);
+ smmu->translation_enable_2_0 =
+ readl(smmu->regs + MC_SMMU_TRANSLATION_ENABLE_2_0);
+ smmu->asid_security_0 =
+ readl(smmu->regs + MC_SMMU_ASID_SECURITY_0);
+ return 0;
+}
+
+static void smmu_resume(struct tegra_iovmm_device *dev)
+{
+ struct smmu_device *smmu =
+ container_of(dev, struct smmu_device, iovmm_dev);
+
+ if (!smmu->enable)
+ return;
+
+ spin_lock(&smmu->lock);
+ smmu_setup_regs(smmu);
+ spin_unlock(&smmu->lock);
+}
+
+static void flush_ptc_and_tlb(struct smmu_device *smmu,
+ struct smmu_as *as, unsigned long iova,
+ unsigned long *pte, struct page *ptpage, int is_pde)
+{
+ unsigned long tlb_flush_va = is_pde
+ ? MC_SMMU_TLB_FLUSH_0_TLB_FLUSH_VA(iova, SECTION)
+ : MC_SMMU_TLB_FLUSH_0_TLB_FLUSH_VA(iova, GROUP);
+
+ writel(MC_SMMU_PTC_FLUSH_0_PTC_FLUSH_TYPE_ADR |
+ VA_PAGE_TO_PA(pte, ptpage),
+ smmu->regs + MC_SMMU_PTC_FLUSH_0);
+ FLUSH_SMMU_REGS(smmu);
+ writel(tlb_flush_va |
+ MC_SMMU_TLB_FLUSH_0_TLB_FLUSH_ASID_MATCH__ENABLE |
+ (as->asid << MC_SMMU_TLB_FLUSH_0_TLB_FLUSH_ASID_SHIFT),
+ smmu->regs + MC_SMMU_TLB_FLUSH_0);
+ FLUSH_SMMU_REGS(smmu);
+}
+
+static void free_ptbl(struct smmu_as *as, unsigned long iova)
+{
+ unsigned long pdn = SMMU_ADDR_TO_PDN(iova);
+ unsigned long *pdir = (unsigned long *)kmap(as->pdir_page);
+
+ if (pdir[pdn] != _PDE_VACANT(pdn)) {
+ pr_debug("%s:%d pdn=%lx\n", __func__, __LINE__, pdn);
+
+ ClearPageReserved(SMMU_EX_PTBL_PAGE(pdir[pdn]));
+ __free_page(SMMU_EX_PTBL_PAGE(pdir[pdn]));
+ pdir[pdn] = _PDE_VACANT(pdn);
+ FLUSH_CPU_DCACHE(&pdir[pdn], as->pdir_page, sizeof pdir[pdn]);
+ flush_ptc_and_tlb(as->smmu, as, iova, &pdir[pdn],
+ as->pdir_page, 1);
+ }
+ kunmap(as->pdir_page);
+}
+
+static void free_pdir(struct smmu_as *as)
+{
+ if (as->pdir_page) {
+ unsigned addr = as->smmu->iovmm_base;
+ int count = as->smmu->page_count;
+
+ while (count-- > 0) {
+ free_ptbl(as, addr);
+ addr += SMMU_PAGE_SIZE * SMMU_PTBL_COUNT;
+ }
+ ClearPageReserved(as->pdir_page);
+ __free_page(as->pdir_page);
+ as->pdir_page = NULL;
+ kfree(as->pte_count);
+ as->pte_count = NULL;
+ }
+}
+
+static int smmu_remove(struct platform_device *pdev)
+{
+ struct smmu_device *smmu = platform_get_drvdata(pdev);
+
+ if (!smmu)
+ return 0;
+
+ if (smmu->enable) {
+ writel(MC_SMMU_CONFIG_0_SMMU_ENABLE_DISABLE,
+ smmu->regs + MC_SMMU_CONFIG_0);
+ smmu->enable = 0;
+ }
+ platform_set_drvdata(pdev, NULL);
+
+ if (smmu->as) {
+ int asid;
+
+ for (asid = 0; asid < smmu->num_ases; asid++)
+ free_pdir(&smmu->as[asid]);
+ kfree(smmu->as);
+ }
+
+ if (smmu->avp_vector_page)
+ __free_page(smmu->avp_vector_page);
+ if (smmu->regs)
+ iounmap(smmu->regs);
+ if (smmu->regs_ahbarb)
+ iounmap(smmu->regs_ahbarb);
+ tegra_iovmm_unregister(&smmu->iovmm_dev);
+ kfree(smmu);
+ return 0;
+}
+
+/*
+ * Maps PTBL for given iova and returns the PTE address
+ * Caller must unmap the mapped PTBL returned in *ptbl_page_p
+ */
+static unsigned long *locate_pte(struct smmu_as *as,
+ unsigned long iova, bool allocate,
+ struct page **ptbl_page_p,
+ unsigned int **pte_counter)
+{
+ unsigned long ptn = SMMU_ADDR_TO_PFN(iova);
+ unsigned long pdn = SMMU_ADDR_TO_PDN(iova);
+ unsigned long *pdir = kmap(as->pdir_page);
+ unsigned long *ptbl;
+
+ if (pdir[pdn] != _PDE_VACANT(pdn)) {
+ /* Mapped entry table already exists */
+ *ptbl_page_p = SMMU_EX_PTBL_PAGE(pdir[pdn]);
+ ptbl = kmap(*ptbl_page_p);
+ } else if (!allocate) {
+ kunmap(as->pdir_page);
+ return NULL;
+ } else {
+ /* Vacant - allocate a new page table */
+ pr_debug("%s:%d new PTBL pdn=%lx\n", __func__, __LINE__, pdn);
+
+ *ptbl_page_p = alloc_page(GFP_KERNEL | __GFP_DMA);
+ if (!*ptbl_page_p) {
+ kunmap(as->pdir_page);
+ pr_err(DRIVER_NAME
+ ": failed to allocate tegra_iovmm_device page table\n");
+ return NULL;
+ }
+ SetPageReserved(*ptbl_page_p);
+ ptbl = (unsigned long *)kmap(*ptbl_page_p);
+ {
+ int pn;
+ unsigned long addr = SMMU_PDN_TO_ADDR(pdn);
+ for (pn = 0; pn < SMMU_PTBL_COUNT;
+ pn++, addr += SMMU_PAGE_SIZE) {
+ ptbl[pn] = _PTE_VACANT(addr);
+ }
+ }
+ FLUSH_CPU_DCACHE(ptbl, *ptbl_page_p, SMMU_PTBL_SIZE);
+ pdir[pdn] = SMMU_MK_PDE(*ptbl_page_p,
+ as->pde_attr | _PDE_NEXT);
+ FLUSH_CPU_DCACHE(&pdir[pdn], as->pdir_page, sizeof pdir[pdn]);
+ flush_ptc_and_tlb(as->smmu, as, iova, &pdir[pdn],
+ as->pdir_page, 1);
+ }
+ *pte_counter = &as->pte_count[pdn];
+
+ kunmap(as->pdir_page);
+ return &ptbl[ptn % SMMU_PTBL_COUNT];
+}
+
+static void put_signature(struct smmu_as *as,
+ unsigned long addr, unsigned long pfn)
+{
+ if (as->smmu->signature_pid == current->pid) {
+ struct page *page = pfn_to_page(pfn);
+ unsigned long *vaddr = kmap(page);
+ if (vaddr) {
+ vaddr[0] = addr;
+ vaddr[1] = pfn << PAGE_SHIFT;
+ FLUSH_CPU_DCACHE(vaddr, page, sizeof(vaddr[0]) * 2);
+ kunmap(page);
+ }
+ }
+}
+
+static int smmu_map(struct tegra_iovmm_domain *domain,
+ struct tegra_iovmm_area *iovma)
+{
+ struct smmu_as *as = container_of(domain, struct smmu_as, domain);
+ unsigned long addr = iovma->iovm_start;
+ unsigned long pcount = iovma->iovm_length >> SMMU_PAGE_SHIFT;
+ int i;
+
+ pr_debug("%s:%d iova=%lx asid=%d\n", __func__, __LINE__,
+ addr, as - as->smmu->as);
+
+ for (i = 0; i < pcount; i++) {
+ unsigned long pfn;
+ unsigned long *pte;
+ unsigned int *pte_counter;
+ struct page *ptpage;
+
+ pfn = iovma->ops->lock_makeresident(iovma, i << PAGE_SHIFT);
+ if (!pfn_valid(pfn))
+ goto fail;
+
+ mutex_lock(&as->lock);
+
+ pte = locate_pte(as, addr, true, &ptpage, &pte_counter);
+ if (!pte)
+ goto fail2;
+
+ pr_debug("%s:%d iova=%lx pfn=%lx asid=%d\n",
+ __func__, __LINE__, addr, pfn, as - as->smmu->as);
+
+ if (*pte == _PTE_VACANT(addr))
+ (*pte_counter)++;
+ *pte = SMMU_PFN_TO_PTE(pfn, as->pte_attr);
+ if (unlikely((*pte == _PTE_VACANT(addr))))
+ (*pte_counter)--;
+ FLUSH_CPU_DCACHE(pte, ptpage, sizeof *pte);
+ flush_ptc_and_tlb(as->smmu, as, addr, pte, ptpage, 0);
+ kunmap(ptpage);
+ mutex_unlock(&as->lock);
+ put_signature(as, addr, pfn);
+ addr += SMMU_PAGE_SIZE;
+ }
+ return 0;
+
+fail:
+ mutex_lock(&as->lock);
+fail2:
+
+ while (i-- > 0) {
+ unsigned long *pte;
+ unsigned int *pte_counter;
+ struct page *page;
+
+ iovma->ops->release(iovma, i<<PAGE_SHIFT);
+ addr -= SMMU_PAGE_SIZE;
+ pte = locate_pte(as, addr, false, &page, &pte_counter);
+ if (pte) {
+ if (*pte != _PTE_VACANT(addr)) {
+ *pte = _PTE_VACANT(addr);
+ FLUSH_CPU_DCACHE(pte, page, sizeof *pte);
+ flush_ptc_and_tlb(as->smmu, as, addr, pte,
+ page, 0);
+ kunmap(page);
+ if (!--(*pte_counter))
+ free_ptbl(as, addr);
+ } else {
+ kunmap(page);
+ }
+ }
+ }
+ mutex_unlock(&as->lock);
+ return -ENOMEM;
+}
+
+static void smmu_unmap(struct tegra_iovmm_domain *domain,
+ struct tegra_iovmm_area *iovma, bool decommit)
+{
+ struct smmu_as *as = container_of(domain, struct smmu_as, domain);
+ unsigned long addr = iovma->iovm_start;
+ unsigned int pcount = iovma->iovm_length >> SMMU_PAGE_SHIFT;
+ unsigned int i, *pte_counter;
+
+ pr_debug("%s:%d iova=%lx asid=%d\n", __func__, __LINE__,
+ addr, as - as->smmu->as);
+
+ mutex_lock(&as->lock);
+ for (i = 0; i < pcount; i++) {
+ unsigned long *pte;
+ struct page *page;
+
+ if (iovma->ops && iovma->ops->release)
+ iovma->ops->release(iovma, i << PAGE_SHIFT);
+
+ pte = locate_pte(as, addr, false, &page, &pte_counter);
+ if (pte) {
+ if (*pte != _PTE_VACANT(addr)) {
+ *pte = _PTE_VACANT(addr);
+ FLUSH_CPU_DCACHE(pte, page, sizeof *pte);
+ flush_ptc_and_tlb(as->smmu, as, addr, pte,
+ page, 0);
+ kunmap(page);
+ if (!--(*pte_counter) && decommit) {
+ free_ptbl(as, addr);
+ smmu_flush_regs(as->smmu, 0);
+ }
+ }
+ }
+ addr += SMMU_PAGE_SIZE;
+ }
+ mutex_unlock(&as->lock);
+}
+
+static void smmu_map_pfn(struct tegra_iovmm_domain *domain,
+ struct tegra_iovmm_area *iovma, tegra_iovmm_addr_t addr,
+ unsigned long pfn)
+{
+ struct smmu_as *as = container_of(domain, struct smmu_as, domain);
+ struct smmu_device *smmu = as->smmu;
+ unsigned long *pte;
+ unsigned int *pte_counter;
+ struct page *ptpage;
+
+ pr_debug("%s:%d iova=%lx pfn=%lx asid=%d\n", __func__, __LINE__,
+ (unsigned long)addr, pfn, as - as->smmu->as);
+
+ BUG_ON(!pfn_valid(pfn));
+ mutex_lock(&as->lock);
+ pte = locate_pte(as, addr, true, &ptpage, &pte_counter);
+ if (pte) {
+ if (*pte == _PTE_VACANT(addr))
+ (*pte_counter)++;
+ *pte = SMMU_PFN_TO_PTE(pfn, as->pte_attr);
+ if (unlikely((*pte == _PTE_VACANT(addr))))
+ (*pte_counter)--;
+ FLUSH_CPU_DCACHE(pte, ptpage, sizeof *pte);
+ flush_ptc_and_tlb(smmu, as, addr, pte, ptpage, 0);
+ kunmap(ptpage);
+ put_signature(as, addr, pfn);
+ }
+ mutex_unlock(&as->lock);
+}
+
+/*
+ * Caller must lock/unlock as
+ */
+static int alloc_pdir(struct smmu_as *as)
+{
+ unsigned long *pdir;
+ int pdn;
+
+ if (as->pdir_page)
+ return 0;
+
+ as->pte_count = kzalloc(sizeof(as->pte_count[0]) * SMMU_PDIR_COUNT,
+ GFP_KERNEL);
+ if (!as->pte_count) {
+ pr_err(DRIVER_NAME
+ ": failed to allocate tegra_iovmm_device PTE cunters\n");
+ return -ENOMEM;
+ }
+ as->pdir_page = alloc_page(GFP_KERNEL | __GFP_DMA);
+ if (!as->pdir_page) {
+ pr_err(DRIVER_NAME
+ ": failed to allocate tegra_iovmm_device page directory\n");
+ kfree(as->pte_count);
+ as->pte_count = NULL;
+ return -ENOMEM;
+ }
+ SetPageReserved(as->pdir_page);
+ pdir = kmap(as->pdir_page);
+
+ for (pdn = 0; pdn < SMMU_PDIR_COUNT; pdn++)
+ pdir[pdn] = _PDE_VACANT(pdn);
+ FLUSH_CPU_DCACHE(pdir, as->pdir_page, SMMU_PDIR_SIZE);
+ writel(MC_SMMU_PTC_FLUSH_0_PTC_FLUSH_TYPE_ADR |
+ VA_PAGE_TO_PA(pdir, as->pdir_page),
+ as->smmu->regs + MC_SMMU_PTC_FLUSH_0);
+ FLUSH_SMMU_REGS(as->smmu);
+ writel(MC_SMMU_TLB_FLUSH_0_TLB_FLUSH_VA_MATCH_ALL |
+ MC_SMMU_TLB_FLUSH_0_TLB_FLUSH_ASID_MATCH__ENABLE |
+ (as->asid << MC_SMMU_TLB_FLUSH_0_TLB_FLUSH_ASID_SHIFT),
+ as->smmu->regs + MC_SMMU_TLB_FLUSH_0);
+ FLUSH_SMMU_REGS(as->smmu);
+ kunmap(as->pdir_page);
+
+ return 0;
+}
+
+static void _sysfs_create(struct smmu_as *as, struct device *sysfs_parent);
+
+/*
+ * Allocate resources for an AS
+ * TODO: split into "alloc" and "lock"
+ */
+static struct tegra_iovmm_domain *smmu_alloc_domain(
+ struct tegra_iovmm_device *dev, struct tegra_iovmm_client *client)
+{
+ struct smmu_device *smmu =
+ container_of(dev, struct smmu_device, iovmm_dev);
+ struct smmu_as *as = NULL;
+ const struct domain_hwc_map *map = NULL;
+ int asid, i;
+
+ /* Look for a free AS */
+ for (asid = smmu->lowest_asid; asid < smmu->num_ases; asid++) {
+ mutex_lock(&smmu->as[asid].lock);
+ if (!smmu->as[asid].hwclients) {
+ as = &smmu->as[asid];
+ break;
+ }
+ mutex_unlock(&smmu->as[asid].lock);
+ }
+
+ if (!as) {
+ pr_err(DRIVER_NAME ": no free AS\n");
+ return NULL;
+ }
+
+ if (alloc_pdir(as) < 0)
+ goto bad3;
+
+ /* Look for a matching hardware client group */
+ for (i = 0; ARRAY_SIZE(smmu_hwc_map); i++) {
+ if (!strcmp(smmu_hwc_map[i].dev_name, client->misc_dev->name)) {
+ map = &smmu_hwc_map[i];
+ break;
+ }
+ }
+
+ if (!map) {
+ pr_err(DRIVER_NAME ": no SMMU resource for %s (%s)\n",
+ client->name, client->misc_dev->name);
+ goto bad2;
+ }
+
+ spin_lock(&smmu->lock);
+ /* Update PDIR register */
+ writel(MC_SMMU_PTB_ASID_0_CURRENT_ASID(as->asid),
+ as->smmu->regs + MC_SMMU_PTB_ASID_0);
+ writel(SMMU_MK_PDIR(as->pdir_page, as->pdir_attr),
+ as->smmu->regs + MC_SMMU_PTB_DATA_0);
+ FLUSH_SMMU_REGS(smmu);
+
+ /* Put each hardware client in the group into the address space */
+ for (i = 0; i < map->nr_hwcs; i++) {
+ struct smmu_hwc_state *hwcst = &smmu->hwc_state[map->hwcs[i]];
+
+ /* Is the hardware client busy? */
+ if (hwcst->enable_disable != SMMU_ASID_DISABLE &&
+ hwcst->enable_disable != SMMU_ASID_ENABLE(as->asid)) {
+ pr_err(DRIVER_NAME
+ ": HW 0x%lx busy for ASID %ld (client!=%s)\n",
+ hwcst->reg,
+ SMMU_ASID_ASID(hwcst->enable_disable),
+ client->name);
+ goto bad;
+ }
+ hwcst->enable_disable = SMMU_ASID_ENABLE(as->asid);
+ writel(hwcst->enable_disable, smmu->regs + hwcst->reg);
+ }
+ FLUSH_SMMU_REGS(smmu);
+ spin_unlock(&smmu->lock);
+ as->hwclients = map;
+ _sysfs_create(as, client->misc_dev->this_device);
+ mutex_unlock(&as->lock);
+
+ /* Reserve "page zero" for AVP vectors using a common dummy page */
+ smmu_map_pfn(&as->domain, NULL, 0,
+ page_to_phys(as->smmu->avp_vector_page) >> SMMU_PAGE_SHIFT);
+ return &as->domain;
+
+bad:
+ /* Reset hardware clients that have been enabled */
+ while (--i >= 0) {
+ struct smmu_hwc_state *hwcst = &smmu->hwc_state[map->hwcs[i]];
+
+ hwcst->enable_disable = SMMU_ASID_DISABLE;
+ writel(hwcst->enable_disable, smmu->regs + hwcst->reg);
+ }
+ FLUSH_SMMU_REGS(smmu);
+ spin_unlock(&as->smmu->lock);
+bad2:
+ free_pdir(as);
+bad3:
+ mutex_unlock(&as->lock);
+ return NULL;
+
+}
+
+/*
+ * Release resources for an AS
+ * TODO: split into "unlock" and "free"
+ */
+static void smmu_free_domain(
+ struct tegra_iovmm_domain *domain, struct tegra_iovmm_client *client)
+{
+ struct smmu_as *as = container_of(domain, struct smmu_as, domain);
+ struct smmu_device *smmu = as->smmu;
+ const struct domain_hwc_map *map = NULL;
+ int i;
+
+ mutex_lock(&as->lock);
+ map = as->hwclients;
+
+ spin_lock(&smmu->lock);
+ for (i = 0; i < map->nr_hwcs; i++) {
+ struct smmu_hwc_state *hwcst = &smmu->hwc_state[map->hwcs[i]];
+
+ hwcst->enable_disable = SMMU_ASID_DISABLE;
+ writel(SMMU_ASID_DISABLE, smmu->regs + hwcst->reg);
+ }
+ FLUSH_SMMU_REGS(smmu);
+ spin_unlock(&smmu->lock);
+
+ as->hwclients = NULL;
+ if (as->pdir_page) {
+ spin_lock(&smmu->lock);
+ writel(MC_SMMU_PTB_ASID_0_CURRENT_ASID(as->asid),
+ smmu->regs + MC_SMMU_PTB_ASID_0);
+ writel(MC_SMMU_PTB_DATA_0_RESET_VAL,
+ smmu->regs + MC_SMMU_PTB_DATA_0);
+ FLUSH_SMMU_REGS(smmu);
+ spin_unlock(&smmu->lock);
+
+ free_pdir(as);
+ }
+ mutex_unlock(&as->lock);
+}
+
+static struct tegra_iovmm_device_ops tegra_iovmm_smmu_ops = {
+ .map = smmu_map,
+ .unmap = smmu_unmap,
+ .map_pfn = smmu_map_pfn,
+ .alloc_domain = smmu_alloc_domain,
+ .free_domain = smmu_free_domain,
+ .suspend = smmu_suspend,
+ .resume = smmu_resume,
+};
+
+static int smmu_probe(struct platform_device *pdev)
+{
+ struct smmu_device *smmu;
+ struct resource *regs, *regs2;
+ struct tegra_smmu_window *window;
+ int e, asid;
+
+ if (!pdev) {
+ pr_err(DRIVER_NAME ": platform_device required\n");
+ return -ENODEV;
+ }
+
+ if (PAGE_SHIFT != SMMU_PAGE_SHIFT) {
+ pr_err(DRIVER_NAME ": SMMU and CPU page sizes must match\n");
+ return -ENXIO;
+ }
+
+ if (ARRAY_SIZE(smmu_hwc_state_init) != HWC_COUNT) {
+ pr_err(DRIVER_NAME
+ ": sizeof smmu_hwc_state_init != enum smmu_hwclient\n");
+ return -ENXIO;
+ }
+
+ regs = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mc");
+ regs2 = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ahbarb");
+ window = tegra_smmu_window(0);
+
+ if (!regs || !regs2 || !window) {
+ pr_err(DRIVER_NAME ": No SMMU resources\n");
+ return -ENODEV;
+ }
+ smmu = kzalloc(sizeof(*smmu), GFP_KERNEL);
+ if (!smmu) {
+ pr_err(DRIVER_NAME ": failed to allocate smmu_device\n");
+ return -ENOMEM;
+ }
+
+ smmu->num_ases = MC_SMMU_NUM_ASIDS;
+ smmu->iovmm_base = (tegra_iovmm_addr_t)window->start;
+ smmu->page_count = (window->end + 1 - window->start) >> SMMU_PAGE_SHIFT;
+ smmu->regs = ioremap(regs->start, regs->end + 1 - regs->start);
+ smmu->regs_ahbarb =
+ ioremap(regs2->start, regs2->end + 1 - regs2->start);
+ if (!smmu->regs || !smmu->regs_ahbarb) {
+ pr_err(DRIVER_NAME ": failed to remap SMMU registers\n");
+ e = -ENXIO;
+ goto fail;
+ }
+
+ smmu->translation_enable_0_0 = ~0;
+ smmu->translation_enable_1_0 = ~0;
+ smmu->translation_enable_2_0 = ~0;
+ smmu->asid_security_0 = 0;
+
+ memcpy(smmu->hwc_state, smmu_hwc_state_init, sizeof(smmu->hwc_state));
+
+ smmu->iovmm_dev.name = VMM_NAME;
+ smmu->iovmm_dev.ops = &tegra_iovmm_smmu_ops;
+ smmu->iovmm_dev.pgsize_bits = SMMU_PAGE_SHIFT;
+
+ e = tegra_iovmm_register(&smmu->iovmm_dev);
+ if (e)
+ goto fail;
+
+ smmu->as = kzalloc(sizeof(smmu->as[0]) * smmu->num_ases, GFP_KERNEL);
+ if (!smmu->as) {
+ pr_err(DRIVER_NAME ": failed to allocate smmu_as\n");
+ e = -ENOMEM;
+ goto fail;
+ }
+
+ /* Initialize address space structure array */
+ for (asid = 0; asid < smmu->num_ases; asid++) {
+ struct smmu_as *as = &smmu->as[asid];
+
+ as->smmu = smmu;
+ as->asid = asid;
+ as->pdir_attr = _PDIR_ATTR;
+ as->pde_attr = _PDE_ATTR;
+ as->pte_attr = _PTE_ATTR;
+
+ mutex_init(&as->lock);
+
+ e = tegra_iovmm_domain_init(&as->domain, &smmu->iovmm_dev,
+ smmu->iovmm_base,
+ smmu->iovmm_base +
+ (smmu->page_count << SMMU_PAGE_SHIFT));
+ if (e)
+ goto fail;
+ }
+ spin_lock_init(&smmu->lock);
+ smmu_setup_regs(smmu);
+ smmu->enable = 1;
+ platform_set_drvdata(pdev, smmu);
+
+ smmu->avp_vector_page = alloc_page(GFP_KERNEL);
+ if (!smmu->avp_vector_page)
+ goto fail;
+ return 0;
+
+fail:
+ if (smmu->avp_vector_page)
+ __free_page(smmu->avp_vector_page);
+ if (smmu->regs)
+ iounmap(smmu->regs);
+ if (smmu->regs_ahbarb)
+ iounmap(smmu->regs_ahbarb);
+ if (smmu && smmu->as) {
+ for (asid = 0; asid < smmu->num_ases; asid++) {
+ if (smmu->as[asid].pdir_page) {
+ ClearPageReserved(smmu->as[asid].pdir_page);
+ __free_page(smmu->as[asid].pdir_page);
+ }
+ }
+ kfree(smmu->as);
+ }
+ kfree(smmu);
+ return e;
+}
+
+static struct platform_driver tegra_iovmm_smmu_drv = {
+ .probe = smmu_probe,
+ .remove = smmu_remove,
+ .driver = {
+ .name = DRIVER_NAME,
+ },
+};
+
+static int __devinit smmu_init(void)
+{
+ return platform_driver_register(&tegra_iovmm_smmu_drv);
+}
+
+static void __exit smmu_exit(void)
+{
+ platform_driver_unregister(&tegra_iovmm_smmu_drv);
+}
+
+subsys_initcall(smmu_init);
+module_exit(smmu_exit);
+
+/*
+ * SMMU-global sysfs interface for debugging
+ */
+static ssize_t _sysfs_show_reg(struct device *d,
+ struct device_attribute *da, char *buf);
+static ssize_t _sysfs_store_reg(struct device *d,
+ struct device_attribute *da, const char *buf,
+ size_t count);
+
+#define _NAME_MAP(_name) { \
+ .name = __stringify(_name), \
+ .offset = _name##_0, \
+ .dev_attr = __ATTR(_name, S_IRUGO | S_IWUSR, \
+ _sysfs_show_reg, _sysfs_store_reg) \
+}
+
+static
+struct _reg_name_map {
+ const char *name;
+ unsigned offset;
+ struct device_attribute dev_attr;
+} _smmu_reg_name_map[] = {
+ _NAME_MAP(MC_SMMU_CONFIG),
+ _NAME_MAP(MC_SMMU_TLB_CONFIG),
+ _NAME_MAP(MC_SMMU_PTC_CONFIG),
+ _NAME_MAP(MC_SMMU_PTB_ASID),
+ _NAME_MAP(MC_SMMU_PTB_DATA),
+ _NAME_MAP(MC_SMMU_TLB_FLUSH),
+ _NAME_MAP(MC_SMMU_PTC_FLUSH),
+ _NAME_MAP(MC_SMMU_ASID_SECURITY),
+ _NAME_MAP(MC_SMMU_STATS_TLB_HIT_COUNT),
+ _NAME_MAP(MC_SMMU_STATS_TLB_MISS_COUNT),
+ _NAME_MAP(MC_SMMU_STATS_PTC_HIT_COUNT),
+ _NAME_MAP(MC_SMMU_STATS_PTC_MISS_COUNT),
+ _NAME_MAP(MC_SMMU_TRANSLATION_ENABLE_0),
+ _NAME_MAP(MC_SMMU_TRANSLATION_ENABLE_1),
+ _NAME_MAP(MC_SMMU_TRANSLATION_ENABLE_2),
+ _NAME_MAP(MC_SMMU_AFI_ASID),
+ _NAME_MAP(MC_SMMU_AVPC_ASID),
+ _NAME_MAP(MC_SMMU_DC_ASID),
+ _NAME_MAP(MC_SMMU_DCB_ASID),
+ _NAME_MAP(MC_SMMU_EPP_ASID),
+ _NAME_MAP(MC_SMMU_G2_ASID),
+ _NAME_MAP(MC_SMMU_HC_ASID),
+ _NAME_MAP(MC_SMMU_HDA_ASID),
+ _NAME_MAP(MC_SMMU_ISP_ASID),
+ _NAME_MAP(MC_SMMU_MPE_ASID),
+ _NAME_MAP(MC_SMMU_NV_ASID),
+ _NAME_MAP(MC_SMMU_NV2_ASID),
+ _NAME_MAP(MC_SMMU_PPCS_ASID),
+ _NAME_MAP(MC_SMMU_SATA_ASID),
+ _NAME_MAP(MC_SMMU_VDE_ASID),
+ _NAME_MAP(MC_SMMU_VI_ASID),
+};
+
+static ssize_t lookup_reg(struct device_attribute *da)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(_smmu_reg_name_map); i++) {
+ if (!strcmp(_smmu_reg_name_map[i].name, da->attr.name))
+ return _smmu_reg_name_map[i].offset;
+ }
+ return -ENODEV;
+}
+
+static ssize_t _sysfs_show_reg(struct device *d,
+ struct device_attribute *da, char *buf)
+{
+ struct smmu_device *smmu =
+ container_of(d, struct smmu_device, sysfs_dev);
+ ssize_t offset = lookup_reg(da);
+
+ if (offset < 0)
+ return offset;
+ return sprintf(buf, "%08lx\n",
+ (unsigned long)readl(smmu->regs + offset));
+}
+
+static ssize_t _sysfs_store_reg(struct device *d,
+ struct device_attribute *da,
+ const char *buf, size_t count)
+{
+ struct smmu_device *smmu =
+ container_of(d, struct smmu_device, sysfs_dev);
+ ssize_t offset = lookup_reg(da);
+ u32 value;
+ int err;
+
+ if (offset < 0)
+ return offset;
+
+ err = kstrtou32(buf, 16, &value);
+ if (err)
+ return err;
+
+#ifdef CONFIG_TEGRA_IOVMM_SMMU_SYSFS
+ writel(value, smmu->regs + offset);
+#else
+ /* Allow writing to reg only for TLB/PTC stats enabling/disabling */
+ {
+ unsigned long mask = 0;
+ switch (offset) {
+ case MC_SMMU_TLB_CONFIG_0:
+ mask = MC_SMMU_TLB_CONFIG_0_TLB_STATS__MASK;
+ break;
+ case MC_SMMU_PTC_CONFIG_0:
+ mask = MC_SMMU_PTC_CONFIG_0_PTC_STATS__MASK;
+ break;
+ default:
+ break;
+ }
+
+ if (mask) {
+ unsigned long currval = readl(smmu->regs + offset);
+ currval &= ~mask;
+ value &= mask;
+ value |= currval;
+ writel(value, smmu->regs + offset);
+ }
+ }
+#endif
+ return count;
+}
+
+static ssize_t _sysfs_show_smmu(struct device *d,
+ struct device_attribute *da, char *buf)
+{
+ struct smmu_device *smmu =
+ container_of(d, struct smmu_device, sysfs_dev);
+ ssize_t rv = 0;
+
+ rv += sprintf(buf + rv , " regs: %p\n", smmu->regs);
+ rv += sprintf(buf + rv , "iovmm_base: %p\n", (void *)smmu->iovmm_base);
+ rv += sprintf(buf + rv , "page_count: %lx\n", smmu->page_count);
+ rv += sprintf(buf + rv , " num_ases: %d\n", smmu->num_ases);
+ rv += sprintf(buf + rv , " as: %p\n", smmu->as);
+ rv += sprintf(buf + rv , " enable: %s\n",
+ smmu->enable ? "yes" : "no");
+ return rv;
+}
+
+static struct device_attribute _attr_show_smmu
+ = __ATTR(show_smmu, S_IRUGO, _sysfs_show_smmu, NULL);
+
+#define _SYSFS_SHOW_VALUE(name, field, fmt) \
+static ssize_t _sysfs_show_##name(struct device *d, \
+ struct device_attribute *da, char *buf) \
+{ \
+ struct smmu_device *smmu = \
+ container_of(d, struct smmu_device, sysfs_dev); \
+ ssize_t rv = 0; \
+ rv += sprintf(buf + rv, fmt "\n", smmu->field); \
+ return rv; \
+}
+
+static void (*_sysfs_null_callback)(struct smmu_device *, unsigned long *) =
+ NULL;
+
+#define _SYSFS_SET_VALUE_DO(name, field, base, ceil, callback) \
+static ssize_t _sysfs_set_##name(struct device *d, \
+ struct device_attribute *da, const char *buf, size_t count) \
+{ \
+ int err; \
+ u32 value; \
+ struct smmu_device *smmu = \
+ container_of(d, struct smmu_device, sysfs_dev); \
+ err = kstrtou32(buf, base, &value); \
+ if (err) \
+ return err; \
+ if (0 <= value && value < ceil) { \
+ smmu->field = value; \
+ if (callback) \
+ callback(smmu, &smmu->field); \
+ } \
+ return count; \
+}
+#ifdef CONFIG_TEGRA_IOVMM_SMMU_SYSFS
+#define _SYSFS_SET_VALUE _SYSFS_SET_VALUE_DO
+#else
+#define _SYSFS_SET_VALUE(name, field, base, ceil, callback) \
+static ssize_t _sysfs_set_##name(struct device *d, \
+ struct device_attribute *da, const char *buf, size_t count) \
+{ \
+ return count; \
+}
+#endif
+
+_SYSFS_SHOW_VALUE(lowest_asid, lowest_asid, "%lu")
+_SYSFS_SET_VALUE(lowest_asid, lowest_asid, 10,
+ MC_SMMU_NUM_ASIDS, _sysfs_null_callback)
+_SYSFS_SHOW_VALUE(debug_asid, debug_asid, "%lu")
+_SYSFS_SET_VALUE(debug_asid, debug_asid, 10,
+ MC_SMMU_NUM_ASIDS, _sysfs_null_callback)
+_SYSFS_SHOW_VALUE(signature_pid, signature_pid, "%lu")
+_SYSFS_SET_VALUE_DO(signature_pid, signature_pid, 10, PID_MAX_LIMIT + 1,
+ _sysfs_null_callback)
+
+#ifdef CONFIG_TEGRA_IOVMM_SMMU_SYSFS
+static void _sysfs_mask_attr(struct smmu_device *smmu, unsigned long *field)
+{
+ *field &= _MASK_ATTR;
+}
+
+static void _sysfs_mask_pdir_attr(struct smmu_device *smmu,
+ unsigned long *field)
+{
+ unsigned long pdir;
+
+ _sysfs_mask_attr(smmu, field);
+ writel(MC_SMMU_PTB_ASID_0_CURRENT_ASID(smmu->debug_asid),
+ smmu->regs + MC_SMMU_PTB_ASID_0);
+ pdir = readl(smmu->regs + MC_SMMU_PTB_DATA_0);
+ pdir &= ~_MASK_ATTR;
+ pdir |= *field;
+ writel(pdir, smmu->regs + MC_SMMU_PTB_DATA_0);
+ FLUSH_SMMU_REGS(smmu);
+}
+
+static void (*_sysfs_mask_attr_callback)(struct smmu_device *,
+ unsigned long *field) = &_sysfs_mask_attr;
+static void (*_sysfs_mask_pdir_attr_callback)(struct smmu_device *,
+ unsigned long *field) = &_sysfs_mask_pdir_attr;
+#endif
+
+_SYSFS_SHOW_VALUE(pdir_attr, as[smmu->debug_asid].pdir_attr, "%lx")
+_SYSFS_SET_VALUE(pdir_attr, as[smmu->debug_asid].pdir_attr, 16,
+ _PDIR_ATTR + 1, _sysfs_mask_pdir_attr_callback)
+_SYSFS_SHOW_VALUE(pde_attr, as[smmu->debug_asid].pde_attr, "%lx")
+_SYSFS_SET_VALUE(pde_attr, as[smmu->debug_asid].pde_attr, 16,
+ _PDE_ATTR + 1, _sysfs_mask_attr_callback)
+_SYSFS_SHOW_VALUE(pte_attr, as[smmu->debug_asid].pte_attr, "%lx")
+_SYSFS_SET_VALUE(pte_attr, as[smmu->debug_asid].pte_attr, 16,
+ _PTE_ATTR + 1, _sysfs_mask_attr_callback)
+
+static struct device_attribute _attr_values[] = {
+ __ATTR(lowest_asid, S_IRUGO | S_IWUSR,
+ _sysfs_show_lowest_asid, _sysfs_set_lowest_asid),
+ __ATTR(debug_asid, S_IRUGO | S_IWUSR,
+ _sysfs_show_debug_asid, _sysfs_set_debug_asid),
+ __ATTR(signature_pid, S_IRUGO | S_IWUSR,
+ _sysfs_show_signature_pid, _sysfs_set_signature_pid),
+
+ __ATTR(pdir_attr, S_IRUGO | S_IWUSR,
+ _sysfs_show_pdir_attr, _sysfs_set_pdir_attr),
+ __ATTR(pde_attr, S_IRUGO | S_IWUSR,
+ _sysfs_show_pde_attr, _sysfs_set_pde_attr),
+ __ATTR(pte_attr, S_IRUGO | S_IWUSR,
+ _sysfs_show_pte_attr, _sysfs_set_pte_attr),
+};
+
+static struct attribute *_smmu_attrs[
+ ARRAY_SIZE(_smmu_reg_name_map) + ARRAY_SIZE(_attr_values) + 3];
+static struct attribute_group _smmu_attr_group = {
+ .attrs = _smmu_attrs
+};
+
+static void _sysfs_smmu(struct smmu_device *smmu, struct device *parent)
+{
+ int i, j;
+
+ if (smmu->sysfs_use_count++ > 0)
+ return;
+ for (i = 0; i < ARRAY_SIZE(_smmu_reg_name_map); i++)
+ _smmu_attrs[i] = &_smmu_reg_name_map[i].dev_attr.attr;
+ for (j = 0; j < ARRAY_SIZE(_attr_values); j++)
+ _smmu_attrs[i++] = &_attr_values[j].attr;
+ _smmu_attrs[i++] = &_attr_show_smmu.attr;
+ _smmu_attrs[i] = NULL;
+
+ dev_set_name(&smmu->sysfs_dev, "smmu");
+ smmu->sysfs_dev.parent = parent;
+ smmu->sysfs_dev.driver = NULL;
+ smmu->sysfs_dev.release = NULL;
+ if (device_register(&smmu->sysfs_dev)) {
+ pr_err("%s: failed to register smmu_sysfs_dev\n", __func__);
+ smmu->sysfs_use_count--;
+ return;
+ }
+ if (sysfs_create_group(&smmu->sysfs_dev.kobj, &_smmu_attr_group)) {
+ pr_err("%s: failed to create group for smmu_sysfs_dev\n",
+ __func__);
+ smmu->sysfs_use_count--;
+ return;
+ }
+}
+
+static void _sysfs_create(struct smmu_as *as, struct device *parent)
+{
+ _sysfs_smmu(as->smmu, parent);
+}
diff --git a/arch/arm/mach-tegra/iovmm.c b/arch/arm/mach-tegra/iovmm.c
new file mode 100644
index 000000000000..129993e4363f
--- /dev/null
+++ b/arch/arm/mach-tegra/iovmm.c
@@ -0,0 +1,950 @@
+/*
+ * arch/arm/mach-tegra/iovmm.c
+ *
+ * Tegra I/O VM manager
+ *
+ * Copyright (c) 2010-2011, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/proc_fs.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/syscore_ops.h>
+
+#include <mach/iovmm.h>
+
+/*
+ * after the best-fit block is located, the remaining pages not needed
+ * for the allocation will be split into a new free block if the
+ * number of remaining pages is >= MIN_SPLIT_PAGE.
+ */
+#define MIN_SPLIT_PAGE 4
+#define MIN_SPLIT_BYTES(_d) (MIN_SPLIT_PAGE << (_d)->dev->pgsize_bits)
+#define NO_SPLIT(m) ((m) < MIN_SPLIT_BYTES(domain))
+#define DO_SPLIT(m) ((m) >= MIN_SPLIT_BYTES(domain))
+
+#define iovmm_start(_b) ((_b)->vm_area.iovm_start)
+#define iovmm_length(_b) ((_b)->vm_area.iovm_length)
+#define iovmm_end(_b) (iovmm_start(_b) + iovmm_length(_b))
+
+/* flags for the block */
+#define BK_free 0 /* indicates free mappings */
+#define BK_map_dirty 1 /* used by demand-loaded mappings */
+
+/* flags for the client */
+#define CL_locked 0
+
+/* flags for the domain */
+#define DM_map_dirty 0
+
+struct tegra_iovmm_block {
+ struct tegra_iovmm_area vm_area;
+ tegra_iovmm_addr_t start;
+ size_t length;
+ atomic_t ref;
+ unsigned long flags;
+ unsigned long poison;
+ struct rb_node free_node;
+ struct rb_node all_node;
+};
+
+struct iovmm_share_group {
+ const char *name;
+ struct tegra_iovmm_domain *domain;
+ struct list_head client_list;
+ struct list_head group_list;
+ spinlock_t lock; /* for client_list */
+};
+
+static LIST_HEAD(iovmm_devices);
+static LIST_HEAD(iovmm_groups);
+static DEFINE_MUTEX(iovmm_group_list_lock);
+static struct kmem_cache *iovmm_cache;
+
+static tegra_iovmm_addr_t iovmm_align_up(struct tegra_iovmm_device *dev,
+ tegra_iovmm_addr_t addr)
+{
+ addr += (1<<dev->pgsize_bits);
+ addr--;
+ addr &= ~((1<<dev->pgsize_bits)-1);
+ return addr;
+}
+
+static tegra_iovmm_addr_t iovmm_align_down(struct tegra_iovmm_device *dev,
+ tegra_iovmm_addr_t addr)
+{
+ addr &= ~((1<<dev->pgsize_bits)-1);
+ return addr;
+}
+
+#define SIMALIGN(b, a) (((b)->start % (a)) ? ((a) - ((b)->start % (a))) : 0)
+
+#define iovmprint(fmt, arg...) snprintf(page+len, count-len, fmt, ## arg)
+
+size_t tegra_iovmm_get_max_free(struct tegra_iovmm_client *client)
+{
+ struct rb_node *n;
+ struct tegra_iovmm_block *b;
+ struct tegra_iovmm_domain *domain = client->domain;
+ tegra_iovmm_addr_t max_free = 0;
+
+ spin_lock(&domain->block_lock);
+ n = rb_first(&domain->all_blocks);
+ while (n) {
+ b = rb_entry(n, struct tegra_iovmm_block, all_node);
+ n = rb_next(n);
+ if (test_bit(BK_free, &b->flags)) {
+ max_free = max_t(tegra_iovmm_addr_t,
+ max_free, iovmm_length(b));
+ }
+ }
+ spin_unlock(&domain->block_lock);
+ return max_free;
+}
+
+
+static void tegra_iovmm_block_stats(struct tegra_iovmm_domain *domain,
+ unsigned int *num_blocks, unsigned int *num_free,
+ tegra_iovmm_addr_t *total, size_t *total_free, size_t *max_free)
+{
+ struct rb_node *n;
+ struct tegra_iovmm_block *b;
+
+ *num_blocks = 0;
+ *num_free = 0;
+ *total = 0;
+ *total_free = 0;
+ *max_free = 0;
+
+ spin_lock(&domain->block_lock);
+ n = rb_first(&domain->all_blocks);
+ while (n) {
+ b = rb_entry(n, struct tegra_iovmm_block, all_node);
+ n = rb_next(n);
+ (*num_blocks)++;
+ *total += b->length;
+ if (test_bit(BK_free, &b->flags)) {
+ (*num_free)++;
+ *total_free += b->length;
+ *max_free = max_t(size_t, *max_free, b->length);
+ }
+ }
+ spin_unlock(&domain->block_lock);
+}
+
+static int tegra_iovmm_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ struct iovmm_share_group *grp;
+ size_t max_free, total_free, total;
+ unsigned int num, num_free;
+
+ int len = 0;
+
+ mutex_lock(&iovmm_group_list_lock);
+ len += iovmprint("\ngroups\n");
+ if (list_empty(&iovmm_groups))
+ len += iovmprint("\t<empty>\n");
+ else {
+ list_for_each_entry(grp, &iovmm_groups, group_list) {
+ len += iovmprint("\t%s (device: %s)\n",
+ (grp->name) ? grp->name : "<unnamed>",
+ grp->domain->dev->name);
+ tegra_iovmm_block_stats(grp->domain, &num,
+ &num_free, &total, &total_free, &max_free);
+ total >>= 10;
+ total_free >>= 10;
+ max_free >>= 10;
+ len += iovmprint("\t\tsize: %uKiB free: %uKiB "
+ "largest: %uKiB (%u free / %u total blocks)\n",
+ total, total_free, max_free, num_free, num);
+ }
+ }
+ mutex_unlock(&iovmm_group_list_lock);
+
+ *eof = 1;
+ return len;
+}
+
+static void iovmm_block_put(struct tegra_iovmm_block *b)
+{
+ BUG_ON(b->poison);
+ BUG_ON(atomic_read(&b->ref) == 0);
+ if (!atomic_dec_return(&b->ref)) {
+ b->poison = 0xa5a5a5a5;
+ kmem_cache_free(iovmm_cache, b);
+ }
+}
+
+static void iovmm_free_block(struct tegra_iovmm_domain *domain,
+ struct tegra_iovmm_block *block)
+{
+ struct tegra_iovmm_block *pred = NULL; /* address-order predecessor */
+ struct tegra_iovmm_block *succ = NULL; /* address-order successor */
+ struct rb_node **p;
+ struct rb_node *parent = NULL, *temp;
+ int pred_free = 0, succ_free = 0;
+
+ iovmm_block_put(block);
+
+ spin_lock(&domain->block_lock);
+ temp = rb_prev(&block->all_node);
+ if (temp)
+ pred = rb_entry(temp, struct tegra_iovmm_block, all_node);
+ temp = rb_next(&block->all_node);
+ if (temp)
+ succ = rb_entry(temp, struct tegra_iovmm_block, all_node);
+
+ if (pred)
+ pred_free = test_bit(BK_free, &pred->flags);
+ if (succ)
+ succ_free = test_bit(BK_free, &succ->flags);
+
+ if (pred_free && succ_free) {
+ pred->length += block->length;
+ pred->length += succ->length;
+ rb_erase(&block->all_node, &domain->all_blocks);
+ rb_erase(&succ->all_node, &domain->all_blocks);
+ rb_erase(&succ->free_node, &domain->free_blocks);
+ rb_erase(&pred->free_node, &domain->free_blocks);
+ iovmm_block_put(block);
+ iovmm_block_put(succ);
+ block = pred;
+ } else if (pred_free) {
+ pred->length += block->length;
+ rb_erase(&block->all_node, &domain->all_blocks);
+ rb_erase(&pred->free_node, &domain->free_blocks);
+ iovmm_block_put(block);
+ block = pred;
+ } else if (succ_free) {
+ block->length += succ->length;
+ rb_erase(&succ->all_node, &domain->all_blocks);
+ rb_erase(&succ->free_node, &domain->free_blocks);
+ iovmm_block_put(succ);
+ }
+
+ p = &domain->free_blocks.rb_node;
+ while (*p) {
+ struct tegra_iovmm_block *b;
+ parent = *p;
+ b = rb_entry(parent, struct tegra_iovmm_block, free_node);
+ if (block->length >= b->length)
+ p = &parent->rb_right;
+ else
+ p = &parent->rb_left;
+ }
+ rb_link_node(&block->free_node, parent, p);
+ rb_insert_color(&block->free_node, &domain->free_blocks);
+ set_bit(BK_free, &block->flags);
+ spin_unlock(&domain->block_lock);
+}
+
+/*
+ * if the best-fit block is larger than the requested size, a remainder
+ * block will be created and inserted into the free list in its place.
+ * since all free blocks are stored in two trees the new block needs to be
+ * linked into both.
+ */
+static struct tegra_iovmm_block *iovmm_split_free_block(
+ struct tegra_iovmm_domain *domain,
+ struct tegra_iovmm_block *block, unsigned long size)
+{
+ struct rb_node **p;
+ struct rb_node *parent = NULL;
+ struct tegra_iovmm_block *rem;
+ struct tegra_iovmm_block *b;
+
+ rem = kmem_cache_zalloc(iovmm_cache, GFP_KERNEL);
+ if (!rem)
+ return NULL;
+
+ spin_lock(&domain->block_lock);
+ p = &domain->free_blocks.rb_node;
+
+ rem->start = block->start + size;
+ rem->length = block->length - size;
+ atomic_set(&rem->ref, 1);
+ block->length = size;
+
+ while (*p) {
+ parent = *p;
+ b = rb_entry(parent, struct tegra_iovmm_block, free_node);
+ if (rem->length >= b->length)
+ p = &parent->rb_right;
+ else
+ p = &parent->rb_left;
+ }
+ set_bit(BK_free, &rem->flags);
+ rb_link_node(&rem->free_node, parent, p);
+ rb_insert_color(&rem->free_node, &domain->free_blocks);
+
+ p = &domain->all_blocks.rb_node;
+ parent = NULL;
+ while (*p) {
+ parent = *p;
+ b = rb_entry(parent, struct tegra_iovmm_block, all_node);
+ if (rem->start >= b->start)
+ p = &parent->rb_right;
+ else
+ p = &parent->rb_left;
+ }
+ rb_link_node(&rem->all_node, parent, p);
+ rb_insert_color(&rem->all_node, &domain->all_blocks);
+
+ return rem;
+}
+
+static int iovmm_block_splitting;
+static struct tegra_iovmm_block *iovmm_alloc_block(
+ struct tegra_iovmm_domain *domain, size_t size, size_t align)
+{
+ struct rb_node *n;
+ struct tegra_iovmm_block *b, *best;
+ size_t simalign;
+
+ BUG_ON(!size);
+ size = iovmm_align_up(domain->dev, size);
+ align = iovmm_align_up(domain->dev, align);
+ for (;;) {
+ spin_lock(&domain->block_lock);
+ if (!iovmm_block_splitting)
+ break;
+ spin_unlock(&domain->block_lock);
+ schedule();
+ }
+ n = domain->free_blocks.rb_node;
+ best = NULL;
+ while (n) {
+ tegra_iovmm_addr_t aligned_start, block_ceil;
+
+ b = rb_entry(n, struct tegra_iovmm_block, free_node);
+ simalign = SIMALIGN(b, align);
+ aligned_start = b->start + simalign;
+ block_ceil = b->start + b->length;
+
+ if (block_ceil >= aligned_start + size) {
+ /* Block has enough size */
+ best = b;
+ if (NO_SPLIT(simalign) &&
+ NO_SPLIT(block_ceil - (aligned_start + size)))
+ break;
+ n = n->rb_left;
+ } else {
+ n = n->rb_right;
+ }
+ }
+ if (!best) {
+ spin_unlock(&domain->block_lock);
+ return NULL;
+ }
+
+ simalign = SIMALIGN(best, align);
+ if (DO_SPLIT(simalign)) {
+ iovmm_block_splitting = 1;
+ spin_unlock(&domain->block_lock);
+
+ /* Split off misalignment */
+ b = best;
+ best = iovmm_split_free_block(domain, b, simalign);
+ if (best)
+ simalign = 0;
+ else
+ best = b;
+ }
+
+ /* Unfree designed block */
+ rb_erase(&best->free_node, &domain->free_blocks);
+ clear_bit(BK_free, &best->flags);
+ atomic_inc(&best->ref);
+
+ iovmm_start(best) = best->start + simalign;
+ iovmm_length(best) = size;
+
+ if (DO_SPLIT((best->start + best->length) - iovmm_end(best))) {
+ iovmm_block_splitting = 1;
+ spin_unlock(&domain->block_lock);
+
+ /* Split off excess */
+ (void)iovmm_split_free_block(domain, best, size + simalign);
+ }
+
+ iovmm_block_splitting = 0;
+ spin_unlock(&domain->block_lock);
+
+ return best;
+}
+
+static struct tegra_iovmm_block *iovmm_allocate_vm(
+ struct tegra_iovmm_domain *domain, size_t size,
+ size_t align, unsigned long iovm_start)
+{
+ struct rb_node *n;
+ struct tegra_iovmm_block *b, *best;
+
+ BUG_ON(iovm_start % align);
+ BUG_ON(!size);
+
+ size = iovmm_align_up(domain->dev, size);
+ for (;;) {
+ spin_lock(&domain->block_lock);
+ if (!iovmm_block_splitting)
+ break;
+ spin_unlock(&domain->block_lock);
+ schedule();
+ }
+
+ n = rb_first(&domain->free_blocks);
+ best = NULL;
+ while (n) {
+ b = rb_entry(n, struct tegra_iovmm_block, free_node);
+ if ((b->start <= iovm_start) &&
+ (b->start + b->length) >= (iovm_start + size)) {
+ best = b;
+ break;
+ }
+ n = rb_next(n);
+ }
+
+ if (!best)
+ goto fail;
+
+ /* split the mem before iovm_start. */
+ if (DO_SPLIT(iovm_start - best->start)) {
+ iovmm_block_splitting = 1;
+ spin_unlock(&domain->block_lock);
+ best = iovmm_split_free_block(domain, best,
+ (iovm_start - best->start));
+ }
+ if (!best)
+ goto fail;
+
+ /* remove the desired block from free list. */
+ rb_erase(&best->free_node, &domain->free_blocks);
+ clear_bit(BK_free, &best->flags);
+ atomic_inc(&best->ref);
+
+ iovmm_start(best) = iovm_start;
+ iovmm_length(best) = size;
+
+ BUG_ON(best->start > iovmm_start(best));
+ BUG_ON((best->start + best->length) < iovmm_end(best));
+ /* split the mem after iovm_start+size. */
+ if (DO_SPLIT(best->start + best->length - iovmm_end(best))) {
+ iovmm_block_splitting = 1;
+ spin_unlock(&domain->block_lock);
+ (void)iovmm_split_free_block(domain, best,
+ (iovmm_start(best) - best->start + size));
+ }
+fail:
+ iovmm_block_splitting = 0;
+ spin_unlock(&domain->block_lock);
+ return best;
+}
+
+int tegra_iovmm_domain_init(struct tegra_iovmm_domain *domain,
+ struct tegra_iovmm_device *dev, tegra_iovmm_addr_t start,
+ tegra_iovmm_addr_t end)
+{
+ struct tegra_iovmm_block *b;
+
+ b = kmem_cache_zalloc(iovmm_cache, GFP_KERNEL);
+ if (!b)
+ return -ENOMEM;
+
+ domain->dev = dev;
+ atomic_set(&domain->clients, 0);
+ atomic_set(&domain->locks, 0);
+ atomic_set(&b->ref, 1);
+ spin_lock_init(&domain->block_lock);
+ init_rwsem(&domain->map_lock);
+ init_waitqueue_head(&domain->delay_lock);
+ b->start = iovmm_align_up(dev, start);
+ b->length = iovmm_align_down(dev, end) - b->start;
+ set_bit(BK_free, &b->flags);
+ rb_link_node(&b->free_node, NULL, &domain->free_blocks.rb_node);
+ rb_insert_color(&b->free_node, &domain->free_blocks);
+ rb_link_node(&b->all_node, NULL, &domain->all_blocks.rb_node);
+ rb_insert_color(&b->all_node, &domain->all_blocks);
+ return 0;
+}
+
+/*
+ * If iovm_start != 0, tries to allocate specified iova block if it is
+ * free. if it is not free, it fails.
+ */
+struct tegra_iovmm_area *tegra_iovmm_create_vm(
+ struct tegra_iovmm_client *client, struct tegra_iovmm_area_ops *ops,
+ size_t size, size_t align, pgprot_t pgprot, unsigned long iovm_start)
+{
+ struct tegra_iovmm_block *b;
+ struct tegra_iovmm_domain *domain;
+
+ if (!client)
+ return NULL;
+
+ domain = client->domain;
+
+ if (iovm_start)
+ b = iovmm_allocate_vm(domain, size, align, iovm_start);
+ else
+ b = iovmm_alloc_block(domain, size, align);
+ if (!b)
+ return NULL;
+
+ b->vm_area.domain = domain;
+ b->vm_area.pgprot = pgprot;
+ b->vm_area.ops = ops;
+
+ down_read(&b->vm_area.domain->map_lock);
+ if (ops && !test_bit(CL_locked, &client->flags)) {
+ set_bit(BK_map_dirty, &b->flags);
+ set_bit(DM_map_dirty, &client->domain->flags);
+ } else if (ops) {
+ if (domain->dev->ops->map(domain, &b->vm_area))
+ pr_err("%s failed to map locked domain\n", __func__);
+ }
+ up_read(&b->vm_area.domain->map_lock);
+
+ return &b->vm_area;
+}
+
+void tegra_iovmm_vm_insert_pfn(struct tegra_iovmm_area *vm,
+ tegra_iovmm_addr_t vaddr, unsigned long pfn)
+{
+ struct tegra_iovmm_domain *domain = vm->domain;
+ BUG_ON(vaddr & ((1<<domain->dev->pgsize_bits)-1));
+ BUG_ON(vaddr >= vm->iovm_start + vm->iovm_length);
+ BUG_ON(vaddr < vm->iovm_start);
+ BUG_ON(vm->ops);
+
+ domain->dev->ops->map_pfn(domain, vm, vaddr, pfn);
+}
+
+void tegra_iovmm_zap_vm(struct tegra_iovmm_area *vm)
+{
+ struct tegra_iovmm_block *b;
+ struct tegra_iovmm_domain *domain;
+
+ b = container_of(vm, struct tegra_iovmm_block, vm_area);
+ domain = vm->domain;
+ /*
+ * if the vm area mapping was deferred, don't unmap it since
+ * the memory for the page tables it uses may not be allocated
+ */
+ down_read(&domain->map_lock);
+ if (!test_and_clear_bit(BK_map_dirty, &b->flags))
+ domain->dev->ops->unmap(domain, vm, false);
+ up_read(&domain->map_lock);
+}
+
+void tegra_iovmm_unzap_vm(struct tegra_iovmm_area *vm)
+{
+ struct tegra_iovmm_block *b;
+ struct tegra_iovmm_domain *domain;
+
+ b = container_of(vm, struct tegra_iovmm_block, vm_area);
+ domain = vm->domain;
+ if (!vm->ops)
+ return;
+
+ down_read(&domain->map_lock);
+ if (vm->ops) {
+ if (atomic_read(&domain->locks))
+ domain->dev->ops->map(domain, vm);
+ else {
+ set_bit(BK_map_dirty, &b->flags);
+ set_bit(DM_map_dirty, &domain->flags);
+ }
+ }
+ up_read(&domain->map_lock);
+}
+
+void tegra_iovmm_free_vm(struct tegra_iovmm_area *vm)
+{
+ struct tegra_iovmm_block *b;
+ struct tegra_iovmm_domain *domain;
+
+ if (!vm)
+ return;
+
+ b = container_of(vm, struct tegra_iovmm_block, vm_area);
+ domain = vm->domain;
+ down_read(&domain->map_lock);
+ if (!test_and_clear_bit(BK_map_dirty, &b->flags))
+ domain->dev->ops->unmap(domain, vm, true);
+ iovmm_free_block(domain, b);
+ up_read(&domain->map_lock);
+}
+
+struct tegra_iovmm_area *tegra_iovmm_area_get(struct tegra_iovmm_area *vm)
+{
+ struct tegra_iovmm_block *b;
+
+ BUG_ON(!vm);
+ b = container_of(vm, struct tegra_iovmm_block, vm_area);
+
+ atomic_inc(&b->ref);
+ return &b->vm_area;
+}
+
+void tegra_iovmm_area_put(struct tegra_iovmm_area *vm)
+{
+ struct tegra_iovmm_block *b;
+ BUG_ON(!vm);
+ b = container_of(vm, struct tegra_iovmm_block, vm_area);
+ iovmm_block_put(b);
+}
+
+struct tegra_iovmm_area *tegra_iovmm_find_area_get(
+ struct tegra_iovmm_client *client, tegra_iovmm_addr_t addr)
+{
+ struct rb_node *n;
+ struct tegra_iovmm_block *b = NULL;
+
+ if (!client)
+ return NULL;
+
+ spin_lock(&client->domain->block_lock);
+ n = client->domain->all_blocks.rb_node;
+
+ while (n) {
+ b = rb_entry(n, struct tegra_iovmm_block, all_node);
+ if (iovmm_start(b) <= addr && addr <= iovmm_end(b)) {
+ if (test_bit(BK_free, &b->flags))
+ b = NULL;
+ break;
+ }
+ if (addr > iovmm_start(b))
+ n = n->rb_right;
+ else
+ n = n->rb_left;
+ b = NULL;
+ }
+ if (b)
+ atomic_inc(&b->ref);
+ spin_unlock(&client->domain->block_lock);
+ if (!b)
+ return NULL;
+ return &b->vm_area;
+}
+
+static int _iovmm_client_lock(struct tegra_iovmm_client *client)
+{
+ struct tegra_iovmm_device *dev;
+ struct tegra_iovmm_domain *domain;
+ int v;
+
+ if (unlikely(!client))
+ return -ENODEV;
+
+ if (unlikely(test_bit(CL_locked, &client->flags))) {
+ pr_err("attempting to relock client %s\n", client->name);
+ return 0;
+ }
+
+ domain = client->domain;
+ dev = domain->dev;
+ down_write(&domain->map_lock);
+ v = atomic_inc_return(&domain->locks);
+ /*
+ * if the device doesn't export the lock_domain function, the
+ * device must guarantee that any valid domain will be locked.
+ */
+ if (v == 1 && dev->ops->lock_domain) {
+ if (dev->ops->lock_domain(domain, client)) {
+ atomic_dec(&domain->locks);
+ up_write(&domain->map_lock);
+ return -EAGAIN;
+ }
+ }
+ if (test_and_clear_bit(DM_map_dirty, &domain->flags)) {
+ struct rb_node *n;
+ struct tegra_iovmm_block *b;
+
+ spin_lock(&domain->block_lock);
+ n = rb_first(&domain->all_blocks);
+ while (n) {
+ b = rb_entry(n, struct tegra_iovmm_block, all_node);
+ n = rb_next(n);
+ if (test_bit(BK_free, &b->flags))
+ continue;
+
+ if (test_and_clear_bit(BK_map_dirty, &b->flags)) {
+ if (!b->vm_area.ops) {
+ pr_err("%s: "
+ "vm_area ops must exist for lazy maps\n",
+ __func__);
+ continue;
+ }
+ dev->ops->map(domain, &b->vm_area);
+ }
+ }
+ }
+ set_bit(CL_locked, &client->flags);
+ up_write(&domain->map_lock);
+ return 0;
+}
+
+int tegra_iovmm_client_trylock(struct tegra_iovmm_client *client)
+{
+ return _iovmm_client_lock(client);
+}
+
+int tegra_iovmm_client_lock(struct tegra_iovmm_client *client)
+{
+ int ret;
+
+ if (!client)
+ return -ENODEV;
+
+ ret = wait_event_interruptible(client->domain->delay_lock,
+ _iovmm_client_lock(client) != -EAGAIN);
+
+ if (ret == -ERESTARTSYS)
+ return -EINTR;
+
+ return ret;
+}
+
+void tegra_iovmm_client_unlock(struct tegra_iovmm_client *client)
+{
+ struct tegra_iovmm_device *dev;
+ struct tegra_iovmm_domain *domain;
+ int do_wake = 0;
+
+ if (!client)
+ return;
+
+ if (!test_and_clear_bit(CL_locked, &client->flags)) {
+ pr_err("unlocking unlocked client %s\n", client->name);
+ return;
+ }
+
+ domain = client->domain;
+ dev = domain->dev;
+ down_write(&domain->map_lock);
+ if (!atomic_dec_return(&domain->locks)) {
+ if (dev->ops->unlock_domain)
+ dev->ops->unlock_domain(domain, client);
+ do_wake = 1;
+ }
+ up_write(&domain->map_lock);
+ if (do_wake)
+ wake_up(&domain->delay_lock);
+}
+
+size_t tegra_iovmm_get_vm_size(struct tegra_iovmm_client *client)
+{
+ struct tegra_iovmm_domain *domain;
+ struct rb_node *n;
+ struct tegra_iovmm_block *b;
+ size_t size = 0;
+
+ if (!client)
+ return 0;
+
+ domain = client->domain;
+
+ spin_lock(&domain->block_lock);
+ n = rb_first(&domain->all_blocks);
+ while (n) {
+ b = rb_entry(n, struct tegra_iovmm_block, all_node);
+ n = rb_next(n);
+ size += b->length;
+ }
+ spin_unlock(&domain->block_lock);
+
+ return size;
+}
+
+void tegra_iovmm_free_client(struct tegra_iovmm_client *client)
+{
+ struct tegra_iovmm_device *dev;
+ struct tegra_iovmm_domain *domain;
+
+ if (!client)
+ return;
+
+ BUG_ON(!client->domain || !client->domain->dev);
+
+ domain = client->domain;
+ dev = domain->dev;
+
+ if (test_and_clear_bit(CL_locked, &client->flags)) {
+ pr_err("freeing locked client %s\n", client->name);
+ if (!atomic_dec_return(&domain->locks)) {
+ down_write(&domain->map_lock);
+ if (dev->ops->unlock_domain)
+ dev->ops->unlock_domain(domain, client);
+ up_write(&domain->map_lock);
+ wake_up(&domain->delay_lock);
+ }
+ }
+ mutex_lock(&iovmm_group_list_lock);
+ if (!atomic_dec_return(&domain->clients))
+ if (dev->ops->free_domain)
+ dev->ops->free_domain(domain, client);
+ list_del(&client->list);
+ if (list_empty(&client->group->client_list)) {
+ list_del(&client->group->group_list);
+ kfree(client->group->name);
+ kfree(client->group);
+ }
+ kfree(client->name);
+ kfree(client);
+ mutex_unlock(&iovmm_group_list_lock);
+}
+
+struct tegra_iovmm_client *tegra_iovmm_alloc_client(const char *name,
+ const char *share_group, struct miscdevice *misc_dev)
+{
+ struct tegra_iovmm_client *c = kzalloc(sizeof(*c), GFP_KERNEL);
+ struct iovmm_share_group *grp = NULL;
+ struct tegra_iovmm_device *dev;
+
+ if (!c)
+ return NULL;
+ c->name = kstrdup(name, GFP_KERNEL);
+ if (!c->name)
+ goto fail;
+ c->misc_dev = misc_dev;
+
+ mutex_lock(&iovmm_group_list_lock);
+ if (share_group) {
+ list_for_each_entry(grp, &iovmm_groups, group_list) {
+ if (grp->name && !strcmp(grp->name, share_group))
+ break;
+ }
+ }
+ if (!grp || strcmp(grp->name, share_group)) {
+ grp = kzalloc(sizeof(*grp), GFP_KERNEL);
+ if (!grp)
+ goto fail_lock;
+ grp->name =
+ share_group ? kstrdup(share_group, GFP_KERNEL) : NULL;
+ if (share_group && !grp->name) {
+ kfree(grp);
+ goto fail_lock;
+ }
+ list_for_each_entry(dev, &iovmm_devices, list) {
+ grp->domain = dev->ops->alloc_domain(dev, c);
+ if (grp->domain)
+ break;
+ }
+ if (!grp->domain) {
+ pr_err("%s: alloc_domain failed for %s\n",
+ __func__, c->name);
+ dump_stack();
+ kfree(grp->name);
+ kfree(grp);
+ grp = NULL;
+ goto fail_lock;
+ }
+ spin_lock_init(&grp->lock);
+ INIT_LIST_HEAD(&grp->client_list);
+ list_add_tail(&grp->group_list, &iovmm_groups);
+ }
+
+ atomic_inc(&grp->domain->clients);
+ c->group = grp;
+ c->domain = grp->domain;
+ spin_lock(&grp->lock);
+ list_add_tail(&c->list, &grp->client_list);
+ spin_unlock(&grp->lock);
+ mutex_unlock(&iovmm_group_list_lock);
+ return c;
+
+fail_lock:
+ mutex_unlock(&iovmm_group_list_lock);
+fail:
+ if (c)
+ kfree(c->name);
+ kfree(c);
+ return NULL;
+}
+
+int tegra_iovmm_register(struct tegra_iovmm_device *dev)
+{
+ BUG_ON(!dev);
+ mutex_lock(&iovmm_group_list_lock);
+ if (list_empty(&iovmm_devices)) {
+ iovmm_cache = KMEM_CACHE(tegra_iovmm_block, 0);
+ if (!iovmm_cache) {
+ pr_err("%s: failed to make kmem cache\n", __func__);
+ mutex_unlock(&iovmm_group_list_lock);
+ return -ENOMEM;
+ }
+ create_proc_read_entry("iovmminfo", S_IRUGO, NULL,
+ tegra_iovmm_read_proc, NULL);
+ }
+ list_add_tail(&dev->list, &iovmm_devices);
+ mutex_unlock(&iovmm_group_list_lock);
+ pr_info("%s: added %s\n", __func__, dev->name);
+ return 0;
+}
+
+int tegra_iovmm_unregister(struct tegra_iovmm_device *dev)
+{
+ mutex_lock(&iovmm_group_list_lock);
+ list_del(&dev->list);
+ mutex_unlock(&iovmm_group_list_lock);
+ return 0;
+}
+
+static int tegra_iovmm_suspend(void)
+{
+ int rc = 0;
+ struct tegra_iovmm_device *dev;
+
+ list_for_each_entry(dev, &iovmm_devices, list) {
+ if (!dev->ops->suspend)
+ continue;
+
+ rc = dev->ops->suspend(dev);
+ if (rc) {
+ pr_err("%s: %s suspend returned %d\n",
+ __func__, dev->name, rc);
+ return rc;
+ }
+ }
+ return 0;
+}
+
+static void tegra_iovmm_resume(void)
+{
+ struct tegra_iovmm_device *dev;
+
+ list_for_each_entry(dev, &iovmm_devices, list) {
+ if (dev->ops->resume)
+ dev->ops->resume(dev);
+ }
+}
+
+static struct syscore_ops tegra_iovmm_syscore_ops = {
+ .suspend = tegra_iovmm_suspend,
+ .resume = tegra_iovmm_resume,
+};
+
+static __init int tegra_iovmm_syscore_init(void)
+{
+ register_syscore_ops(&tegra_iovmm_syscore_ops);
+ return 0;
+}
+subsys_initcall(tegra_iovmm_syscore_init);
diff --git a/arch/arm/mach-tegra/irq.c b/arch/arm/mach-tegra/irq.c
index 4956c3cea731..e989d19a2fa4 100644
--- a/arch/arm/mach-tegra/irq.c
+++ b/arch/arm/mach-tegra/irq.c
@@ -4,7 +4,7 @@
* Author:
* Colin Cross <ccross@android.com>
*
- * Copyright (C) 2010, NVIDIA Corporation
+ * Copyright (C) 2010-2011, NVIDIA Corporation
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@@ -21,12 +21,15 @@
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/io.h>
+#include <linux/syscore_ops.h>
#include <asm/hardware/gic.h>
#include <mach/iomap.h>
+#include <mach/legacy_irq.h>
#include "board.h"
+#include "pm-irq.h"
#define INT_SYS_NR (INT_GPIO_BASE - INT_PRI_BASE)
#define INT_SYS_SZ (INT_SEC_BASE - INT_PRI_BASE)
@@ -47,7 +50,7 @@
#define ICTLR_COP_IER_CLR 0x38
#define ICTLR_COP_IEP_CLASS 0x3c
-#define NUM_ICTLRS 4
+#define NUM_ICTLRS (INT_MAIN_NR/32)
#define FIRST_LEGACY_IRQ 32
static void __iomem *ictlr_reg_base[] = {
@@ -55,8 +58,17 @@ static void __iomem *ictlr_reg_base[] = {
IO_ADDRESS(TEGRA_SECONDARY_ICTLR_BASE),
IO_ADDRESS(TEGRA_TERTIARY_ICTLR_BASE),
IO_ADDRESS(TEGRA_QUATERNARY_ICTLR_BASE),
+#if (NUM_ICTLRS > 4)
+ IO_ADDRESS(TEGRA_QUINARY_ICTLR_BASE),
+#endif
};
+#ifdef CONFIG_PM_SLEEP
+static u32 cop_ier[NUM_ICTLRS];
+static u32 cpu_ier[NUM_ICTLRS];
+static u32 cpu_iep[NUM_ICTLRS];
+#endif
+
static inline void tegra_irq_write_mask(unsigned int irq, unsigned long reg)
{
void __iomem *base;
@@ -113,6 +125,70 @@ static int tegra_retrigger(struct irq_data *d)
return 1;
}
+static int tegra_set_type(struct irq_data *d, unsigned int flow_type)
+{
+ return tegra_pm_irq_set_wake_type(d->irq, flow_type);
+}
+
+
+#ifdef CONFIG_PM_SLEEP
+static int tegra_set_wake(struct irq_data *d, unsigned int enable)
+{
+ return tegra_pm_irq_set_wake(d->irq, enable);
+}
+
+static int tegra_legacy_irq_suspend(void)
+{
+ unsigned long flags;
+ int i;
+
+ local_irq_save(flags);
+ for (i = 0; i < NUM_ICTLRS; i++) {
+ void __iomem *ictlr = ictlr_reg_base[i];
+ cpu_ier[i] = readl(ictlr + ICTLR_CPU_IER);
+ cpu_iep[i] = readl(ictlr + ICTLR_CPU_IEP_CLASS);
+ cop_ier[i] = readl(ictlr + ICTLR_COP_IER);
+ writel(~0, ictlr + ICTLR_COP_IER_CLR);
+ }
+ local_irq_restore(flags);
+
+ return 0;
+}
+
+static void tegra_legacy_irq_resume(void)
+{
+ unsigned long flags;
+ int i;
+
+ local_irq_save(flags);
+ for (i = 0; i < NUM_ICTLRS; i++) {
+ void __iomem *ictlr = ictlr_reg_base[i];
+ writel(cpu_iep[i], ictlr + ICTLR_CPU_IEP_CLASS);
+ writel(~0ul, ictlr + ICTLR_CPU_IER_CLR);
+ writel(cpu_ier[i], ictlr + ICTLR_CPU_IER_SET);
+ writel(0, ictlr + ICTLR_COP_IEP_CLASS);
+ writel(~0ul, ictlr + ICTLR_COP_IER_CLR);
+ writel(cop_ier[i], ictlr + ICTLR_COP_IER_SET);
+ }
+ local_irq_restore(flags);
+}
+
+static struct syscore_ops tegra_legacy_irq_syscore_ops = {
+ .suspend = tegra_legacy_irq_suspend,
+ .resume = tegra_legacy_irq_resume,
+};
+
+static int tegra_legacy_irq_syscore_init(void)
+{
+ register_syscore_ops(&tegra_legacy_irq_syscore_ops);
+
+ return 0;
+}
+subsys_initcall(tegra_legacy_irq_syscore_init);
+#else
+#define tegra_set_wake NULL
+#endif
+
void __init tegra_init_irq(void)
{
int i;
@@ -121,6 +197,7 @@ void __init tegra_init_irq(void)
void __iomem *ictlr = ictlr_reg_base[i];
writel(~0, ictlr + ICTLR_CPU_IER_CLR);
writel(0, ictlr + ICTLR_CPU_IEP_CLASS);
+ writel(~0, ictlr + ICTLR_CPU_IEP_FIR_CLR);
}
gic_arch_extn.irq_ack = tegra_ack;
@@ -128,7 +205,21 @@ void __init tegra_init_irq(void)
gic_arch_extn.irq_mask = tegra_mask;
gic_arch_extn.irq_unmask = tegra_unmask;
gic_arch_extn.irq_retrigger = tegra_retrigger;
+ gic_arch_extn.irq_set_type = tegra_set_type;
+ gic_arch_extn.irq_set_wake = tegra_set_wake;
+ gic_arch_extn.flags = IRQCHIP_MASK_ON_SUSPEND;
gic_init(0, 29, IO_ADDRESS(TEGRA_ARM_INT_DIST_BASE),
IO_ADDRESS(TEGRA_ARM_PERIF_BASE + 0x100));
}
+
+void tegra_init_legacy_irq_cop(void)
+{
+ int i;
+
+ for (i = 0; i < NUM_ICTLRS; i++) {
+ void __iomem *ictlr = ictlr_reg_base[i];
+ writel(~0, ictlr + ICTLR_COP_IER_CLR);
+ writel(0, ictlr + ICTLR_COP_IEP_CLASS);
+ }
+}
diff --git a/arch/arm/mach-tegra/kfuse.c b/arch/arm/mach-tegra/kfuse.c
new file mode 100644
index 000000000000..1b8f033df48f
--- /dev/null
+++ b/arch/arm/mach-tegra/kfuse.c
@@ -0,0 +1,105 @@
+/*
+ * arch/arm/mach-tegra/kfuse.c
+ *
+ * Copyright (C) 2010-2011 NVIDIA Corporation.
+ *
+ * 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.
+ *
+ */
+
+/* The kfuse block stores downstream and upstream HDCP keys for use by HDMI
+ * module.
+ */
+
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+
+#include <mach/iomap.h>
+#include <mach/kfuse.h>
+
+#include "clock.h"
+#include "apbio.h"
+
+static struct clk *kfuse_clk = NULL;
+
+/* register definition */
+#define KFUSE_STATE 0x80
+#define KFUSE_STATE_DONE (1u << 16)
+#define KFUSE_STATE_CRCPASS (1u << 17)
+#define KFUSE_KEYADDR 0x88
+#define KFUSE_KEYADDR_AUTOINC (1u << 16)
+#define KFUSE_KEYS 0x8c
+
+static inline u32 tegra_kfuse_readl(unsigned long offset)
+{
+ return tegra_apb_readl(TEGRA_KFUSE_BASE + offset);
+}
+
+static inline void tegra_kfuse_writel(u32 value, unsigned long offset)
+{
+ tegra_apb_writel(value, TEGRA_KFUSE_BASE + offset);
+}
+
+static int wait_for_done(void)
+{
+ u32 reg;
+ int retries = 50;
+ do {
+ reg = tegra_kfuse_readl(KFUSE_STATE);
+ if (reg & KFUSE_STATE_DONE)
+ return 0;
+ msleep(10);
+ } while(--retries);
+ return -ETIMEDOUT;
+}
+
+/* read up to KFUSE_DATA_SZ bytes into dest.
+ * always starts at the first kfuse.
+ */
+int tegra_kfuse_read(void *dest, size_t len)
+{
+ u32 v;
+ unsigned cnt;
+
+ if (len > KFUSE_DATA_SZ)
+ return -EINVAL;
+
+ if (kfuse_clk == NULL) {
+ kfuse_clk = tegra_get_clock_by_name("kfuse");
+ if (IS_ERR_OR_NULL(kfuse_clk)) {
+ pr_err("kfuse: can't get kfuse clock\n");
+ return -EINVAL;
+ }
+ }
+
+ clk_enable(kfuse_clk);
+
+ tegra_kfuse_writel(KFUSE_KEYADDR_AUTOINC, KFUSE_KEYADDR);
+ wait_for_done();
+
+ if ((tegra_kfuse_readl(KFUSE_STATE) & KFUSE_STATE_CRCPASS) == 0) {
+ pr_err("kfuse: crc failed\n");
+ clk_disable(kfuse_clk);
+ return -EIO;
+ }
+
+ for (cnt = 0; cnt < len; cnt += 4) {
+ v = tegra_kfuse_readl(KFUSE_KEYS);
+ memcpy(dest + cnt, &v, sizeof v);
+ }
+
+ clk_disable(kfuse_clk);
+
+ return 0;
+}
diff --git a/arch/arm/mach-tegra/latency_allowance.c b/arch/arm/mach-tegra/latency_allowance.c
new file mode 100644
index 000000000000..ab2459bc4ca8
--- /dev/null
+++ b/arch/arm/mach-tegra/latency_allowance.c
@@ -0,0 +1,593 @@
+/*
+ * arch/arm/mach-tegra/latency_allowance.c
+ *
+ * Copyright (C) 2011 NVIDIA Corporation
+ *
+ * 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/types.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/err.h>
+#include <linux/spinlock_types.h>
+#include <linux/spinlock.h>
+#include <linux/stringify.h>
+#include <asm/bug.h>
+#include <asm/io.h>
+#include <asm/string.h>
+#include <mach/iomap.h>
+#include <mach/io.h>
+#include <mach/latency_allowance.h>
+
+#define MC_ARB_OVERRIDE 0xe8
+#define GLOBAL_LATENCY_SCALING_ENABLE_BIT 7
+
+#define MC_LA_AFI_0 0x2e0
+#define MC_LA_AVPC_ARM7_0 0x2e4
+#define MC_LA_DC_0 0x2e8
+#define MC_LA_DC_1 0x2ec
+#define MC_LA_DC_2 0x2f0
+#define MC_LA_DCB_0 0x2f4
+#define MC_LA_DCB_1 0x2f8
+#define MC_LA_DCB_2 0x2fc
+#define MC_LA_EPP_0 0x300
+#define MC_LA_EPP_1 0x304
+#define MC_LA_G2_0 0x308
+#define MC_LA_G2_1 0x304
+#define MC_LA_HC_0 0x310
+#define MC_LA_HC_1 0x314
+#define MC_LA_HDA_0 0x318
+#define MC_LA_ISP_0 0x31C
+#define MC_LA_MPCORE_0 0x320
+#define MC_LA_MPCORELP_0 0x324
+#define MC_LA_MPE_0 0x328
+#define MC_LA_MPE_1 0x32c
+#define MC_LA_MPE_2 0x330
+#define MC_LA_NV_0 0x334
+#define MC_LA_NV_1 0x338
+#define MC_LA_NV2_0 0x33c
+#define MC_LA_NV2_1 0x340
+#define MC_LA_PPCS_0 0x344
+#define MC_LA_PPCS_1 0x348
+#define MC_LA_PTC_0 0x34c
+#define MC_LA_SATA_0 0x350
+#define MC_LA_VDE_0 0x354
+#define MC_LA_VDE_1 0x358
+#define MC_LA_VDE_2 0x35c
+#define MC_LA_VDE_3 0x360
+#define MC_LA_VI_0 0x364
+#define MC_LA_VI_1 0x368
+#define MC_LA_VI_2 0x36c
+
+#define DS_DISP_MCCIF_DISPLAY0A_HYST (0x481 * 4)
+#define DS_DISP_MCCIF_DISPLAY0B_HYST (0x482 * 4)
+#define DS_DISP_MCCIF_DISPLAY0C_HYST (0x483 * 4)
+#define DS_DISP_MCCIF_DISPLAY1B_HYST (0x484 * 4)
+
+#define DS_DISP_MCCIF_DISPLAY0AB_HYST (0x481 * 4)
+#define DS_DISP_MCCIF_DISPLAY0BB_HYST (0x482 * 4)
+#define DS_DISP_MCCIF_DISPLAY0CB_HYST (0x483 * 4)
+#define DS_DISP_MCCIF_DISPLAY1BB_HYST (0x484 * 4)
+
+#define VI_MCCIF_VIWSB_HYST (0x9a * 4)
+#define VI_MCCIF_VIWU_HYST (0x9b * 4)
+#define VI_MCCIF_VIWV_HYST (0x9c * 4)
+#define VI_MCCIF_VIWY_HYST (0x9d * 4)
+
+#define VI_TIMEOUT_WOCAL_VI (0x70 * 4)
+#define VI_RESERVE_3 (0x97 * 4)
+#define VI_RESERVE_4 (0x98 * 4)
+
+/* maximum valid value for latency allowance */
+#define MC_LA_MAX_VALUE 255
+
+#define ENABLE_LA_DEBUG 0
+#define TEST_LA_CODE 0
+
+#define la_debug(fmt, ...) \
+ if (ENABLE_LA_DEBUG) { \
+ printk(KERN_INFO pr_fmt(fmt), ##__VA_ARGS__); \
+ }
+
+static struct dentry *latency_debug_dir;
+
+struct la_client_info {
+ unsigned int fifo_size_in_atoms;
+ unsigned int expiration_in_ns; /* worst case expiration value */
+ unsigned long reg_addr;
+ unsigned long mask;
+ unsigned long shift;
+ enum tegra_la_id id;
+ char *name;
+ bool scaling_supported;
+};
+
+static DEFINE_SPINLOCK(safety_lock);
+
+static const int ns_per_tick = 30;
+/* fifo atom size in bytes for non-fdc clients*/
+static const int normal_atom_size = 16;
+/* fifo atom size in bytes for fdc clients*/
+static const int fdc_atom_size = 32;
+
+#define MC_RA(r) \
+ ((u32)IO_ADDRESS(TEGRA_MC_BASE) + (MC_##r))
+#define RA(r) \
+ ((u32)IO_ADDRESS(TEGRA_MC_BASE) + (MC_LA_##r))
+
+#define MASK(x) \
+ ((0xFFFFFFFFUL >> (31 - (1 ? x) + (0 ? x))) << (0 ? x))
+#define SHIFT(x) \
+ (0 ? x)
+#define ID(id) \
+ TEGRA_LA_##id
+
+#define LA_INFO(f, e, a, r, id, ss) \
+{f, e, RA(a), MASK(r), SHIFT(r), ID(id), __stringify(id), ss}
+
+/*
+ * The rule for getting the fifo_size_in_atoms is:
+ * 1.If REORDER_DEPTH exists, use it(default is overridden).
+ * 2.Else if (write_client) use RFIFO_DEPTH.
+ * 3.Else (read client) use RDFIFO_DEPTH.
+ * Refer to project.h file.
+ */
+struct la_client_info la_info[] = {
+ LA_INFO(32, 150, AFI_0, 7 : 0, AFIR, false),
+ LA_INFO(32, 150, AFI_0, 23 : 16, AFIW, false),
+ LA_INFO(2, 150, AVPC_ARM7_0, 7 : 0, AVPC_ARM7R, false),
+ LA_INFO(2, 150, AVPC_ARM7_0, 23 : 16, AVPC_ARM7W, false),
+ LA_INFO(128, 1050, DC_0, 7 : 0, DISPLAY_0A, true),
+ LA_INFO(64, 1050, DC_0, 23 : 16, DISPLAY_0B, true),
+ LA_INFO(128, 1050, DC_1, 7 : 0, DISPLAY_0C, true),
+ LA_INFO(64, 1050, DC_1, 23 : 16, DISPLAY_1B, true),
+ LA_INFO(2, 1050, DC_2, 7 : 0, DISPLAY_HC, false),
+ LA_INFO(128, 1050, DCB_0, 7 : 0, DISPLAY_0AB, true),
+ LA_INFO(64, 1050, DCB_0, 23 : 16, DISPLAY_0BB, true),
+ LA_INFO(128, 1050, DCB_1, 7 : 0, DISPLAY_0CB, true),
+ LA_INFO(64, 1050, DCB_1, 23 : 16, DISPLAY_1BB, true),
+ LA_INFO(2, 1050, DCB_2, 7 : 0, DISPLAY_HCB, false),
+ LA_INFO(8, 150, EPP_0, 7 : 0, EPPUP, false),
+ LA_INFO(64, 150, EPP_0, 23 : 16, EPPU, false),
+ LA_INFO(64, 150, EPP_1, 7 : 0, EPPV, false),
+ LA_INFO(64, 150, EPP_1, 23 : 16, EPPY, false),
+ LA_INFO(64, 150, G2_0, 7 : 0, G2PR, false),
+ LA_INFO(64, 150, G2_0, 23 : 16, G2SR, false),
+ LA_INFO(48, 150, G2_1, 7 : 0, G2DR, false),
+ LA_INFO(128, 150, G2_1, 23 : 16, G2DW, false),
+ LA_INFO(16, 150, HC_0, 7 : 0, HOST1X_DMAR, false),
+ LA_INFO(8, 150, HC_0, 23 : 16, HOST1XR, false),
+ LA_INFO(32, 150, HC_1, 7 : 0, HOST1XW, false),
+ LA_INFO(16, 150, HDA_0, 7 : 0, HDAR, false),
+ LA_INFO(16, 150, HDA_0, 23 : 16, HDAW, false),
+ LA_INFO(64, 150, ISP_0, 7 : 0, ISPW, false),
+ LA_INFO(14, 150, MPCORE_0, 7 : 0, MPCORER, false),
+ LA_INFO(24, 150, MPCORE_0, 23 : 16, MPCOREW, false),
+ LA_INFO(14, 150, MPCORELP_0, 7 : 0, MPCORE_LPR, false),
+ LA_INFO(24, 150, MPCORELP_0, 23 : 16, MPCORE_LPW, false),
+ LA_INFO(8, 150, MPE_0, 7 : 0, MPE_UNIFBR, false),
+ LA_INFO(2, 150, MPE_0, 23 : 16, MPE_IPRED, false),
+ LA_INFO(64, 150, MPE_1, 7 : 0, MPE_AMEMRD, false),
+ LA_INFO(8, 150, MPE_1, 23 : 16, MPE_CSRD, false),
+ LA_INFO(8, 150, MPE_2, 7 : 0, MPE_UNIFBW, false),
+ LA_INFO(8, 150, MPE_2, 23 : 16, MPE_CSWR, false),
+ LA_INFO(48, 150, NV_0, 7 : 0, FDCDRD, false),
+ LA_INFO(64, 150, NV_0, 23 : 16, IDXSRD, false),
+ LA_INFO(64, 150, NV_1, 7 : 0, TEXSRD, false),
+ LA_INFO(48, 150, NV_1, 23 : 16, FDCDWR, false),
+ LA_INFO(48, 150, NV2_0, 7 : 0, FDCDRD2, false),
+ LA_INFO(64, 150, NV2_0, 23 : 16, IDXSRD2, false),
+ LA_INFO(64, 150, NV2_1, 7 : 0, TEXSRD2, false),
+ LA_INFO(48, 150, NV2_1, 23 : 16, FDCDWR2, false),
+ LA_INFO(2, 150, PPCS_0, 7 : 0, PPCS_AHBDMAR, false),
+ LA_INFO(8, 150, PPCS_0, 23 : 16, PPCS_AHBSLVR, false),
+ LA_INFO(2, 150, PPCS_1, 7 : 0, PPCS_AHBDMAW, false),
+ LA_INFO(4, 150, PPCS_1, 23 : 16, PPCS_AHBSLVW, false),
+ LA_INFO(2, 150, PTC_0, 7 : 0, PTCR, false),
+ LA_INFO(32, 150, SATA_0, 7 : 0, SATAR, false),
+ LA_INFO(32, 150, SATA_0, 23 : 16, SATAW, false),
+ LA_INFO(8, 150, VDE_0, 7 : 0, VDE_BSEVR, false),
+ LA_INFO(4, 150, VDE_0, 23 : 16, VDE_MBER, false),
+ LA_INFO(16, 150, VDE_1, 7 : 0, VDE_MCER, false),
+ LA_INFO(16, 150, VDE_1, 23 : 16, VDE_TPER, false),
+ LA_INFO(4, 150, VDE_2, 7 : 0, VDE_BSEVW, false),
+ LA_INFO(16, 150, VDE_2, 23 : 16, VDE_DBGW, false),
+ LA_INFO(2, 150, VDE_3, 7 : 0, VDE_MBEW, false),
+ LA_INFO(16, 150, VDE_3, 23 : 16, VDE_TPMW, false),
+ LA_INFO(8, 1050, VI_0, 7 : 0, VI_RUV, false),
+ LA_INFO(64, 1050, VI_0, 23 : 16, VI_WSB, true),
+ LA_INFO(64, 1050, VI_1, 7 : 0, VI_WU, true),
+ LA_INFO(64, 1050, VI_1, 23 : 16, VI_WV, true),
+ LA_INFO(64, 1050, VI_2, 7 : 0, VI_WY, true),
+
+/* end of list. */
+ LA_INFO(0, 0, AFI_0, 0 : 0, MAX_ID, false)
+};
+
+struct la_scaling_info {
+ unsigned int threshold_low;
+ unsigned int threshold_mid;
+ unsigned int threshold_high;
+ int scaling_ref_count;
+ int actual_la_to_set;
+ int la_set;
+};
+
+struct la_scaling_reg_info {
+ enum tegra_la_id id;
+ unsigned int tl_reg_addr;
+ unsigned int tl_mask;
+ unsigned int tl_shift;
+ unsigned int tm_reg_addr;
+ unsigned int tm_mask;
+ unsigned int tm_shift;
+ unsigned int th_reg_addr;
+ unsigned int th_mask;
+ unsigned int th_shift;
+};
+
+#define DISP1_RA(r) \
+ ((u32)IO_ADDRESS(TEGRA_DISPLAY_BASE) + DS_DISP_MCCIF_##r##_HYST)
+#define DISP2_RA(r) \
+ ((u32)IO_ADDRESS(TEGRA_DISPLAY2_BASE) + DS_DISP_MCCIF_##r##_HYST)
+
+#define DISP_SCALING_REG_INFO(id, r, ra) \
+ { \
+ ID(id), \
+ ra(r), MASK(15 : 8), SHIFT(15 : 8), \
+ ra(r), MASK(23 : 16), SHIFT(15 : 8), \
+ ra(r), MASK(7 : 0), SHIFT(15 : 8) \
+ }
+
+struct la_scaling_reg_info disp_info[] = {
+ DISP_SCALING_REG_INFO(DISPLAY_0A, DISPLAY0A, DISP1_RA),
+ DISP_SCALING_REG_INFO(DISPLAY_0B, DISPLAY0B, DISP1_RA),
+ DISP_SCALING_REG_INFO(DISPLAY_0C, DISPLAY0C, DISP1_RA),
+ DISP_SCALING_REG_INFO(DISPLAY_1B, DISPLAY1B, DISP1_RA),
+ DISP_SCALING_REG_INFO(MAX_ID, DISPLAY1B, DISP1_RA), /*dummy entry*/
+ DISP_SCALING_REG_INFO(DISPLAY_0AB, DISPLAY0AB, DISP2_RA),
+ DISP_SCALING_REG_INFO(DISPLAY_0BB, DISPLAY0BB, DISP2_RA),
+ DISP_SCALING_REG_INFO(DISPLAY_0CB, DISPLAY0CB, DISP2_RA),
+ DISP_SCALING_REG_INFO(DISPLAY_1BB, DISPLAY1BB, DISP2_RA),
+};
+
+#define VI_TH_RA(r) \
+ ((u32)IO_ADDRESS(TEGRA_VI_BASE) + VI_MCCIF_##r##_HYST)
+#define VI_TM_RA(r) \
+ ((u32)IO_ADDRESS(TEGRA_VI_BASE) + VI_TIMEOUT_WOCAL_VI)
+#define VI_TL_RA(r) \
+ ((u32)IO_ADDRESS(TEGRA_VI_BASE) + VI_RESERVE_##r)
+
+struct la_scaling_reg_info vi_info[] = {
+ {
+ ID(VI_WSB),
+ VI_TL_RA(4), MASK(7 : 0), SHIFT(7 : 0),
+ VI_TM_RA(0), MASK(7 : 0), SHIFT(7 : 0),
+ VI_TH_RA(VIWSB), MASK(7 : 0), SHIFT(7 : 0)
+ },
+ {
+ ID(VI_WU),
+ VI_TL_RA(3), MASK(15 : 8), SHIFT(15 : 8),
+ VI_TM_RA(0), MASK(15 : 8), SHIFT(15 : 8),
+ VI_TH_RA(VIWU), MASK(7 : 0), SHIFT(7 : 0)
+ },
+ {
+ ID(VI_WV),
+ VI_TL_RA(3), MASK(7 : 0), SHIFT(7 : 0),
+ VI_TM_RA(0), MASK(23 : 16), SHIFT(23 : 16),
+ VI_TH_RA(VIWV), MASK(7 : 0), SHIFT(7 : 0)
+ },
+ {
+ ID(VI_WY),
+ VI_TL_RA(4), MASK(15 : 8), SHIFT(15 : 8),
+ VI_TM_RA(0), MASK(31 : 24), SHIFT(31 : 24),
+ VI_TH_RA(VIWY), MASK(7 : 0), SHIFT(7 : 0)
+ }
+};
+
+static struct la_scaling_info scaling_info[TEGRA_LA_MAX_ID];
+static int la_scaling_enable_count;
+
+#define VALIDATE_ID(id) \
+ do { \
+ if (id >= TEGRA_LA_MAX_ID) \
+ return -EINVAL; \
+ BUG_ON(la_info[id].id != id); \
+ } while (0)
+
+#define VALIDATE_BW(bw_in_mbps) \
+ do { \
+ if (bw_in_mbps >= 4096) \
+ return -EINVAL; \
+ } while (0)
+
+#define VALIDATE_THRESHOLDS(tl, tm, th) \
+ do { \
+ if (tl > 100 || tm > 100 || th > 100) \
+ return -EINVAL; \
+ } while (0)
+
+static void set_thresholds(struct la_scaling_reg_info *info,
+ enum tegra_la_id id)
+{
+ unsigned long reg_read;
+ unsigned long reg_write;
+ unsigned int thresh_low;
+ unsigned int thresh_mid;
+ unsigned int thresh_high;
+ int la_set;
+
+ reg_read = readl(la_info[id].reg_addr);
+ la_set = (reg_read & la_info[id].mask) >> la_info[id].shift;
+ /* la should be set before enabling scaling. */
+ BUG_ON(la_set != scaling_info[id].la_set);
+
+ thresh_low = (scaling_info[id].threshold_low * la_set) / 100;
+ thresh_mid = (scaling_info[id].threshold_mid * la_set) / 100;
+ thresh_high = (scaling_info[id].threshold_high * la_set) / 100;
+ la_debug("%s: la_set=%d, thresh_low=%d(%d%%), thresh_mid=%d(%d%%),"
+ " thresh_high=%d(%d%%) ", __func__, la_set,
+ thresh_low, scaling_info[id].threshold_low,
+ thresh_mid, scaling_info[id].threshold_mid,
+ thresh_high, scaling_info[id].threshold_high);
+
+ reg_read = readl(info->tl_reg_addr);
+ reg_write = (reg_read & ~info->tl_mask) |
+ (thresh_low << info->tl_shift);
+ writel(reg_write, info->tl_reg_addr);
+ la_debug("reg_addr=0x%x, read=0x%x, write=0x%x",
+ (u32)info->tl_reg_addr, (u32)reg_read, (u32)reg_write);
+
+ reg_read = readl(info->tm_reg_addr);
+ reg_write = (reg_read & ~info->tm_mask) |
+ (thresh_mid << info->tm_shift);
+ writel(reg_write, info->tm_reg_addr);
+ la_debug("reg_addr=0x%x, read=0x%x, write=0x%x",
+ (u32)info->tm_reg_addr, (u32)reg_read, (u32)reg_write);
+
+ reg_read = readl(info->th_reg_addr);
+ reg_write = (reg_read & ~info->th_mask) |
+ (thresh_high << info->th_shift);
+ writel(reg_write, info->th_reg_addr);
+ la_debug("reg_addr=0x%x, read=0x%x, write=0x%x",
+ (u32)info->th_reg_addr, (u32)reg_read, (u32)reg_write);
+}
+
+static void set_disp_latency_thresholds(enum tegra_la_id id)
+{
+ set_thresholds(&disp_info[id - ID(DISPLAY_0A)], id);
+}
+
+static void set_vi_latency_thresholds(enum tegra_la_id id)
+{
+ set_thresholds(&vi_info[id - ID(VI_WSB)], id);
+}
+
+/* Sets latency allowance based on clients memory bandwitdh requirement.
+ * Bandwidth passed is in mega bytes per second.
+ */
+int tegra_set_latency_allowance(enum tegra_la_id id,
+ unsigned int bandwidth_in_mbps)
+{
+ int ideal_la;
+ int la_to_set;
+ unsigned long reg_read;
+ unsigned long reg_write;
+ int bytes_per_atom = normal_atom_size;
+ struct la_client_info *ci;
+
+ VALIDATE_ID(id);
+ VALIDATE_BW(bandwidth_in_mbps);
+ if (id == ID(FDCDRD) || id == ID(FDCDWR) ||
+ id == ID(FDCDRD2) || id == ID(FDCDWR2))
+ bytes_per_atom = fdc_atom_size;
+
+ ci = &la_info[id];
+
+ if (bandwidth_in_mbps == 0) {
+ la_to_set = MC_LA_MAX_VALUE;
+ } else {
+ ideal_la = (ci->fifo_size_in_atoms * bytes_per_atom * 1000) /
+ (bandwidth_in_mbps * ns_per_tick);
+ la_to_set = ideal_la - (ci->expiration_in_ns/ns_per_tick) - 1;
+ }
+
+ la_debug("\n%s:id=%d,bw=%dmbps, la_to_set=%d",
+ __func__, id, bandwidth_in_mbps, la_to_set);
+ la_to_set = (la_to_set < 0) ? 0 : la_to_set;
+ la_to_set = (la_to_set > MC_LA_MAX_VALUE) ? MC_LA_MAX_VALUE : la_to_set;
+ scaling_info[id].actual_la_to_set = la_to_set;
+
+ /* until display can use latency allowance scaling, use a more
+ * aggressive LA setting. Bug 862709 */
+ if (id >= ID(DISPLAY_0A) && id <= ID(DISPLAY_HCB))
+ la_to_set /= 3;
+
+ spin_lock(&safety_lock);
+ reg_read = readl(ci->reg_addr);
+ reg_write = (reg_read & ~ci->mask) |
+ (la_to_set << ci->shift);
+ writel(reg_write, ci->reg_addr);
+ scaling_info[id].la_set = la_to_set;
+ la_debug("reg_addr=0x%x, read=0x%x, write=0x%x",
+ (u32)ci->reg_addr, (u32)reg_read, (u32)reg_write);
+ spin_unlock(&safety_lock);
+ return 0;
+}
+
+/* Thresholds for scaling are specified in % of fifo freeness.
+ * If threshold_low is specified as 20%, it means when the fifo free
+ * between 0 to 20%, use la as programmed_la.
+ * If threshold_mid is specified as 50%, it means when the fifo free
+ * between 20 to 50%, use la as programmed_la/2 .
+ * If threshold_high is specified as 80%, it means when the fifo free
+ * between 50 to 80%, use la as programmed_la/4.
+ * When the fifo is free between 80 to 100%, use la as 0(highest priority).
+ */
+int tegra_enable_latency_scaling(enum tegra_la_id id,
+ unsigned int threshold_low,
+ unsigned int threshold_mid,
+ unsigned int threshold_high)
+{
+ unsigned long reg;
+ unsigned long scaling_enable_reg = MC_RA(ARB_OVERRIDE);
+
+ VALIDATE_ID(id);
+ VALIDATE_THRESHOLDS(threshold_low, threshold_mid, threshold_high);
+
+ if (la_info[id].scaling_supported == false)
+ goto exit;
+
+ spin_lock(&safety_lock);
+
+ la_debug("\n%s: id=%d, tl=%d, tm=%d, th=%d", __func__,
+ id, threshold_low, threshold_mid, threshold_high);
+ scaling_info[id].threshold_low = threshold_low;
+ scaling_info[id].threshold_mid = threshold_mid;
+ scaling_info[id].threshold_high = threshold_high;
+ scaling_info[id].scaling_ref_count++;
+
+ if (id >= ID(DISPLAY_0A) && id <= ID(DISPLAY_1BB))
+ set_disp_latency_thresholds(id);
+ else if (id >= ID(VI_WSB) && id <= ID(VI_WY))
+ set_vi_latency_thresholds(id);
+ if (!la_scaling_enable_count++) {
+ reg = readl(scaling_enable_reg);
+ reg |= (1 << GLOBAL_LATENCY_SCALING_ENABLE_BIT);
+ writel(reg, scaling_enable_reg);
+ la_debug("enabled scaling.");
+ }
+ spin_unlock(&safety_lock);
+exit:
+ return 0;
+}
+
+void tegra_disable_latency_scaling(enum tegra_la_id id)
+{
+ unsigned long reg;
+ unsigned long scaling_enable_reg = MC_RA(ARB_OVERRIDE);
+
+ if (id >= TEGRA_LA_MAX_ID)
+ return;
+ BUG_ON(la_info[id].id != id);
+
+ if (la_info[id].scaling_supported == false)
+ return;
+ spin_lock(&safety_lock);
+ la_debug("\n%s: id=%d", __func__, id);
+ scaling_info[id].scaling_ref_count--;
+ BUG_ON(scaling_info[id].scaling_ref_count < 0);
+
+ if (!--la_scaling_enable_count) {
+ reg = readl(scaling_enable_reg);
+ reg = reg & ~(1 << GLOBAL_LATENCY_SCALING_ENABLE_BIT);
+ writel(reg, scaling_enable_reg);
+ la_debug("disabled scaling.");
+ }
+ spin_unlock(&safety_lock);
+}
+
+static int la_regs_show(struct seq_file *s, void *unused)
+{
+ unsigned i;
+ unsigned long la;
+
+ /* iterate the list, but don't print MAX_ID */
+ for (i = 0; i < ARRAY_SIZE(la_info) - 1; i++) {
+ la = (readl(la_info[i].reg_addr) & la_info[i].mask)
+ >> la_info[i].shift;
+ seq_printf(s, "%-16s: %4lu\n", la_info[i].name, la);
+ }
+
+ return 0;
+}
+
+static int dbg_la_regs_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, la_regs_show, inode->i_private);
+}
+
+static const struct file_operations regs_fops = {
+ .open = dbg_la_regs_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int __init tegra_latency_allowance_debugfs_init(void)
+{
+ if (latency_debug_dir)
+ return 0;
+
+ latency_debug_dir = debugfs_create_dir("tegra_latency", NULL);
+
+ debugfs_create_file("la_info", S_IRUGO, latency_debug_dir, NULL,
+ &regs_fops);
+
+ return 0;
+}
+
+late_initcall(tegra_latency_allowance_debugfs_init);
+
+static int __init tegra_latency_allowance_init(void)
+{
+ la_scaling_enable_count = 0;
+ return 0;
+}
+
+core_initcall(tegra_latency_allowance_init);
+
+#if TEST_LA_CODE
+static int __init test_la(void)
+{
+ int err;
+ enum tegra_la_id id = 0;
+ int repeat_count = 5;
+
+ do {
+ for (id = 0; id < TEGRA_LA_MAX_ID; id++) {
+ err = tegra_set_latency_allowance(id, 200);
+ if (err)
+ la_debug("\n***tegra_set_latency_allowance,"
+ " err=%d", err);
+ }
+
+ for (id = 0; id < TEGRA_LA_MAX_ID; id++) {
+ if (id >= ID(DISPLAY_0AB) && id <= ID(DISPLAY_HCB))
+ continue;
+ if (id >= ID(VI_WSB) && id <= ID(VI_WY))
+ continue;
+ err = tegra_enable_latency_scaling(id, 20, 50, 80);
+ if (err)
+ la_debug("\n***tegra_enable_latency_scaling,"
+ " err=%d", err);
+ }
+
+ la_debug("la_scaling_enable_count =%d",
+ la_scaling_enable_count);
+ for (id = 0; id < TEGRA_LA_MAX_ID; id++) {
+ if (id >= ID(DISPLAY_0AB) && id <= ID(DISPLAY_HCB))
+ continue;
+ if (id >= ID(VI_WSB) && id <= ID(VI_WY))
+ continue;
+ tegra_disable_latency_scaling(id);
+ }
+ la_debug("la_scaling_enable_count=%d",
+ la_scaling_enable_count);
+ } while (--repeat_count);
+ return 0;
+}
+
+late_initcall(test_la);
+#endif
diff --git a/arch/arm/mach-tegra/mc.c b/arch/arm/mach-tegra/mc.c
new file mode 100644
index 000000000000..0dff50461a3c
--- /dev/null
+++ b/arch/arm/mach-tegra/mc.c
@@ -0,0 +1,73 @@
+/*
+ * arch/arm/mach-tegra/mc.c
+ *
+ * Copyright (C) 2010 Google, Inc.
+ * Copyright (C) 2011 NVIDIA Corporation
+ *
+ * Author:
+ * Erik Gilling <konkers@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/io.h>
+#include <linux/spinlock.h>
+
+#include <mach/iomap.h>
+#include <mach/mc.h>
+
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
+static DEFINE_SPINLOCK(tegra_mc_lock);
+
+void tegra_mc_set_priority(unsigned long client, unsigned long prio)
+{
+ unsigned long mc_base = IO_TO_VIRT(TEGRA_MC_BASE);
+ unsigned long reg = client >> 8;
+ int field = client & 0xff;
+ unsigned long val;
+ unsigned long flags;
+
+ spin_lock_irqsave(&tegra_mc_lock, flags);
+ val = readl(mc_base + reg);
+ val &= ~(TEGRA_MC_PRIO_MASK << field);
+ val |= prio << field;
+ writel(val, mc_base + reg);
+ spin_unlock_irqrestore(&tegra_mc_lock, flags);
+
+}
+
+int tegra_mc_get_tiled_memory_bandwidth_multiplier(void)
+{
+ return 1;
+}
+
+#else
+ /* !!!FIXME!!! IMPLEMENT tegra_mc_set_priority() */
+
+#include "tegra3_emc.h"
+
+/*
+ * If using T30/DDR3, the 2nd 16 bytes part of DDR3 atom is 2nd line and is
+ * discarded in tiling mode.
+ */
+int tegra_mc_get_tiled_memory_bandwidth_multiplier(void)
+{
+ int type;
+
+ type = tegra_emc_get_dram_type();
+ WARN_ONCE(type == -1, "unknown type DRAM because DVFS is disabled\n");
+
+ if (type == DRAM_TYPE_DDR3)
+ return 2;
+ else
+ return 1;
+}
+#endif
diff --git a/arch/arm/mach-tegra/p852/Kconfig b/arch/arm/mach-tegra/p852/Kconfig
new file mode 100644
index 000000000000..ca44f9543be2
--- /dev/null
+++ b/arch/arm/mach-tegra/p852/Kconfig
@@ -0,0 +1,110 @@
+config MACH_P852
+ bool "P852 board"
+ depends on ARCH_TEGRA_2x_SOC
+ help
+ Support for NVIDIA P852 platform
+
+config P852_SKU1
+ bool "P852 SKU1 board"
+ depends on MACH_P852
+ default MACH_P852
+ help
+ Support for NVIDIA P852 SKU1 platform
+
+config P852_SKU1_B00
+ bool "P852 SKU1 rev B board"
+ depends on MACH_P852
+ default MACH_P852
+ help
+ Support for NVIDIA P852 SKU1 B00 platform
+
+config P852_SKU1_C0x
+ bool "P852 SKU1 rev C boards"
+ depends on MACH_P852
+ default MACH_P852
+ help
+ Support for NVIDIA P852 SKU1 C0x platform
+
+config P852_SKU3
+ bool "P852 SKU3 board"
+ depends on MACH_P852
+ default MACH_P852
+ help
+ Support for NVIDIA P852 SKU3 platform
+
+config P852_SKU5_B00
+ bool "P852 SKU5 rev B board"
+ depends on MACH_P852
+ default MACH_P852
+ help
+ Support for NVIDIA P852 SKU5 B00 platform
+
+config P852_SKU5_C01
+ bool "P852 SKU5 rev C board"
+ depends on MACH_P852
+ default MACH_P852
+ help
+ Support for NVIDIA P852 SKU5 C01 platform
+
+config P852_SKU8_B00
+ bool "P852 SKU8 rev B board"
+ depends on MACH_P852
+ default MACH_P852
+ help
+ Support for NVIDIA P852 SKU8 B00 platform
+
+config P852_SKU8_C01
+ bool "P852 SKU8 rev C board"
+ depends on MACH_P852
+ default MACH_P852
+ help
+ Support for NVIDIA P852 SKU8 C01 platform
+
+config P852_SKU9_B00
+ bool "P852 SKU9 rev B board"
+ depends on MACH_P852
+ default MACH_P852
+ help
+ Support for NVIDIA P852 SKU9 B00 platform
+
+config P852_SKU9_C01
+ bool "P852 SKU9 rev C board"
+ depends on MACH_P852
+ default MACH_P852
+ help
+ Support for NVIDIA P852 SKU9 C01 platform
+
+config P852_SKU13
+ bool "P852 SKU13 board"
+ depends on MACH_P852
+ default MACH_P852
+ help
+ Support for NVIDIA P852 SKU13 platform
+
+config P852_SKU13_B00
+ bool "P852 SKU13 rev B board"
+ depends on MACH_P852
+ default MACH_P852
+ help
+ Support for NVIDIA P852 SKU23 B00 platform
+
+config P852_SKU23
+ bool "P852 SKU23 board"
+ depends on MACH_P852
+ default MACH_P852
+ help
+ Support for NVIDIA P852 SKU23 platform
+
+config P852_SKU23_B00
+ bool "P852 SKU23 rev B board"
+ depends on MACH_P852
+ default MACH_P852
+ help
+ Support for NVIDIA P852 SKU23 B00 platform
+
+config P852_SKU23_C01
+ bool "P852 SKU23 rev C board"
+ depends on MACH_P852
+ default MACH_P852
+ help
+ Support for NVIDIA P852 SKU23 C01 platform
diff --git a/arch/arm/mach-tegra/p852/Makefile b/arch/arm/mach-tegra/p852/Makefile
new file mode 100644
index 000000000000..2f04ba08f71f
--- /dev/null
+++ b/arch/arm/mach-tegra/p852/Makefile
@@ -0,0 +1,39 @@
+#
+# arch/arm/mach-tegra/p852/Makefile
+#
+# Copyright (c) 2010-2011, NVIDIA Corporation.
+#
+# 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.
+#
+#
+
+obj-${CONFIG_MACH_P852} += board-p852.o
+obj-${CONFIG_MACH_P852} += board-p852-sdhci.o
+obj-${CONFIG_MACH_P852} += board-p852-i2c.o
+obj-${CONFIG_MACH_P852} += board-p852-power.o
+obj-${CONFIG_MACH_P852} += board-p852-pinmux.o
+obj-${CONFIG_MACH_P852} += board-p852-gpio.o
+obj-${CONFIG_MACH_P852} += board-p852-panel.o
+
+obj-${CONFIG_P852_SKU1} += board-p852-sku1.o
+obj-${CONFIG_P852_SKU1_B00} += board-p852-sku1-b00.o
+obj-${CONFIG_P852_SKU1_C0x} += board-p852-sku1-c0x.o
+obj-${CONFIG_P852_SKU3} += board-p852-sku3.o
+obj-${CONFIG_P852_SKU5_B00} += board-p852-sku5-b00.o
+obj-${CONFIG_P852_SKU5_C01} += board-p852-sku5-c01.o
+obj-${CONFIG_P852_SKU8_B00} += board-p852-sku8-b00.o
+obj-${CONFIG_P852_SKU8_C01} += board-p852-sku8-c01.o
+obj-${CONFIG_P852_SKU9_B00} += board-p852-sku9-b00.o
+obj-${CONFIG_P852_SKU9_C01} += board-p852-sku9-c01.o
+obj-${CONFIG_P852_SKU13} += board-p852-sku13.o
+obj-${CONFIG_P852_SKU13_B00} += board-p852-sku13-b00.o
+obj-${CONFIG_P852_SKU23} += board-p852-sku23.o
+obj-${CONFIG_P852_SKU23_B00} += board-p852-sku23-b00.o
+obj-${CONFIG_P852_SKU23_C01} += board-p852-sku23-c01.o
diff --git a/arch/arm/mach-tegra/p852/board-p852-gpio.c b/arch/arm/mach-tegra/p852/board-p852-gpio.c
new file mode 100644
index 000000000000..71f568087c5d
--- /dev/null
+++ b/arch/arm/mach-tegra/p852/board-p852-gpio.c
@@ -0,0 +1,158 @@
+/*
+ * arch/arm/mach-tegra/board-p852-gpio.c
+ *
+ * Copyright (C) 2010-2011 NVIDIA Corporation.
+ *
+ * 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/init.h>
+#include <linux/gpio.h>
+#include <linux/irq.h>
+
+#include "board-p852.h"
+
+static struct gpio p852_sku23_gpios[] = {
+ {TEGRA_GPIO_PW1, GPIOF_OUT_INIT_LOW, "usbpwr0_ena"},
+ {TEGRA_GPIO_PB2, GPIOF_OUT_INIT_LOW, "usbpwr1_ena"},
+ {TEGRA_GPIO_PA0, GPIOF_OUT_INIT_LOW, "a0"},
+ {TEGRA_GPIO_PV2, GPIOF_OUT_INIT_HIGH, "v2"},
+ {TEGRA_GPIO_PT4, GPIOF_OUT_INIT_LOW, "t4"},
+ {TEGRA_GPIO_PD6, GPIOF_OUT_INIT_HIGH, "d6"},
+ {TEGRA_GPIO_PI3, GPIOF_OUT_INIT_LOW, "i3"},
+ {TEGRA_GPIO_PV3, GPIOF_OUT_INIT_HIGH, "v3"},
+ {TEGRA_GPIO_PW4, GPIOF_IN, "w4"},
+ {TEGRA_GPIO_PW5, GPIOF_IN, "w5"},
+ {TEGRA_GPIO_PT1, GPIOF_OUT_INIT_LOW, "t1"},
+ {TEGRA_GPIO_PW3, GPIOF_OUT_INIT_HIGH, "w3"},
+ {TEGRA_GPIO_PD5, GPIOF_IN, "d5"},
+ {TEGRA_GPIO_PBB1, GPIOF_OUT_INIT_LOW, "bb1"},
+};
+
+static struct gpio p852_sku23_b00_gpios[] = {
+ {TEGRA_GPIO_PW1, GPIOF_OUT_INIT_HIGH, "usbpwr0_ena"},
+ {TEGRA_GPIO_PB2, GPIOF_OUT_INIT_HIGH, "usbpwr1_ena"},
+ {TEGRA_GPIO_PA0, GPIOF_OUT_INIT_LOW, "a0"},
+ {TEGRA_GPIO_PV2, GPIOF_OUT_INIT_HIGH, "v2"},
+ {TEGRA_GPIO_PT4, GPIOF_OUT_INIT_LOW, "t4"},
+ {TEGRA_GPIO_PD6, GPIOF_OUT_INIT_HIGH, "d6"},
+ {TEGRA_GPIO_PI3, GPIOF_OUT_INIT_LOW, "i3"},
+ {TEGRA_GPIO_PV3, GPIOF_OUT_INIT_HIGH, "v3"},
+ {TEGRA_GPIO_PW4, GPIOF_IN, "w4"},
+ {TEGRA_GPIO_PW5, GPIOF_IN, "w5"},
+ {TEGRA_GPIO_PT1, GPIOF_OUT_INIT_LOW, "t1"},
+ {TEGRA_GPIO_PW3, GPIOF_OUT_INIT_HIGH, "w3"},
+ {TEGRA_GPIO_PD5, GPIOF_IN, "d5"},
+ {TEGRA_GPIO_PBB1, GPIOF_OUT_INIT_LOW, "bb1"},
+};
+
+static struct gpio p852_sku5_gpios[] = {
+ {TEGRA_GPIO_PW1, GPIOF_OUT_INIT_HIGH, "usbpwr0_ena"},
+ {TEGRA_GPIO_PB2, GPIOF_OUT_INIT_HIGH, "usbpwr1_ena"},
+ {TEGRA_GPIO_PA0, GPIOF_OUT_INIT_LOW, "a0"},
+ {TEGRA_GPIO_PV2, GPIOF_OUT_INIT_HIGH, "v2"},
+ {TEGRA_GPIO_PT4, GPIOF_OUT_INIT_LOW, "t4"},
+ {TEGRA_GPIO_PD6, GPIOF_OUT_INIT_HIGH, "d6"},
+ {TEGRA_GPIO_PI3, GPIOF_OUT_INIT_LOW, "i3"},
+ {TEGRA_GPIO_PV3, GPIOF_OUT_INIT_HIGH, "v3"},
+ {TEGRA_GPIO_PW4, GPIOF_IN, "w4"},
+ {TEGRA_GPIO_PW5, GPIOF_IN, "w5"},
+ {TEGRA_GPIO_PT1, GPIOF_OUT_INIT_LOW, "t1"},
+ {TEGRA_GPIO_PW3, GPIOF_OUT_INIT_HIGH, "w3"},
+ {TEGRA_GPIO_PD5, GPIOF_IN, "d5"},
+ {TEGRA_GPIO_PBB1, GPIOF_OUT_INIT_LOW, "bb1"},
+ {TEGRA_GPIO_PS3, GPIOF_IN, "s3"},
+};
+
+static struct gpio p852_sku8_gpios[] = {
+ {TEGRA_GPIO_PW1, GPIOF_OUT_INIT_HIGH, "w1"},
+ {TEGRA_GPIO_PB2, GPIOF_OUT_INIT_HIGH, "b2"},
+};
+
+static struct gpio p852_sku13_b00_gpios[] = {
+ {TEGRA_GPIO_PW1, GPIOF_OUT_INIT_HIGH, "w1"},
+ {TEGRA_GPIO_PB2, GPIOF_OUT_INIT_HIGH, "b2"},
+ {TEGRA_GPIO_PW2, GPIOF_IN, "w2"},
+ {TEGRA_GPIO_PW3, GPIOF_IN, "w3"},
+ {TEGRA_GPIO_PD5, GPIOF_OUT_INIT_LOW, "d5"},
+ {TEGRA_GPIO_PBB1, GPIOF_OUT_INIT_LOW, "bb1"},
+ {TEGRA_GPIO_PN7, GPIOF_OUT_INIT_LOW, "n7"},
+ {TEGRA_GPIO_PA6, GPIOF_OUT_INIT_HIGH, "a6"},
+ {TEGRA_GPIO_PA7, GPIOF_OUT_INIT_HIGH, "a7"},
+};
+
+static struct gpio p852_gpios[] = {
+ {TEGRA_GPIO_PW1, GPIOF_OUT_INIT_LOW, "w1"},
+ {TEGRA_GPIO_PB2, GPIOF_OUT_INIT_LOW, "b2"},
+ {TEGRA_GPIO_PW2, GPIOF_IN, "w2"},
+ {TEGRA_GPIO_PW3, GPIOF_IN, "w3"},
+ {TEGRA_GPIO_PD5, GPIOF_OUT_INIT_LOW, "d5"},
+ {TEGRA_GPIO_PBB1, GPIOF_OUT_INIT_LOW, "bb1"},
+ {TEGRA_GPIO_PN7, GPIOF_OUT_INIT_LOW, "n7"},
+ {TEGRA_GPIO_PA6, GPIOF_OUT_INIT_HIGH, "a6"},
+ {TEGRA_GPIO_PA7, GPIOF_OUT_INIT_HIGH, "a7"},
+};
+
+void __init p852_gpio_init(void)
+{
+ int pin_count = 0;
+ int i;
+ struct gpio *gpios_info = NULL;
+
+ switch (system_rev) {
+ case P852_SKU23:
+ {
+ gpios_info = p852_sku23_gpios;
+ pin_count = ARRAY_SIZE(p852_sku23_gpios);
+ }
+ break;
+ case P852_SKU23_B00:
+ case P852_SKU23_C01:
+ {
+ gpios_info = p852_sku23_b00_gpios;
+ pin_count = ARRAY_SIZE(p852_sku23_b00_gpios);
+ }
+ break;
+ case P852_SKU5_B00:
+ case P852_SKU5_C01:
+ {
+ gpios_info = p852_sku5_gpios;
+ pin_count = ARRAY_SIZE(p852_sku5_gpios);
+ }
+ break;
+ case P852_SKU8_B00:
+ case P852_SKU8_C01:
+ case P852_SKU9_B00:
+ case P852_SKU9_C01:
+ {
+ gpios_info = p852_sku8_gpios;
+ pin_count = ARRAY_SIZE(p852_sku8_gpios);
+ }
+ break;
+ case P852_SKU13_B00:
+ {
+ gpios_info = p852_sku13_b00_gpios;
+ pin_count = ARRAY_SIZE(p852_sku13_b00_gpios);
+ }
+ break;
+ default:
+ {
+ gpios_info = p852_gpios;
+ pin_count = ARRAY_SIZE(p852_gpios);
+ }
+ }
+
+ gpio_request_array(gpios_info, pin_count);
+ for (i = 0; i < pin_count; i++) {
+ tegra_gpio_enable(gpios_info[i].gpio);
+ gpio_export(gpios_info[i].gpio, true);
+ }
+}
diff --git a/arch/arm/mach-tegra/p852/board-p852-i2c.c b/arch/arm/mach-tegra/p852/board-p852-i2c.c
new file mode 100644
index 000000000000..041ec252b6c1
--- /dev/null
+++ b/arch/arm/mach-tegra/p852/board-p852-i2c.c
@@ -0,0 +1,180 @@
+/*
+ * arch/arm/mach-tegra/board-p852-i2c.c
+ *
+ * Copyright (c) 2010-2011, NVIDIA Corporation.
+ *
+ * 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/resource.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+
+#include <asm/mach-types.h>
+#include <mach/irqs.h>
+#include <mach/iomap.h>
+#include <linux/i2c.h>
+#include <mach/pinmux.h>
+#include <asm/mach-types.h>
+
+#include "board-p852.h"
+
+static struct resource i2c_resource1[] = {
+ [0] = {
+ .start = INT_I2C,
+ .end = INT_I2C,
+ .flags = IORESOURCE_IRQ,
+ },
+ [1] = {
+ .start = TEGRA_I2C_BASE,
+ .end = TEGRA_I2C_BASE + TEGRA_I2C_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct resource i2c_resource2[] = {
+ [0] = {
+ .start = INT_I2C2,
+ .end = INT_I2C2,
+ .flags = IORESOURCE_IRQ,
+ },
+ [1] = {
+ .start = TEGRA_I2C2_BASE,
+ .end = TEGRA_I2C2_BASE + TEGRA_I2C2_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct resource i2c_resource3[] = {
+ [0] = {
+ .start = INT_I2C3,
+ .end = INT_I2C3,
+ .flags = IORESOURCE_IRQ,
+ },
+ [1] = {
+ .start = TEGRA_I2C3_BASE,
+ .end = TEGRA_I2C3_BASE + TEGRA_I2C3_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct resource i2c_resource4[] = {
+ [0] = {
+ .start = INT_DVC,
+ .end = INT_DVC,
+ .flags = IORESOURCE_IRQ,
+ },
+ [1] = {
+ .start = TEGRA_DVC_BASE,
+ .end = TEGRA_DVC_BASE + TEGRA_DVC_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static const struct tegra_pingroup_config i2c2_ddc = {
+ .pingroup = TEGRA_PINGROUP_DDC,
+ .func = TEGRA_MUX_I2C2,
+};
+
+static const struct tegra_pingroup_config i2c_i2cp = {
+ .pingroup = TEGRA_PINGROUP_I2CP,
+ .func = TEGRA_MUX_I2C,
+};
+
+static struct tegra_i2c_platform_data p852_i2c1_platform_data = {
+ .adapter_nr = 0,
+ .bus_count = 1,
+ .bus_clk_rate = {400000},
+};
+
+static struct tegra_i2c_platform_data p852_i2c2_platform_data = {
+ .adapter_nr = 1,
+ .bus_count = 1,
+ .bus_clk_rate = {100000},
+ .bus_mux = {&i2c2_ddc},
+ .bus_mux_len = {1},
+};
+
+static struct tegra_i2c_platform_data p852_i2c3_platform_data = {
+ .adapter_nr = 2,
+ .bus_count = 1,
+ .bus_clk_rate = {400000},
+};
+
+static struct tegra_i2c_platform_data p852_dvc_platform_data = {
+ .adapter_nr = 3,
+ .bus_count = 1,
+ .bus_clk_rate = {100000},
+ .bus_mux = {&i2c_i2cp},
+ .bus_mux_len = {1},
+ .is_dvc = true,
+};
+
+struct platform_device tegra_i2c_device[] = {
+ {
+ .name = "tegra-i2c",
+ .id = 0,
+ .resource = i2c_resource1,
+ .num_resources = ARRAY_SIZE(i2c_resource1),
+ .dev = {
+ .platform_data = &p852_i2c1_platform_data,
+ },
+ },
+ {
+ .name = "tegra-i2c",
+ .id = 1,
+ .resource = i2c_resource2,
+ .num_resources = ARRAY_SIZE(i2c_resource2),
+ .dev = {
+ .platform_data = &p852_i2c2_platform_data,
+ },
+ },
+ {
+ .name = "tegra-i2c",
+ .id = 2,
+ .resource = i2c_resource3,
+ .num_resources = ARRAY_SIZE(i2c_resource3),
+ .dev = {
+ .platform_data = &p852_i2c3_platform_data,
+ },
+ },
+ {
+ .name = "tegra-i2c",
+ .id = 3,
+ .resource = i2c_resource4,
+ .num_resources = ARRAY_SIZE(i2c_resource4),
+ .dev = {
+ .platform_data = &p852_dvc_platform_data,
+ },
+ }
+};
+
+void __init p852_i2c_set_default_clock(int adapter, unsigned long clock)
+{
+ if (adapter >= 0 && adapter < ARRAY_SIZE(tegra_i2c_device))
+ ((struct tegra_i2c_platform_data *)tegra_i2c_device[adapter].
+ dev.platform_data)->bus_clk_rate[0] = clock;
+}
+
+void __init p852_i2c_init(void)
+{
+ int i;
+ unsigned int i2c_config = 0;
+ if (p852_sku_peripherals & P852_SKU_I2C_ENABLE) {
+ for (i = 0; i < P852_MAX_I2C; i++) {
+ i2c_config =
+ (p852_i2c_peripherals >> (P852_I2C_SHIFT * i));
+ if (i2c_config & P852_I2C_ENABLE)
+ platform_device_register(&tegra_i2c_device[i]);
+ }
+ }
+}
diff --git a/arch/arm/mach-tegra/p852/board-p852-panel.c b/arch/arm/mach-tegra/p852/board-p852-panel.c
new file mode 100644
index 000000000000..bfd35fa5da53
--- /dev/null
+++ b/arch/arm/mach-tegra/p852/board-p852-panel.c
@@ -0,0 +1,191 @@
+/*
+ * arch/arm/mach-tegra/board-p852-panel.c
+ *
+ * Copyright (c) 2010-2011, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/resource.h>
+#include <linux/nvhost.h>
+#include <linux/platform_device.h>
+#include <asm/mach-types.h>
+#include <mach/nvmap.h>
+#include <mach/irqs.h>
+#include <mach/iomap.h>
+#include <mach/dc.h>
+#include <mach/fb.h>
+
+#include "board-p852.h"
+
+#define CARVEOUT_IRAM {\
+ .name = "iram",\
+ .usage_mask = NVMAP_HEAP_CARVEOUT_IRAM,\
+ .base = TEGRA_IRAM_BASE,\
+ .size = TEGRA_IRAM_SIZE,\
+ .buddy_size = 0, /* no buddy allocation for IRAM */\
+}
+
+static int p852_panel_enable(void)
+{
+ pr_info("%s\n", __func__);
+ return 0;
+}
+
+static int p852_panel_disable(void)
+{
+ pr_info("%s\n", __func__);
+ return 0;
+}
+
+static struct resource p852_disp_resources[] = {
+ {
+ .name = "irq",
+ .start = INT_DISPLAY_GENERAL,
+ .end = INT_DISPLAY_GENERAL,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "regs",
+ .start = TEGRA_DISPLAY_BASE,
+ .end = TEGRA_DISPLAY_BASE + TEGRA_DISPLAY_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .name = "fbmem",
+ .start = 0,
+ .end = 0,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct tegra_dc_mode p852_panel_modes[] = {
+/* Timings for the LG LB070WV4 panel */
+ {
+ .pclk = 33230769,
+
+ .h_ref_to_sync = 1,
+ .v_ref_to_sync = 1,
+
+ .h_sync_width = 128,
+ .v_sync_width = 2,
+
+ .h_back_porch = 88,
+ .v_back_porch = 30,
+
+ .h_front_porch = 40,
+ .v_front_porch = 13,
+
+ .h_active = 800,
+ .v_active = 480,
+ },
+};
+
+static struct tegra_fb_data p852_fb_data = {
+ .win = 0,
+ .xres = 800,
+ .yres = 480,
+ .bits_per_pixel = 16,
+};
+
+static struct tegra_dc_out p852_disp_out = {
+ .type = TEGRA_DC_OUT_RGB,
+
+ .align = TEGRA_DC_ALIGN_MSB,
+ .order = TEGRA_DC_ORDER_RED_BLUE,
+
+ .modes = p852_panel_modes,
+ .n_modes = ARRAY_SIZE(p852_panel_modes),
+
+ .enable = p852_panel_enable,
+ .disable = p852_panel_disable,
+};
+
+static struct tegra_dc_platform_data p852_disp_pdata = {
+ .flags = TEGRA_DC_FLAG_ENABLED,
+ .default_out = &p852_disp_out,
+ .fb = &p852_fb_data,
+};
+
+static struct nvhost_device p852_disp_device = {
+ .name = "tegradc",
+ .id = 0,
+ .resource = p852_disp_resources,
+ .num_resources = ARRAY_SIZE(p852_disp_resources),
+ .dev = {
+ .platform_data = &p852_disp_pdata,
+ },
+};
+
+static struct nvmap_platform_carveout p852_carveouts[] = {
+ [0] = CARVEOUT_IRAM,
+ [1] = {
+ .name = "generic-0",
+ .usage_mask = NVMAP_HEAP_CARVEOUT_GENERIC,
+ .base = 0,
+ .size = 0,
+ .buddy_size = SZ_32K,
+ },
+};
+
+static struct nvmap_platform_data p852_nvmap_data = {
+ .carveouts = p852_carveouts,
+ .nr_carveouts = ARRAY_SIZE(p852_carveouts),
+};
+
+static struct platform_device p852_nvmap_device = {
+ .name = "tegra-nvmap",
+ .id = -1,
+ .dev = {
+ .platform_data = &p852_nvmap_data,
+ },
+};
+
+static struct platform_device *p852_gfx_devices[] __initdata = {
+ &tegra_grhost_device,
+ &tegra_pwfm2_device,
+};
+
+int __init p852_panel_init(void)
+{
+ int err;
+ struct resource *res;
+
+ pr_info("%s\n", __func__);
+
+ p852_carveouts[1].base = tegra_carveout_start;
+ p852_carveouts[1].size = tegra_carveout_size;
+
+ err = platform_device_register(&p852_nvmap_device);
+ if (err)
+ return err;
+
+ err = platform_add_devices(p852_gfx_devices,
+ ARRAY_SIZE(p852_gfx_devices));
+
+ res = nvhost_get_resource_byname(&p852_disp_device,
+ IORESOURCE_MEM, "fbmem");
+
+ res->start = tegra_fb_start;
+ res->end = tegra_fb_start + tegra_fb_size - 1;
+
+ if (!err)
+ err = nvhost_device_register(&p852_disp_device);
+
+ return err;
+}
diff --git a/arch/arm/mach-tegra/p852/board-p852-pinmux.c b/arch/arm/mach-tegra/p852/board-p852-pinmux.c
new file mode 100644
index 000000000000..0ded989f7a13
--- /dev/null
+++ b/arch/arm/mach-tegra/p852/board-p852-pinmux.c
@@ -0,0 +1,439 @@
+/*
+ * arch/arm/mach-tegra/board-p852-pinmux.c
+ *
+ * Copyright (c) 2010-2011, NVIDIA Corporation.
+ *
+ * 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/init.h>
+#include <mach/pinmux.h>
+#include <asm/mach-types.h>
+
+#include "board-p852.h"
+
+#define DEFAULT_DRIVE(_name) \
+ { \
+ .pingroup = TEGRA_DRIVE_PINGROUP_##_name, \
+ .hsm = TEGRA_HSM_DISABLE, \
+ .schmitt = TEGRA_SCHMITT_ENABLE, \
+ .drive = TEGRA_DRIVE_DIV_1, \
+ .pull_down = TEGRA_PULL_31, \
+ .pull_up = TEGRA_PULL_31, \
+ .slew_rising = TEGRA_SLEW_SLOWEST, \
+ .slew_falling = TEGRA_SLEW_SLOWEST, \
+ }
+
+#define P852_PAD_DRIVE(_name) \
+ { \
+ .pingroup = TEGRA_DRIVE_PINGROUP_##_name, \
+ .hsm = TEGRA_HSM_DISABLE, \
+ .schmitt = TEGRA_SCHMITT_DISABLE, \
+ .drive = TEGRA_DRIVE_DIV_1, \
+ .pull_down = TEGRA_PULL_18, \
+ .pull_up = TEGRA_PULL_22, \
+ .slew_rising = TEGRA_SLEW_SLOWEST, \
+ .slew_falling = TEGRA_SLEW_SLOWEST, \
+ }
+
+#define P852_PAD_DRIVE_HSM(_name) \
+ { \
+ .pingroup = TEGRA_DRIVE_PINGROUP_##_name, \
+ .hsm = TEGRA_HSM_ENABLE, \
+ .schmitt = TEGRA_SCHMITT_ENABLE, \
+ .drive = TEGRA_DRIVE_DIV_1, \
+ .pull_down = TEGRA_PULL_31, \
+ .pull_up = TEGRA_PULL_31, \
+ .slew_rising = TEGRA_SLEW_SLOWEST, \
+ .slew_falling = TEGRA_SLEW_SLOWEST, \
+ }
+
+#define DAP_PAD_DRIVE(_name) \
+ { \
+ .pingroup = TEGRA_DRIVE_PINGROUP_##_name, \
+ .hsm = TEGRA_HSM_DISABLE, \
+ .schmitt = TEGRA_SCHMITT_ENABLE, \
+ .drive = TEGRA_DRIVE_DIV_1, \
+ .pull_down = TEGRA_PULL_3, \
+ .pull_up = TEGRA_PULL_3, \
+ .slew_rising = TEGRA_SLEW_SLOWEST, \
+ .slew_falling = TEGRA_SLEW_SLOWEST, \
+ }
+
+static __initdata struct tegra_drive_pingroup_config p852_drive_pinmux[] = {
+ DEFAULT_DRIVE(DBG),
+ DEFAULT_DRIVE(DDC),
+ DEFAULT_DRIVE(VI1),
+ DEFAULT_DRIVE(VI2),
+ DEFAULT_DRIVE(SDIO1),
+ P852_PAD_DRIVE(SPI),
+ DAP_PAD_DRIVE(DAP1),
+ DAP_PAD_DRIVE(DAP2),
+ DEFAULT_DRIVE(CDEV1),
+ DEFAULT_DRIVE(CDEV2),
+};
+
+static __initdata struct tegra_drive_pingroup_config
+ p852_drive_pinmux_sku8_sku9[] = {
+ DAP_PAD_DRIVE(DAP3),
+};
+
+
+static __initdata struct tegra_pingroup_config p852_common_pinmux[] = {
+ {TEGRA_PINGROUP_SDIO1, TEGRA_MUX_SDIO1, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LSDI, TEGRA_MUX_SPI3, TEGRA_PUPD_PULL_UP, TEGRA_TRI_TRISTATE},
+ {TEGRA_PINGROUP_LSDA, TEGRA_MUX_SPI3, TEGRA_PUPD_PULL_UP, TEGRA_TRI_TRISTATE},
+ {TEGRA_PINGROUP_LCSN, TEGRA_MUX_SPI3, TEGRA_PUPD_PULL_UP, TEGRA_TRI_TRISTATE},
+ {TEGRA_PINGROUP_LSCK, TEGRA_MUX_SPI3, TEGRA_PUPD_PULL_UP, TEGRA_TRI_TRISTATE},
+ {TEGRA_PINGROUP_DTA, TEGRA_MUX_VI, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_DTC, TEGRA_MUX_VI, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_DTD, TEGRA_MUX_VI, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_CSUS, TEGRA_MUX_VI_SENSOR_CLK, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_DTB, TEGRA_MUX_SPI1, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_DTF, TEGRA_MUX_I2C3, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_DTE, TEGRA_MUX_SPI1, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_DDC, TEGRA_MUX_I2C2, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ /* IRDA is same as UART2 option for the pingroup */
+ {TEGRA_PINGROUP_UAD, TEGRA_MUX_IRDA, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_RM, TEGRA_MUX_I2C, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_KBCA, TEGRA_MUX_SDIO2, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_KBCD, TEGRA_MUX_SDIO2, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_KBCB, TEGRA_MUX_SDIO2, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_I2CP, TEGRA_MUX_I2C, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_GPU7, TEGRA_MUX_RTCK, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_DAP1, TEGRA_MUX_DAP1, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_TRISTATE},
+ {TEGRA_PINGROUP_SPDI, TEGRA_MUX_SPDIF, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_SPIF, TEGRA_MUX_SPI2, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_XM2D, TEGRA_MUX_NONE, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_XM2C, TEGRA_MUX_NONE, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_ATB, TEGRA_MUX_SDIO4, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_GMA, TEGRA_MUX_SDIO4, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_GME, TEGRA_MUX_SDIO4, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_CK32, TEGRA_MUX_NONE, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_DDRC, TEGRA_MUX_NONE, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_PMCA, TEGRA_MUX_NONE, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_PMCB, TEGRA_MUX_NONE, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_PMCC, TEGRA_MUX_NONE, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_PMCD, TEGRA_MUX_NONE, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_PMCE, TEGRA_MUX_NONE, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_UAC, TEGRA_MUX_OWR, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_HDINT, TEGRA_MUX_HDMI, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_CRTP, TEGRA_MUX_CRT, TEGRA_PUPD_PULL_UP, TEGRA_TRI_TRISTATE},
+ {TEGRA_PINGROUP_KBCC, TEGRA_MUX_KBC, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_KBCF, TEGRA_MUX_KBC, TEGRA_PUPD_PULL_UP, TEGRA_TRI_TRISTATE},
+ {TEGRA_PINGROUP_KBCE, TEGRA_MUX_KBC, TEGRA_PUPD_PULL_UP, TEGRA_TRI_TRISTATE},
+ {TEGRA_PINGROUP_PMC, TEGRA_MUX_PWR_ON, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_OWC, TEGRA_MUX_RSVD2, TEGRA_PUPD_PULL_UP, TEGRA_TRI_TRISTATE},
+ {TEGRA_PINGROUP_CDEV1, TEGRA_MUX_PLLA_OUT, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_SPDO, TEGRA_MUX_RSVD2, TEGRA_PUPD_NORMAL, TEGRA_TRI_TRISTATE},
+ {TEGRA_PINGROUP_CDEV2, TEGRA_MUX_PLLP_OUT4, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_TRISTATE},
+ {TEGRA_PINGROUP_SPIG, TEGRA_MUX_SPI2_ALT, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_SPIH, TEGRA_MUX_SPI2_ALT, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_GPV, TEGRA_MUX_PCIE, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_GMD, TEGRA_MUX_GMI, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_PTA, TEGRA_MUX_HDMI, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+};
+
+static __initdata struct tegra_pingroup_config p852_nand_pinmux[] = {
+ {TEGRA_PINGROUP_IRRX, TEGRA_MUX_UARTB, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_IRTX, TEGRA_MUX_UARTB, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_UCA, TEGRA_MUX_UARTC, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_UCB, TEGRA_MUX_UARTC, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_GPU, TEGRA_MUX_UARTA, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_DAP2, TEGRA_MUX_DAP2, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_TRISTATE},
+ {TEGRA_PINGROUP_SPID, TEGRA_MUX_SPI2_ALT, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_SPIE, TEGRA_MUX_SPI2_ALT, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_ATC, TEGRA_MUX_NAND, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_ATA, TEGRA_MUX_NAND, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_GMB, TEGRA_MUX_NAND, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_DAP4, TEGRA_MUX_GMI, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_TRISTATE},
+ {TEGRA_PINGROUP_SPIA, TEGRA_MUX_GMI, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_TRISTATE},
+ {TEGRA_PINGROUP_SPIB, TEGRA_MUX_GMI, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_TRISTATE},
+ {TEGRA_PINGROUP_SPIC, TEGRA_MUX_GMI, TEGRA_PUPD_PULL_UP, TEGRA_TRI_TRISTATE},
+ {TEGRA_PINGROUP_ATD, TEGRA_MUX_GMI, TEGRA_PUPD_NORMAL, TEGRA_TRI_TRISTATE},
+ {TEGRA_PINGROUP_ATE, TEGRA_MUX_GMI, TEGRA_PUPD_NORMAL, TEGRA_TRI_TRISTATE},
+ {TEGRA_PINGROUP_DAP3, TEGRA_MUX_DAP3, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_TRISTATE},
+};
+
+static __initdata struct tegra_pingroup_config p852_sdio3_pinmux[] = {
+ {TEGRA_PINGROUP_SDD, TEGRA_MUX_SDIO3, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_SDC, TEGRA_MUX_SDIO3, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_SDB, TEGRA_MUX_SDIO3, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+};
+
+static __initdata struct tegra_pingroup_config p852_uarta_pinmux[] = {
+ {TEGRA_PINGROUP_SDD, TEGRA_MUX_PWM, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_SDC, TEGRA_MUX_PWM, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_SDB, TEGRA_MUX_PWM, TEGRA_PUPD_NORMAL, TEGRA_TRI_TRISTATE},
+};
+
+static __initdata struct tegra_pingroup_config p852_ulpi_pinmux[] = {
+ {TEGRA_PINGROUP_UAA, TEGRA_MUX_ULPI, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_UAB, TEGRA_MUX_ULPI, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_UDA, TEGRA_MUX_ULPI, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+};
+
+static __initdata struct tegra_pingroup_config p852_uarta_1_pinmux[] = {
+ {TEGRA_PINGROUP_UAA, TEGRA_MUX_UARTA, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_UAB, TEGRA_MUX_ULPI, TEGRA_PUPD_NORMAL, TEGRA_TRI_TRISTATE},
+ {TEGRA_PINGROUP_UDA, TEGRA_MUX_ULPI, TEGRA_PUPD_NORMAL, TEGRA_TRI_TRISTATE},
+};
+
+static __initdata struct tegra_pingroup_config p852_uartd_pinmux[] = {
+ {TEGRA_PINGROUP_GMC, TEGRA_MUX_UARTD, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+};
+
+static __initdata struct tegra_pingroup_config p852_spi4_pinmux[] = {
+ {TEGRA_PINGROUP_GMC, TEGRA_MUX_SPI4, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_SLXA, TEGRA_MUX_PCIE, TEGRA_PUPD_NORMAL, TEGRA_TRI_TRISTATE},
+ {TEGRA_PINGROUP_SLXK, TEGRA_MUX_PCIE, TEGRA_PUPD_NORMAL, TEGRA_TRI_TRISTATE},
+ {TEGRA_PINGROUP_SLXC, TEGRA_MUX_SPI4, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_SLXD, TEGRA_MUX_SPDIF, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+};
+
+static __initdata struct tegra_pingroup_config p852_spi4_1_pinmux[] = {
+ {TEGRA_PINGROUP_SLXA, TEGRA_MUX_SPI4, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_SLXK, TEGRA_MUX_SPI4, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_SLXC, TEGRA_MUX_SPI4, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_SLXD, TEGRA_MUX_SPI4, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+};
+
+static __initdata struct tegra_pingroup_config p852_nor_pinmux[] = {
+ {TEGRA_PINGROUP_IRRX, TEGRA_MUX_GMI, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_IRTX, TEGRA_MUX_GMI, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_UCA, TEGRA_MUX_GMI, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_UCB, TEGRA_MUX_GMI, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_GPU, TEGRA_MUX_GMI, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_DAP2, TEGRA_MUX_GMI, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_SPID, TEGRA_MUX_GMI, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_SPIE, TEGRA_MUX_GMI, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_ATC, TEGRA_MUX_GMI, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_ATA, TEGRA_MUX_GMI, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_GMB, TEGRA_MUX_GMI, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_DAP4, TEGRA_MUX_GMI, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_SPIA, TEGRA_MUX_GMI, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_SPIB, TEGRA_MUX_GMI, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_SPIC, TEGRA_MUX_GMI, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_ATD, TEGRA_MUX_GMI, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_ATE, TEGRA_MUX_GMI, TEGRA_PUPD_NORMAL, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_DAP3, TEGRA_MUX_DAP3, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+};
+
+static __initdata struct tegra_pingroup_config p852_display_a_pinmux[] = {
+ {TEGRA_PINGROUP_LSPI, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LSC0, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LHS, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LVS, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LD0, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LD1, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LD2, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LD3, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LD4, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LD5, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LD6, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LD7, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LD8, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LD9, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LD10, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LD11, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LD12, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LD13, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LD14, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LD15, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LD16, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LD17, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LHP1, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LHP2, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LVP1, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LHP0, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LDI, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LPP, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LM1, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LPW0, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LSC1, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_PULL_UP, TEGRA_TRI_TRISTATE},
+ {TEGRA_PINGROUP_LVP0, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_PULL_UP, TEGRA_TRI_TRISTATE},
+ {TEGRA_PINGROUP_LM0, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_PULL_UP, TEGRA_TRI_TRISTATE},
+ {TEGRA_PINGROUP_LDC, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_PULL_UP, TEGRA_TRI_TRISTATE},
+ {TEGRA_PINGROUP_LPW2, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_PULL_UP, TEGRA_TRI_TRISTATE},
+ {TEGRA_PINGROUP_LPW1, TEGRA_MUX_DISPLAYA, TEGRA_PUPD_PULL_UP, TEGRA_TRI_TRISTATE},
+};
+
+static __initdata struct tegra_pingroup_config p852_display_b_pinmux[] = {
+ {TEGRA_PINGROUP_LPW1, TEGRA_MUX_DISPLAYB, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LPW2, TEGRA_MUX_DISPLAYB, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LDC, TEGRA_MUX_DISPLAYB, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LM0, TEGRA_MUX_DISPLAYB, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LVP0, TEGRA_MUX_DISPLAYB, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LSC1, TEGRA_MUX_DISPLAYB, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LM1, TEGRA_MUX_DISPLAYB, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LSPI, TEGRA_MUX_DISPLAYB, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LSC0, TEGRA_MUX_DISPLAYB, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LHS, TEGRA_MUX_DISPLAYB, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LVS, TEGRA_MUX_DISPLAYB, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LD0, TEGRA_MUX_DISPLAYB, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LD1, TEGRA_MUX_DISPLAYB, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LD2, TEGRA_MUX_DISPLAYB, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LD3, TEGRA_MUX_DISPLAYB, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LD4, TEGRA_MUX_DISPLAYB, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LD5, TEGRA_MUX_DISPLAYB, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LD6, TEGRA_MUX_DISPLAYB, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LD7, TEGRA_MUX_DISPLAYB, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LD8, TEGRA_MUX_DISPLAYB, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LD9, TEGRA_MUX_DISPLAYB, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LD10, TEGRA_MUX_DISPLAYB, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LD11, TEGRA_MUX_DISPLAYB, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LD12, TEGRA_MUX_DISPLAYB, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LD13, TEGRA_MUX_DISPLAYB, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LD14, TEGRA_MUX_DISPLAYB, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LD15, TEGRA_MUX_DISPLAYB, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LD16, TEGRA_MUX_DISPLAYB, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LD17, TEGRA_MUX_DISPLAYB, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LHP1, TEGRA_MUX_DISPLAYB, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LHP2, TEGRA_MUX_DISPLAYB, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LVP1, TEGRA_MUX_DISPLAYB, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LHP0, TEGRA_MUX_DISPLAYB, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LDI, TEGRA_MUX_DISPLAYB, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LPP, TEGRA_MUX_DISPLAYB, TEGRA_PUPD_PULL_DOWN, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LM1, TEGRA_MUX_DISPLAYB, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_LPW0, TEGRA_MUX_DISPLAYB, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+};
+
+static __initdata struct tegra_drive_pingroup_config
+ p852_drive_pinmux_sku23[] = {
+ P852_PAD_DRIVE_HSM(SDMMC3),
+};
+
+static __initdata struct tegra_drive_pingroup_config
+ p852_drive_pinmux_sku13[] = {
+ P852_PAD_DRIVE_HSM(SDMMC3),
+};
+
+static __initdata struct tegra_pingroup_config p852_pupd_sku23[] = {
+ {TEGRA_PINGROUP_GPV, TEGRA_MUX_NONE, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_GMC, TEGRA_MUX_NONE, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_DTB, TEGRA_MUX_NONE, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+};
+
+
+static __initdata struct tegra_pingroup_config p852_pupd_sku13[] = {
+ {TEGRA_PINGROUP_GPV, TEGRA_MUX_NONE, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+};
+
+static __initdata struct tegra_pingroup_config p852_pupd_sku5[] = {
+ {TEGRA_PINGROUP_GMC, TEGRA_MUX_NONE, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_DTB, TEGRA_MUX_NONE, TEGRA_PUPD_PULL_UP, TEGRA_TRI_NORMAL},
+};
+
+static void tegra_pinmux_config_pupd_table(
+ const struct tegra_pingroup_config *config,
+ int len)
+{
+ int i;
+ for (i = 0; i < len; i++) {
+ /* config.func, the pin_mux setting is not used here */
+ tegra_pinmux_config_pullupdown_table(&config[i], 1,
+ config[i].pupd);
+ }
+}
+
+void __init p852_pinmux_init(void)
+{
+ unsigned int sdio3_config = (p852_sdhci_peripherals >>
+ P852_SDHCI3_SHIFT) & P852_SDHCI_MASK;
+ unsigned int uartd_config = (p852_uart_peripherals >> P852_UARTD_SHIFT)
+ & P852_UART_MASK;
+ unsigned int uarta_config = (p852_uart_peripherals >> P852_UARTA_SHIFT)
+ & P852_UART_MASK;
+ unsigned int spi4_config = (p852_spi_peripherals >> P852_SPI4_SHIFT)
+ & P852_SPI_MASK;
+ unsigned int displayb_config = (p852_display_peripherals >>
+ P852_DISPB_SHIFT) & P852_DISP_MASK;
+
+ tegra_pinmux_config_table(p852_common_pinmux,
+ ARRAY_SIZE(p852_common_pinmux));
+
+ if ((uarta_config & P852_UART_ENABLE)
+ && (uarta_config & P852_UART_ALT_PIN_CFG)) {
+ tegra_pinmux_config_table(p852_uarta_1_pinmux,
+ ARRAY_SIZE(p852_uarta_1_pinmux));
+ } else {
+ tegra_pinmux_config_table(p852_ulpi_pinmux,
+ ARRAY_SIZE(p852_ulpi_pinmux));
+ }
+
+ if (sdio3_config & P852_SDHCI_ENABLE) {
+ tegra_pinmux_config_table(p852_sdio3_pinmux,
+ ARRAY_SIZE(p852_sdio3_pinmux));
+ } else {
+ tegra_pinmux_config_table(p852_uarta_pinmux,
+ ARRAY_SIZE(p852_uarta_pinmux));
+ }
+
+ if ((uartd_config & P852_UART_ENABLE) &&
+ (spi4_config & P852_SPI_ENABLE)) {
+ tegra_pinmux_config_table(p852_uartd_pinmux,
+ ARRAY_SIZE(p852_uartd_pinmux));
+ tegra_pinmux_config_table(p852_spi4_1_pinmux,
+ ARRAY_SIZE(p852_spi4_1_pinmux));
+ } else {
+ tegra_pinmux_config_table(p852_spi4_pinmux,
+ ARRAY_SIZE(p852_spi4_pinmux));
+ }
+
+ if (p852_sku_peripherals & P852_SKU_NOR_ENABLE) {
+ tegra_pinmux_config_table(p852_nor_pinmux,
+ ARRAY_SIZE(p852_nor_pinmux));
+ } else {
+ tegra_pinmux_config_table(p852_nand_pinmux,
+ ARRAY_SIZE(p852_nand_pinmux));
+ }
+
+ if (p852_sku_peripherals & P852_SKU_DISPLAY_ENABLE) {
+ if (displayb_config) {
+ tegra_pinmux_config_table(p852_display_b_pinmux,
+ ARRAY_SIZE(p852_display_b_pinmux));
+ } else {
+ tegra_pinmux_config_table(p852_display_a_pinmux,
+ ARRAY_SIZE(p852_display_a_pinmux));
+ }
+ }
+
+ tegra_drive_pinmux_config_table(p852_drive_pinmux,
+ ARRAY_SIZE(p852_drive_pinmux));
+
+ if (system_rev == P852_SKU23 ||
+ system_rev == P852_SKU23_B00 ||
+ system_rev == P852_SKU23_C01) {
+ tegra_drive_pinmux_config_table(p852_drive_pinmux_sku23,
+ ARRAY_SIZE(p852_drive_pinmux_sku23));
+
+ tegra_pinmux_config_pupd_table(p852_pupd_sku23,
+ ARRAY_SIZE(p852_pupd_sku23));
+ } else if (system_rev == P852_SKU13 ||
+ system_rev == P852_SKU13_B00) {
+ tegra_drive_pinmux_config_table(p852_drive_pinmux_sku13,
+ ARRAY_SIZE(p852_drive_pinmux_sku13));
+
+ tegra_pinmux_config_pupd_table(p852_pupd_sku13,
+ ARRAY_SIZE(p852_pupd_sku13));
+ } else if (system_rev == P852_SKU5_B00 || system_rev == P852_SKU5_C01) {
+ tegra_pinmux_config_pupd_table(p852_pupd_sku5,
+ ARRAY_SIZE(p852_pupd_sku5));
+ } else if (system_rev == P852_SKU8_B00 || system_rev == P852_SKU9_B00 ||
+ system_rev == P852_SKU8_C01 || system_rev == P852_SKU9_C01) {
+ tegra_drive_pinmux_config_table(p852_drive_pinmux_sku8_sku9,
+ ARRAY_SIZE(p852_drive_pinmux_sku8_sku9));
+ }
+
+}
+
+
diff --git a/arch/arm/mach-tegra/p852/board-p852-power.c b/arch/arm/mach-tegra/p852/board-p852-power.c
new file mode 100644
index 000000000000..dce9bd7e83e2
--- /dev/null
+++ b/arch/arm/mach-tegra/p852/board-p852-power.c
@@ -0,0 +1,225 @@
+/*
+ * arch/arm/mach-tegra/p852/board-p852-power.c
+ *
+ * Copyright (c) 2010-2011, NVIDIA Corporation.
+ *
+ * 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/i2c.h>
+#include <linux/pda_power.h>
+#include <linux/platform_device.h>
+#include <linux/resource.h>
+#include <linux/regulator/machine.h>
+#include <linux/mfd/tps6586x.h>
+#include <linux/gpio.h>
+#include <linux/io.h>
+#include <mach/iomap.h>
+#include <mach/irqs.h>
+#include "board-p852.h"
+
+#define PMC_CTRL 0x0
+#define PMC_CTRL_INTR_LOW (1 << 17)
+
+static struct regulator_consumer_supply tps658621_sm0_supply[] = {
+ REGULATOR_SUPPLY("vdd_core", NULL),
+};
+static struct regulator_consumer_supply tps658621_sm1_supply[] = {
+ REGULATOR_SUPPLY("vdd_cpu", NULL),
+};
+static struct regulator_consumer_supply tps658621_sm2_supply[] = {
+ REGULATOR_SUPPLY("vdd_sm2", NULL),
+};
+static struct regulator_consumer_supply tps658621_ldo0_supply[] = {
+ REGULATOR_SUPPLY("vddio_pex_clk", NULL),
+};
+static struct regulator_consumer_supply tps658621_ldo1_supply[] = {
+ REGULATOR_SUPPLY("avdd_pll", NULL),
+ REGULATOR_SUPPLY("avdd_plla_pc", NULL),
+ REGULATOR_SUPPLY("avdd_pllm", NULL),
+ REGULATOR_SUPPLY("avdd_pllu", NULL),
+ REGULATOR_SUPPLY("avdd_pllx6", NULL),
+};
+static struct regulator_consumer_supply tps658621_ldo2_supply[] = {
+ REGULATOR_SUPPLY("vdd_rtc", NULL),
+};
+static struct regulator_consumer_supply tps658621_ldo3_supply[] = {
+ REGULATOR_SUPPLY("avdd_usb", NULL),
+ REGULATOR_SUPPLY("avdd_usb_pll", NULL),
+ REGULATOR_SUPPLY("avdd_lvds", NULL),
+};
+static struct regulator_consumer_supply tps658621_ldo4_supply[] = {
+ REGULATOR_SUPPLY("avdd_osc", NULL),
+ REGULATOR_SUPPLY("vddio_sys", "panjit_touch"),
+};
+static struct regulator_consumer_supply tps658621_ldo5_supply[] = {
+ REGULATOR_SUPPLY("vddio_lcd", NULL),
+};
+static struct regulator_consumer_supply tps658621_ldo6_supply[] = {
+ REGULATOR_SUPPLY("avdd_vdac", NULL),
+};
+static struct regulator_consumer_supply tps658621_ldo7_supply[] = {
+ REGULATOR_SUPPLY("vddio_vi", NULL),
+ REGULATOR_SUPPLY("vdd_fuse", NULL),
+ REGULATOR_SUPPLY("vspi", "spi_tegra.0"),
+};
+static struct regulator_consumer_supply tps658621_ldo8_supply[] = {
+ REGULATOR_SUPPLY("vddio_bb", NULL),
+ REGULATOR_SUPPLY("vmmc", "sdhci-tegra.0"),
+ REGULATOR_SUPPLY("vmmc", "sdhci-tegra.2"),
+};
+static struct regulator_consumer_supply tps658621_ldo9_supply[] = {
+ REGULATOR_SUPPLY("vdd_ddr_rx", NULL),
+ REGULATOR_SUPPLY("vmmc", "sdhci-tegra.3"),
+};
+
+#define REGULATOR_INIT(_id, _minmv, _maxmv) \
+ { \
+ .constraints = { \
+ .min_uV = (_minmv)*1000, \
+ .max_uV = (_maxmv)*1000, \
+ .valid_modes_mask = (REGULATOR_MODE_NORMAL | \
+ REGULATOR_MODE_STANDBY), \
+ .valid_ops_mask = (REGULATOR_CHANGE_MODE | \
+ REGULATOR_CHANGE_STATUS | \
+ REGULATOR_CHANGE_VOLTAGE), \
+ }, \
+ .num_consumer_supplies = ARRAY_SIZE(tps658621_##_id##_supply),\
+ .consumer_supplies = tps658621_##_id##_supply, \
+ }
+
+static struct regulator_init_data sm0_data = REGULATOR_INIT(sm0, 725, 1500);
+static struct regulator_init_data sm1_data = REGULATOR_INIT(sm1, 725, 1500);
+static struct regulator_init_data sm2_data = REGULATOR_INIT(sm2, 3000, 4550);
+static struct regulator_init_data ldo0_data = REGULATOR_INIT(ldo0, 1250, 3300);
+static struct regulator_init_data ldo1_data = REGULATOR_INIT(ldo1, 725, 1500);
+static struct regulator_init_data ldo2_data = REGULATOR_INIT(ldo2, 725, 1225);
+static struct regulator_init_data ldo3_data = REGULATOR_INIT(ldo3, 1250, 3300);
+static struct regulator_init_data ldo4_data = REGULATOR_INIT(ldo4, 1700, 2475);
+static struct regulator_init_data ldo5_data = REGULATOR_INIT(ldo5, 1250, 3300);
+static struct regulator_init_data ldo6_data = REGULATOR_INIT(ldo6, 1250, 3300);
+static struct regulator_init_data ldo7_data = REGULATOR_INIT(ldo7, 1250, 3300);
+static struct regulator_init_data ldo8_data = REGULATOR_INIT(ldo8, 1250, 3300);
+static struct regulator_init_data ldo9_data = REGULATOR_INIT(ldo9, 1250, 3300);
+
+
+static struct tps6586x_rtc_platform_data rtc_data = {
+ .irq = TEGRA_NR_IRQS + TPS6586X_INT_RTC_ALM1,
+ .cl_sel = 0,
+};
+
+
+#define TPS_REG(_id, _data) \
+ { \
+ .id = TPS6586X_ID_##_id, \
+ .name = "tps6586x-regulator", \
+ .platform_data = _data, \
+ }
+
+static struct tps6586x_subdev_info tps_devs[] = {
+ TPS_REG(SM_0, &sm0_data),
+ TPS_REG(SM_1, &sm1_data),
+ TPS_REG(SM_2, &sm2_data),
+ TPS_REG(LDO_0, &ldo0_data),
+ TPS_REG(LDO_1, &ldo1_data),
+ TPS_REG(LDO_2, &ldo2_data),
+ TPS_REG(LDO_3, &ldo3_data),
+ TPS_REG(LDO_4, &ldo4_data),
+ TPS_REG(LDO_5, &ldo5_data),
+ TPS_REG(LDO_6, &ldo6_data),
+ TPS_REG(LDO_7, &ldo7_data),
+ TPS_REG(LDO_8, &ldo8_data),
+ TPS_REG(LDO_9, &ldo9_data),
+ {
+ .id = 0,
+ .name = "tps6586x-rtc",
+ .platform_data = &rtc_data,
+ },
+};
+
+static struct tps6586x_platform_data tps_platform = {
+ .irq_base = TEGRA_NR_IRQS,
+ .num_subdevs = ARRAY_SIZE(tps_devs),
+ .subdevs = tps_devs,
+ .gpio_base = TEGRA_NR_GPIOS,
+};
+
+static struct i2c_board_info __initdata p852_regulators[] = {
+ {
+ I2C_BOARD_INFO("tps6586x", 0x34),
+ .irq = INT_EXTERNAL_PMU,
+ .platform_data = &tps_platform,
+ },
+};
+
+static struct tegra_suspend_platform_data p852_suspend_data = {
+ .cpu_timer = 2000,
+ .cpu_off_timer = 0,
+ .suspend_mode = TEGRA_SUSPEND_LP1,
+ .core_timer = 0x7e7e,
+ .core_off_timer = 0,
+ .corereq_high = false,
+ .sysclkreq_high = true,
+};
+
+static void p852_power_off(void)
+{
+ int ret;
+
+ ret = tps6586x_power_off();
+ if (ret)
+ pr_err("p852: failed to power off\n");
+
+ while (1)
+ ;
+}
+
+void __init p852_power_off_init(void)
+{
+ pm_power_off = p852_power_off;
+}
+
+static void __init tps6586x_rtc_preinit(void)
+{
+ int i;
+ struct tps6586x_rtc_platform_data *rtc_pdata;
+
+ if (system_rev == P852_SKU23_B00 ||
+ system_rev == P852_SKU23_C01 ||
+ system_rev == P852_SKU13_B00 ||
+ system_rev == P852_SKU5_B00 ||
+ system_rev == P852_SKU5_C01) {
+ for (i = 0; i < tps_platform.num_subdevs; ++i)
+ if (!strcmp(tps_platform.subdevs[i].name,
+ "tps6586x-rtc"))
+ rtc_pdata =
+ (struct tps6586x_rtc_platform_data *)
+ (tps_platform.subdevs[i].platform_data);
+ rtc_pdata->cl_sel = TPS6586X_RTC_CL_SEL_1_5PF;
+ }
+}
+
+int __init p852_regulator_init(void)
+{
+ void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE);
+ u32 pmc_ctrl;
+
+ /* configure the power management controller to trigger PMU
+ * interrupts when low */
+ pmc_ctrl = readl(pmc + PMC_CTRL);
+ writel(pmc_ctrl | PMC_CTRL_INTR_LOW, pmc + PMC_CTRL);
+ i2c_register_board_info(3, p852_regulators, 1);
+ tegra_init_suspend(&p852_suspend_data);
+
+ tps6586x_rtc_preinit();
+
+ return 0;
+}
diff --git a/arch/arm/mach-tegra/p852/board-p852-sdhci.c b/arch/arm/mach-tegra/p852/board-p852-sdhci.c
new file mode 100644
index 000000000000..dc5b81fa3727
--- /dev/null
+++ b/arch/arm/mach-tegra/p852/board-p852-sdhci.c
@@ -0,0 +1,199 @@
+/*
+ * arch/arm/mach-tegra/board-p852-sdhci.c
+ *
+ * Copyright (c) 2010-2011, NVIDIA Corporation.
+ *
+ * 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/resource.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+
+#include <asm/mach-types.h>
+#include <mach/irqs.h>
+#include <mach/iomap.h>
+#include <mach/sdhci.h>
+#include <mach/pinmux.h>
+#include <asm/mach-types.h>
+
+#include "board-p852.h"
+
+static struct resource sdhci_resource1[] = {
+ [0] = {
+ .start = INT_SDMMC1,
+ .end = INT_SDMMC1,
+ .flags = IORESOURCE_IRQ,
+ },
+ [1] = {
+ .start = TEGRA_SDMMC1_BASE,
+ .end = TEGRA_SDMMC1_BASE + TEGRA_SDMMC1_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct resource sdhci_resource2[] = {
+ [0] = {
+ .start = INT_SDMMC2,
+ .end = INT_SDMMC2,
+ .flags = IORESOURCE_IRQ,
+ },
+ [1] = {
+ .start = TEGRA_SDMMC2_BASE,
+ .end = TEGRA_SDMMC2_BASE + TEGRA_SDMMC2_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct resource sdhci_resource3[] = {
+ [0] = {
+ .start = INT_SDMMC3,
+ .end = INT_SDMMC3,
+ .flags = IORESOURCE_IRQ,
+ },
+ [1] = {
+ .start = TEGRA_SDMMC3_BASE,
+ .end = TEGRA_SDMMC3_BASE + TEGRA_SDMMC3_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct resource sdhci_resource4[] = {
+ [0] = {
+ .start = INT_SDMMC4,
+ .end = INT_SDMMC4,
+ .flags = IORESOURCE_IRQ,
+ },
+ [1] = {
+ .start = TEGRA_SDMMC4_BASE,
+ .end = TEGRA_SDMMC4_BASE + TEGRA_SDMMC4_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+struct tegra_sdhci_platform_data p852_sdhci_platform_data[] = {
+ {
+ .cd_gpio = -1,
+ .wp_gpio = -1,
+ .power_gpio = -1,
+ },
+ {
+ .cd_gpio = -1,
+ .wp_gpio = -1,
+ .power_gpio = -1,
+ },
+ {
+ .cd_gpio = -1,
+ .wp_gpio = -1,
+ .power_gpio = -1,
+ },
+ {
+ .cd_gpio = -1,
+ .wp_gpio = -1,
+ .power_gpio = -1,
+ },
+};
+
+static struct platform_device tegra_sdhci_device[] = {
+ {
+ .name = "sdhci-tegra",
+ .id = 0,
+ .resource = sdhci_resource1,
+ .num_resources = ARRAY_SIZE(sdhci_resource1),
+ .dev = {
+ .platform_data = &p852_sdhci_platform_data[0],
+ },
+ },
+ {
+ .name = "sdhci-tegra",
+ .id = 1,
+ .resource = sdhci_resource2,
+ .num_resources = ARRAY_SIZE(sdhci_resource2),
+ .dev = {
+ .platform_data = &p852_sdhci_platform_data[1],
+ },
+ },
+ {
+ .name = "sdhci-tegra",
+ .id = 2,
+ .resource = sdhci_resource3,
+ .num_resources = ARRAY_SIZE(sdhci_resource3),
+ .dev = {
+ .platform_data = &p852_sdhci_platform_data[2],
+ },
+ },
+ {
+ .name = "sdhci-tegra",
+ .id = 3,
+ .resource = sdhci_resource4,
+ .num_resources = ARRAY_SIZE(sdhci_resource4),
+ .dev = {
+ .platform_data = &p852_sdhci_platform_data[3],
+ },
+ },
+
+};
+
+void __init p852_sdhci_init(void)
+{
+
+ int i, count = 10;
+ int cd = 0, wp = 0, pw = 0;
+ static char gpio_name[12][10];
+ unsigned int sdhci_config = 0;
+
+ if (p852_sku_peripherals & P852_SKU_SDHCI_ENABLE)
+ for (i = 0; i < P852_MAX_SDHCI; i++) {
+ sdhci_config =
+ (p852_sdhci_peripherals >> (P852_SDHCI_SHIFT * i));
+ cd = i * 3;
+ wp = cd + 1;
+ pw = wp + 1;
+ if (sdhci_config & P852_SDHCI_ENABLE) {
+ if (sdhci_config & P852_SDHCI_CD_EN) {
+ snprintf(gpio_name[cd], count,
+ "sdhci%d_cd", i);
+ gpio_request(p852_sdhci_platform_data
+ [i].cd_gpio,
+ gpio_name[cd]);
+ tegra_gpio_enable
+ (p852_sdhci_platform_data[i].
+ cd_gpio);
+ }
+
+ if (sdhci_config & P852_SDHCI_WP_EN) {
+ snprintf(gpio_name[wp], count,
+ "sdhci%d_wp", i);
+ gpio_request(p852_sdhci_platform_data
+ [i].wp_gpio,
+ gpio_name[wp]);
+ tegra_gpio_enable
+ (p852_sdhci_platform_data[i].
+ wp_gpio);
+ }
+
+ if (sdhci_config & P852_SDHCI_PW_EN) {
+ snprintf(gpio_name[pw], count,
+ "sdhci%d_pw", i);
+ gpio_request(p852_sdhci_platform_data
+ [i].power_gpio,
+ gpio_name[pw]);
+ tegra_gpio_enable
+ (p852_sdhci_platform_data[i].
+ power_gpio);
+ }
+
+ platform_device_register(&tegra_sdhci_device
+ [i]);
+ }
+ }
+}
diff --git a/arch/arm/mach-tegra/p852/board-p852-sku1-b00.c b/arch/arm/mach-tegra/p852/board-p852-sku1-b00.c
new file mode 100644
index 000000000000..1cd89c5dfd76
--- /dev/null
+++ b/arch/arm/mach-tegra/p852/board-p852-sku1-b00.c
@@ -0,0 +1,98 @@
+/*
+ * arch/arm/mach-tegra/board-p852-sku1-b00.c
+ *
+ * Copyright (C) 2010-2011, NVIDIA Corporation.
+ *
+ * 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 "board-p852.h"
+
+static inline void p852_sku1_b00_spi_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_SPI_ENABLE;
+ p852_spi_peripherals |=
+ ((P852_SPI_MASTER | P852_SPI_ENABLE) << P852_SPI1_SHIFT) |
+ ((P852_SPI_MASTER | P852_SPI_ENABLE) << P852_SPI4_SHIFT);
+}
+
+static inline void p852_sku1_b00_i2s_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_I2S_ENABLE;
+ p852_i2s_peripherals |= ((P852_I2S_ENABLE | P852_I2S_TDM)
+ << P852_I2S1_SHIFT) | ((P852_I2S_ENABLE | P852_I2S_TDM)
+ << P852_I2S2_SHIFT);
+}
+
+static inline void p852_sku1_b00_sdhci_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_SDHCI_ENABLE;
+ p852_sdhci_peripherals |=
+ ((P852_SDHCI_ENABLE)
+ << P852_SDHCI4_SHIFT) |
+ ((P852_SDHCI_ENABLE | P852_SDHCI_CD_EN | P852_SDHCI_WP_EN)
+ << P852_SDHCI1_SHIFT) |
+ ((P852_SDHCI_ENABLE | P852_SDHCI_CD_EN | P852_SDHCI_WP_EN)
+ << P852_SDHCI3_SHIFT);
+
+ p852_sdhci_platform_data[0].cd_gpio = TEGRA_GPIO_PV0;
+ p852_sdhci_platform_data[0].wp_gpio = TEGRA_GPIO_PV1;
+ p852_sdhci_platform_data[2].cd_gpio = TEGRA_GPIO_PD7;
+ p852_sdhci_platform_data[2].wp_gpio = TEGRA_GPIO_PT4;
+}
+
+static inline void p852_sku1_b00_uart_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_UART_ENABLE;
+ p852_uart_peripherals |=
+ ((P852_UART_ENABLE | P852_UART_DB) << P852_UARTD_SHIFT) |
+ ((P852_UART_ENABLE | P852_UART_HS) << P852_UARTB_SHIFT) |
+ ((P852_UART_ENABLE | P852_UART_HS | P852_UART_ALT_PIN_CFG)
+ << P852_UARTA_SHIFT);
+}
+
+static inline void p852_sku1_b00_display_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_DISPLAY_ENABLE;
+ p852_display_peripherals |=
+ (P852_DISP_ENABLE << P852_DISPB_SHIFT);
+}
+
+static inline void p852_sku1_b00_ulpi_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_ULPI_DISABLE;
+}
+
+static inline void p852_sku1_b00_i2c_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_I2C_ENABLE;
+ p852_i2c_peripherals |=
+ ((P852_I2C_ENABLE) << P852_I2C1_SHIFT) |
+ ((P852_I2C_ENABLE) << P852_I2C2_SHIFT) |
+ ((P852_I2C_ENABLE) << P852_I2C3_SHIFT) |
+ ((P852_I2C_ENABLE) << P852_I2C4_SHIFT);
+}
+
+void __init p852_sku1_b00_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_NOR_ENABLE;
+
+ p852_sku1_b00_spi_init();
+ p852_sku1_b00_i2s_init();
+ p852_sku1_b00_uart_init();
+ p852_sku1_b00_sdhci_init();
+ p852_sku1_b00_i2c_init();
+ p852_sku1_b00_display_init();
+ p852_sku1_b00_ulpi_init();
+
+ p852_common_init();
+}
+
diff --git a/arch/arm/mach-tegra/p852/board-p852-sku1-c0x.c b/arch/arm/mach-tegra/p852/board-p852-sku1-c0x.c
new file mode 100644
index 000000000000..4a783fb9b635
--- /dev/null
+++ b/arch/arm/mach-tegra/p852/board-p852-sku1-c0x.c
@@ -0,0 +1,98 @@
+/*
+ * arch/arm/mach-tegra/board-p852-sku1-c0x.c
+ *
+ * Copyright (C) 2010-2011, NVIDIA Corporation.
+ *
+ * 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 "board-p852.h"
+
+static inline void p852_sku1_c0x_spi_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_SPI_ENABLE;
+ p852_spi_peripherals |=
+ ((P852_SPI_MASTER | P852_SPI_ENABLE) << P852_SPI1_SHIFT) |
+ ((P852_SPI_MASTER | P852_SPI_ENABLE) << P852_SPI4_SHIFT);
+}
+
+static inline void p852_sku1_c0x_i2s_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_I2S_ENABLE;
+ p852_i2s_peripherals |= ((P852_I2S_ENABLE | P852_I2S_TDM)
+ << P852_I2S1_SHIFT) | ((P852_I2S_ENABLE | P852_I2S_TDM)
+ << P852_I2S2_SHIFT);
+}
+
+static inline void p852_sku1_c0x_sdhci_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_SDHCI_ENABLE;
+ p852_sdhci_peripherals |=
+ ((P852_SDHCI_ENABLE)
+ << P852_SDHCI4_SHIFT) |
+ ((P852_SDHCI_ENABLE | P852_SDHCI_CD_EN | P852_SDHCI_WP_EN)
+ << P852_SDHCI1_SHIFT) |
+ ((P852_SDHCI_ENABLE | P852_SDHCI_CD_EN | P852_SDHCI_WP_EN)
+ << P852_SDHCI3_SHIFT);
+
+ p852_sdhci_platform_data[0].cd_gpio = TEGRA_GPIO_PV0;
+ p852_sdhci_platform_data[0].wp_gpio = TEGRA_GPIO_PV1;
+ p852_sdhci_platform_data[2].cd_gpio = TEGRA_GPIO_PD7;
+ p852_sdhci_platform_data[2].wp_gpio = TEGRA_GPIO_PT4;
+}
+
+static inline void p852_sku1_c0x_uart_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_UART_ENABLE;
+ p852_uart_peripherals |=
+ ((P852_UART_ENABLE | P852_UART_DB) << P852_UARTD_SHIFT) |
+ ((P852_UART_ENABLE | P852_UART_HS) << P852_UARTB_SHIFT) |
+ ((P852_UART_ENABLE | P852_UART_HS | P852_UART_ALT_PIN_CFG)
+ << P852_UARTA_SHIFT);
+}
+
+static inline void p852_sku1_c0x_display_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_DISPLAY_ENABLE;
+ p852_display_peripherals |=
+ (P852_DISP_ENABLE << P852_DISPB_SHIFT);
+}
+
+static inline void p852_sku1_c0x_ulpi_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_ULPI_DISABLE;
+}
+
+static inline void p852_sku1_c0x_i2c_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_I2C_ENABLE;
+ p852_i2c_peripherals |=
+ ((P852_I2C_ENABLE) << P852_I2C1_SHIFT) |
+ ((P852_I2C_ENABLE) << P852_I2C2_SHIFT) |
+ ((P852_I2C_ENABLE) << P852_I2C3_SHIFT) |
+ ((P852_I2C_ENABLE) << P852_I2C4_SHIFT);
+}
+
+void __init p852_sku1_c0x_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_NOR_ENABLE;
+
+ p852_sku1_c0x_spi_init();
+ p852_sku1_c0x_i2s_init();
+ p852_sku1_c0x_uart_init();
+ p852_sku1_c0x_sdhci_init();
+ p852_sku1_c0x_i2c_init();
+ p852_sku1_c0x_display_init();
+ p852_sku1_c0x_ulpi_init();
+
+ p852_common_init();
+}
+
diff --git a/arch/arm/mach-tegra/p852/board-p852-sku1.c b/arch/arm/mach-tegra/p852/board-p852-sku1.c
new file mode 100644
index 000000000000..387ba054bd84
--- /dev/null
+++ b/arch/arm/mach-tegra/p852/board-p852-sku1.c
@@ -0,0 +1,89 @@
+/*
+ * arch/arm/mach-tegra/board-p852-sku1.c
+ *
+ * Copyright (c) 2010-2011, NVIDIA Corporation.
+ *
+ * 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 "board-p852.h"
+
+static inline void p852_sku1_spi_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_SPI_ENABLE;
+ p852_spi_peripherals |=
+ ((P852_SPI_MASTER | P852_SPI_ENABLE) << P852_SPI1_SHIFT) |
+ ((P852_SPI_MASTER | P852_SPI_ENABLE) << P852_SPI4_SHIFT);
+}
+
+static inline void p852_sku1_i2s_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_I2S_ENABLE;
+ p852_i2s_peripherals |= ((P852_I2S_ENABLE | P852_I2S_TDM)
+ << P852_I2S1_SHIFT) | ((P852_I2S_ENABLE | P852_I2S_TDM)
+ << P852_I2S2_SHIFT);
+}
+
+static inline void p852_sku1_sdhci_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_SDHCI_ENABLE;
+ p852_sdhci_peripherals |=
+ ((P852_SDHCI_ENABLE | P852_SDHCI_CD_EN | P852_SDHCI_WP_EN)
+ << P852_SDHCI1_SHIFT) |
+ ((P852_SDHCI_ENABLE | P852_SDHCI_CD_EN | P852_SDHCI_WP_EN)
+ << P852_SDHCI3_SHIFT) |
+ (P852_SDHCI_ENABLE << P852_SDHCI4_SHIFT);
+
+ p852_sdhci_platform_data[0].cd_gpio = TEGRA_GPIO_PV0;
+ p852_sdhci_platform_data[0].wp_gpio = TEGRA_GPIO_PV1;
+ p852_sdhci_platform_data[2].cd_gpio = TEGRA_GPIO_PD7;
+ p852_sdhci_platform_data[2].wp_gpio = TEGRA_GPIO_PT4;
+}
+
+static inline void p852_sku1_uart_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_UART_ENABLE;
+ p852_uart_peripherals |=
+ ((P852_UART_ENABLE | P852_UART_DB) << P852_UARTD_SHIFT) |
+ ((P852_UART_ENABLE | P852_UART_HS) << P852_UARTB_SHIFT);
+}
+
+static inline void p852_sku1_display_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_DISPLAY_ENABLE;
+ p852_display_peripherals |=
+ (P852_DISP_ENABLE << P852_DISPB_SHIFT);
+}
+
+static inline void p852_sku1_i2c_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_I2C_ENABLE;
+ p852_i2c_peripherals |=
+ ((P852_I2C_ENABLE) << P852_I2C1_SHIFT) |
+ ((P852_I2C_ENABLE) << P852_I2C2_SHIFT) |
+ ((P852_I2C_ENABLE) << P852_I2C3_SHIFT) |
+ ((P852_I2C_ENABLE) << P852_I2C4_SHIFT);
+}
+
+void __init p852_sku1_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_NOR_ENABLE;
+
+ p852_sku1_spi_init();
+ p852_sku1_i2s_init();
+ p852_sku1_uart_init();
+ p852_sku1_sdhci_init();
+ p852_sku1_i2c_init();
+ p852_sku1_display_init();
+
+ p852_common_init();
+}
+
diff --git a/arch/arm/mach-tegra/p852/board-p852-sku13-b00.c b/arch/arm/mach-tegra/p852/board-p852-sku13-b00.c
new file mode 100644
index 000000000000..39e01f660eaf
--- /dev/null
+++ b/arch/arm/mach-tegra/p852/board-p852-sku13-b00.c
@@ -0,0 +1,114 @@
+/*
+ * arch/arm/mach-tegra/board-p852-sku13-b00.c
+ *
+ * Copyright (c) 2010-2011, NVIDIA Corporation.
+ *
+ * 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 "board-p852.h"
+
+static inline void p852_sku13_b00_spi_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_SPI_ENABLE;
+ p852_spi_peripherals |=
+ ((P852_SPI_MASTER | P852_SPI_ENABLE) << P852_SPI1_SHIFT) |
+ ((P852_SPI_SLAVE | P852_SPI_ENABLE) << P852_SPI2_SHIFT) |
+ ((P852_SPI_SLAVE | P852_SPI_ENABLE) << P852_SPI3_SHIFT) |
+ ((P852_SPI_MASTER | P852_SPI_ENABLE) << P852_SPI4_SHIFT);
+}
+
+static inline void p852_sku13_b00_i2s_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_I2S_ENABLE;
+ p852_i2s_peripherals |= (P852_I2S_ENABLE << P852_I2S1_SHIFT) |
+ (P852_I2S_ENABLE << P852_I2S2_SHIFT);
+}
+
+static inline void p852_sku13_b00_sdhci_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_SDHCI_ENABLE;
+ p852_sdhci_peripherals |=
+ (P852_SDHCI_ENABLE << P852_SDHCI1_SHIFT) |
+ (P852_SDHCI_ENABLE << P852_SDHCI2_SHIFT);
+
+ p852_sdhci_platform_data[1].is_8bit = true;
+}
+
+static inline void p852_sku13_b00_uart_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_UART_ENABLE;
+ p852_uart_peripherals |=
+ ((P852_UART_ENABLE | P852_UART_DB) << P852_UARTA_SHIFT) |
+ ((P852_UART_ENABLE | P852_UART_HS) << P852_UARTB_SHIFT) |
+ ((P852_UART_ENABLE) << P852_UARTC_SHIFT);
+}
+
+static inline void p852_sku13_b00_display_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_DISPLAY_ENABLE;
+ p852_display_peripherals |=
+ (P852_DISP_ENABLE << P852_DISPA_SHIFT);
+}
+
+static inline void p852_sku13_b00_i2c_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_I2C_ENABLE;
+ p852_i2c_peripherals |=
+ ((P852_I2C_ENABLE) << P852_I2C1_SHIFT) |
+ ((P852_I2C_ENABLE) << P852_I2C2_SHIFT) |
+ ((P852_I2C_ENABLE) << P852_I2C3_SHIFT) |
+ ((P852_I2C_ENABLE) << P852_I2C4_SHIFT);
+ p852_i2c_set_default_clock(0, 40000);
+}
+
+#ifdef CONFIG_TEGRA_SPI_I2S
+static struct tegra_spi_i2s_platform_data spi_i2s_data = {
+ .gpio_i2s = {
+ .gpio_no = TEGRA_GPIO_PT5,
+ .active_state = 1,
+ },
+ .gpio_spi = {
+ .gpio_no = TEGRA_GPIO_PV7,
+ .active_state = 1,
+ },
+ .spi_i2s_timeout_ms = 25,
+};
+
+static inline void p852_sku13_b00_spi_i2s_init(void)
+{
+ tegra_spi_i2s_device.platform_data = &spi_i2s_data;
+ /* cpld_gpio_dir1 */
+ tegra_pinmux_set_tristate(TEGRA_PINGROUP_PTA, TEGRA_TRI_NORMAL);
+ /* cpld_gpio_dir2 */
+ tegra_pinmux_set_tristate(TEGRA_PINGROUP_LVP0, TEGRA_TRI_NORMAL);
+ p852_spi_i2s_init();
+}
+#endif
+
+void __init p852_sku13_b00_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_NAND_ENABLE;
+
+
+ p852_sku13_b00_spi_init();
+ p852_sku13_b00_i2s_init();
+ p852_sku13_b00_uart_init();
+ p852_sku13_b00_sdhci_init();
+ p852_sku13_b00_i2c_init();
+ p852_sku13_b00_display_init();
+
+ p852_common_init();
+
+#ifdef CONFIG_TEGRA_SPI_I2S
+ p852_sku13_b00_spi_i2s_init();
+#endif
+}
diff --git a/arch/arm/mach-tegra/p852/board-p852-sku13.c b/arch/arm/mach-tegra/p852/board-p852-sku13.c
new file mode 100644
index 000000000000..92d917e6e2c1
--- /dev/null
+++ b/arch/arm/mach-tegra/p852/board-p852-sku13.c
@@ -0,0 +1,112 @@
+/*
+ * arch/arm/mach-tegra/board-p852-sku13.c
+ *
+ * Copyright (c) 2010-2011, NVIDIA Corporation.
+ *
+ * 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 "board-p852.h"
+
+static inline void p852_sku13_spi_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_SPI_ENABLE;
+ p852_spi_peripherals |=
+ ((P852_SPI_MASTER | P852_SPI_ENABLE) << P852_SPI1_SHIFT) |
+ ((P852_SPI_SLAVE | P852_SPI_ENABLE) << P852_SPI2_SHIFT) |
+ ((P852_SPI_SLAVE | P852_SPI_ENABLE) << P852_SPI3_SHIFT) |
+ ((P852_SPI_MASTER | P852_SPI_ENABLE) << P852_SPI4_SHIFT);
+
+}
+
+static inline void p852_sku13_i2s_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_I2S_ENABLE;
+ p852_i2s_peripherals |= (P852_I2S_ENABLE << P852_I2S1_SHIFT) |
+ (P852_I2S_ENABLE << P852_I2S2_SHIFT);
+}
+
+static inline void p852_sku13_sdhci_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_SDHCI_ENABLE;
+ p852_sdhci_peripherals |=
+ (P852_SDHCI_ENABLE << P852_SDHCI1_SHIFT) |
+ (P852_SDHCI_ENABLE << P852_SDHCI2_SHIFT);
+
+ p852_sdhci_platform_data[1].is_8bit = true;
+}
+
+static inline void p852_sku13_uart_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_UART_ENABLE;
+ p852_uart_peripherals |=
+ ((P852_UART_ENABLE | P852_UART_DB) << P852_UARTA_SHIFT) |
+ ((P852_UART_ENABLE | P852_UART_HS) << P852_UARTB_SHIFT) |
+ ((P852_UART_ENABLE) << P852_UARTC_SHIFT);
+}
+
+static inline void p852_sku13_display_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_DISPLAY_ENABLE;
+ p852_display_peripherals |=
+ (P852_DISP_ENABLE << P852_DISPA_SHIFT);
+}
+
+static inline void p852_sku13_i2c_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_I2C_ENABLE;
+ p852_i2c_peripherals |=
+ ((P852_I2C_ENABLE) << P852_I2C1_SHIFT) |
+ ((P852_I2C_ENABLE) << P852_I2C2_SHIFT) |
+ ((P852_I2C_ENABLE) << P852_I2C3_SHIFT) |
+ ((P852_I2C_ENABLE) << P852_I2C4_SHIFT);
+ p852_i2c_set_default_clock(0, 40000);
+}
+
+#ifdef CONFIG_TEGRA_SPI_I2S
+static struct tegra_spi_i2s_platform_data spi_i2s_data = {
+ .gpio_i2s = {
+ .gpio_no = TEGRA_GPIO_PS3,
+ .active_state = 0,
+ },
+ .gpio_spi = {
+ .gpio_no = TEGRA_GPIO_PS4,
+ .active_state = 0,
+ },
+ .spi_i2s_timeout_ms = 25,
+};
+
+static inline void p852_sku13_spi_i2s_init(void)
+{
+ tegra_spi_i2s_device.platform_data = &spi_i2s_data;
+ /* cpld_gpio_dir1 and cpld_gpio_dir2*/
+ tegra_pinmux_set_tristate(TEGRA_PINGROUP_KBCB, TEGRA_TRI_NORMAL);
+ p852_spi_i2s_init();
+}
+#endif
+
+void __init p852_sku13_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_NAND_ENABLE;
+
+ p852_sku13_spi_init();
+ p852_sku13_i2s_init();
+ p852_sku13_uart_init();
+ p852_sku13_sdhci_init();
+ p852_sku13_i2c_init();
+ p852_sku13_display_init();
+
+ p852_common_init();
+
+#ifdef CONFIG_TEGRA_SPI_I2S
+ p852_sku13_spi_i2s_init();
+#endif
+}
diff --git a/arch/arm/mach-tegra/p852/board-p852-sku23-b00.c b/arch/arm/mach-tegra/p852/board-p852-sku23-b00.c
new file mode 100644
index 000000000000..6f464ec3620f
--- /dev/null
+++ b/arch/arm/mach-tegra/p852/board-p852-sku23-b00.c
@@ -0,0 +1,115 @@
+/*
+ * arch/arm/mach-tegra/board-p852-sku23-b00.c
+ *
+ * Copyright (c) 2010-2011, NVIDIA Corporation.
+ *
+ * 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 "board-p852.h"
+
+static inline void p852_sku23_b00_spi_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_SPI_ENABLE;
+ p852_spi_peripherals |=
+ ((P852_SPI_SLAVE | P852_SPI_ENABLE) << P852_SPI1_SHIFT) |
+ ((P852_SPI_SLAVE | P852_SPI_ENABLE) << P852_SPI2_SHIFT) |
+ ((P852_SPI_SLAVE | P852_SPI_ENABLE) << P852_SPI3_SHIFT) |
+ ((P852_SPI_SLAVE | P852_SPI_ENABLE) << P852_SPI4_SHIFT);
+}
+
+static inline void p852_sku23_b00_i2s_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_I2S_ENABLE;
+ p852_i2s_peripherals |=
+ ((P852_I2S_TDM | P852_I2S_ENABLE) << P852_I2S1_SHIFT) |
+ ((P852_I2S_TDM | P852_I2S_ENABLE) << P852_I2S2_SHIFT);
+}
+
+static inline void p852_sku23_b00_sdhci_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_SDHCI_ENABLE;
+ p852_sdhci_peripherals |= (P852_SDHCI_ENABLE << P852_SDHCI1_SHIFT) |
+ (P852_SDHCI_ENABLE << P852_SDHCI2_SHIFT) |
+ ((P852_SDHCI_ENABLE | P852_SDHCI_CD_EN) << P852_SDHCI3_SHIFT);
+
+ p852_sdhci_platform_data[1].is_8bit = true;
+ p852_sdhci_platform_data[2].cd_gpio = TEGRA_GPIO_PD7;
+}
+
+static inline void p852_sku23_b00_uart_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_UART_ENABLE;
+ p852_uart_peripherals |=
+ ((P852_UART_ENABLE) << P852_UARTA_SHIFT) |
+ ((P852_UART_ENABLE | P852_UART_HS) << P852_UARTB_SHIFT) |
+ ((P852_UART_ENABLE | P852_UART_DB) << P852_UARTC_SHIFT);
+}
+
+static inline void p852_sku23_b00_display_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_DISPLAY_ENABLE;
+ p852_display_peripherals |=
+ (P852_DISP_ENABLE << P852_DISPA_SHIFT);
+}
+
+static inline void p852_sku23_b00_i2c_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_I2C_ENABLE;
+ p852_i2c_peripherals |=
+ ((P852_I2C_ENABLE) << P852_I2C1_SHIFT) |
+ ((P852_I2C_ENABLE) << P852_I2C2_SHIFT) |
+ ((P852_I2C_ENABLE) << P852_I2C3_SHIFT) |
+ ((P852_I2C_ENABLE) << P852_I2C4_SHIFT);
+}
+
+#ifdef CONFIG_TEGRA_SPI_I2S
+static struct tegra_spi_i2s_platform_data spi_i2s_data = {
+ .gpio_i2s = {
+ .gpio_no = TEGRA_GPIO_PT5,
+ .active_state = 1,
+ },
+ .gpio_spi = {
+ .gpio_no = TEGRA_GPIO_PV7,
+ .active_state = 1,
+ },
+ .spi_i2s_timeout_ms = 25,
+};
+
+static inline void p852_sku23_b00_spi_i2s_init(void)
+{
+ tegra_spi_i2s_device.platform_data = &spi_i2s_data;
+ /* cpld_gpio_dir1 */
+ tegra_pinmux_set_tristate(TEGRA_PINGROUP_PTA, TEGRA_TRI_NORMAL);
+ /* cpld_gpio_dir2 */
+ tegra_pinmux_set_tristate(TEGRA_PINGROUP_LVP0, TEGRA_TRI_NORMAL);
+ p852_spi_i2s_init();
+}
+#endif
+
+void __init p852_sku23_b00_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_NAND_ENABLE;
+
+ p852_sku23_b00_spi_init();
+ p852_sku23_b00_i2s_init();
+ p852_sku23_b00_uart_init();
+ p852_sku23_b00_sdhci_init();
+ p852_sku23_b00_i2c_init();
+ p852_sku23_b00_display_init();
+
+ p852_common_init();
+
+#ifdef CONFIG_TEGRA_SPI_I2S
+ p852_sku23_b00_spi_i2s_init();
+#endif
+}
+
diff --git a/arch/arm/mach-tegra/p852/board-p852-sku23-c01.c b/arch/arm/mach-tegra/p852/board-p852-sku23-c01.c
new file mode 100644
index 000000000000..f946e0ed35ee
--- /dev/null
+++ b/arch/arm/mach-tegra/p852/board-p852-sku23-c01.c
@@ -0,0 +1,87 @@
+/*
+ * arch/arm/mach-tegra/board-p852-sku23-c01.c
+ *
+ * Copyright (c) 2010-2011, NVIDIA Corporation.
+ *
+ * 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 "board-p852.h"
+
+static inline void p852_sku23_c01_spi_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_SPI_ENABLE;
+ p852_spi_peripherals |=
+ ((P852_SPI_SLAVE | P852_SPI_ENABLE) << P852_SPI1_SHIFT) |
+ ((P852_SPI_SLAVE | P852_SPI_ENABLE) << P852_SPI2_SHIFT) |
+ ((P852_SPI_SLAVE | P852_SPI_ENABLE) << P852_SPI3_SHIFT) |
+ ((P852_SPI_SLAVE | P852_SPI_ENABLE) << P852_SPI4_SHIFT);
+}
+
+static inline void p852_sku23_c01_i2s_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_I2S_ENABLE;
+ p852_i2s_peripherals |=
+ ((P852_I2S_TDM | P852_I2S_ENABLE) << P852_I2S1_SHIFT) |
+ ((P852_I2S_TDM | P852_I2S_ENABLE) << P852_I2S2_SHIFT);
+}
+
+static inline void p852_sku23_c01_sdhci_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_SDHCI_ENABLE;
+ p852_sdhci_peripherals |= (P852_SDHCI_ENABLE << P852_SDHCI1_SHIFT) |
+ (P852_SDHCI_ENABLE << P852_SDHCI2_SHIFT) |
+ ((P852_SDHCI_ENABLE | P852_SDHCI_CD_EN) << P852_SDHCI3_SHIFT);
+
+ p852_sdhci_platform_data[1].is_8bit = true;
+ p852_sdhci_platform_data[2].cd_gpio = TEGRA_GPIO_PD7;
+}
+
+static inline void p852_sku23_c01_uart_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_UART_ENABLE;
+ p852_uart_peripherals |=
+ ((P852_UART_ENABLE) << P852_UARTA_SHIFT) |
+ ((P852_UART_ENABLE | P852_UART_HS) << P852_UARTB_SHIFT) |
+ ((P852_UART_ENABLE | P852_UART_DB) << P852_UARTC_SHIFT);
+}
+
+static inline void p852_sku23_c01_display_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_DISPLAY_ENABLE;
+ p852_display_peripherals |=
+ (P852_DISP_ENABLE << P852_DISPA_SHIFT);
+}
+
+static inline void p852_sku23_c01_i2c_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_I2C_ENABLE;
+ p852_i2c_peripherals |=
+ ((P852_I2C_ENABLE) << P852_I2C1_SHIFT) |
+ ((P852_I2C_ENABLE) << P852_I2C2_SHIFT) |
+ ((P852_I2C_ENABLE) << P852_I2C3_SHIFT) |
+ ((P852_I2C_ENABLE) << P852_I2C4_SHIFT);
+}
+
+void __init p852_sku23_c01_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_NAND_ENABLE;
+
+ p852_sku23_c01_spi_init();
+ p852_sku23_c01_i2s_init();
+ p852_sku23_c01_uart_init();
+ p852_sku23_c01_sdhci_init();
+ p852_sku23_c01_i2c_init();
+ p852_sku23_c01_display_init();
+
+ p852_common_init();
+}
+
diff --git a/arch/arm/mach-tegra/p852/board-p852-sku23.c b/arch/arm/mach-tegra/p852/board-p852-sku23.c
new file mode 100644
index 000000000000..a2bc9b4ca0b6
--- /dev/null
+++ b/arch/arm/mach-tegra/p852/board-p852-sku23.c
@@ -0,0 +1,113 @@
+/*
+ * arch/arm/mach-tegra/board-p852-sku23.c
+ *
+ * Copyright (c) 2010-2011, NVIDIA Corporation.
+ *
+ * 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 "board-p852.h"
+
+static inline void __init p852_sku23_spi_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_SPI_ENABLE;
+ p852_spi_peripherals |=
+ ((P852_SPI_SLAVE | P852_SPI_ENABLE) << P852_SPI1_SHIFT) |
+ ((P852_SPI_SLAVE | P852_SPI_ENABLE) << P852_SPI2_SHIFT) |
+ ((P852_SPI_SLAVE | P852_SPI_ENABLE) << P852_SPI3_SHIFT) |
+ ((P852_SPI_SLAVE | P852_SPI_ENABLE) << P852_SPI4_SHIFT);
+}
+
+static inline void p852_sku23_sdhci_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_SDHCI_ENABLE;
+ p852_sdhci_peripherals |= (P852_SDHCI_ENABLE << P852_SDHCI1_SHIFT) |
+ (P852_SDHCI_ENABLE << P852_SDHCI2_SHIFT) |
+ ((P852_SDHCI_ENABLE | P852_SDHCI_CD_EN) << P852_SDHCI3_SHIFT);
+
+ p852_sdhci_platform_data[1].is_8bit = true;
+ p852_sdhci_platform_data[2].cd_gpio = TEGRA_GPIO_PD7;
+}
+
+static inline void p852_sku23_i2s_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_I2S_ENABLE;
+ p852_i2s_peripherals |=
+ ((P852_I2S_TDM | P852_I2S_ENABLE) << P852_I2S1_SHIFT) |
+ ((P852_I2S_TDM | P852_I2S_ENABLE) << P852_I2S2_SHIFT);
+}
+
+static inline void p852_sku23_uart_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_UART_ENABLE;
+ p852_uart_peripherals |=
+ ((P852_UART_ENABLE) << P852_UARTA_SHIFT) |
+ ((P852_UART_ENABLE | P852_UART_HS) << P852_UARTB_SHIFT) |
+ ((P852_UART_ENABLE | P852_UART_DB) << P852_UARTC_SHIFT);
+}
+
+static inline void p852_sku23_display_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_DISPLAY_ENABLE;
+ p852_display_peripherals |=
+ (P852_DISP_ENABLE << P852_DISPA_SHIFT);
+}
+
+static inline void p852_sku23_i2c_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_I2C_ENABLE;
+ p852_i2c_peripherals |=
+ ((P852_I2C_ENABLE) << P852_I2C1_SHIFT) |
+ ((P852_I2C_ENABLE) << P852_I2C2_SHIFT) |
+ ((P852_I2C_ENABLE) << P852_I2C3_SHIFT) |
+ ((P852_I2C_ENABLE) << P852_I2C4_SHIFT);
+}
+
+#ifdef CONFIG_TEGRA_SPI_I2S
+static struct tegra_spi_i2s_platform_data spi_i2s_data = {
+ .gpio_i2s = {
+ .gpio_no = TEGRA_GPIO_PS3,
+ .active_state = 0,
+ },
+ .gpio_spi = {
+ .gpio_no = TEGRA_GPIO_PS4,
+ .active_state = 0,
+ },
+ .spi_i2s_timeout_ms = 25,
+};
+
+static inline void p852_sku23_spi_i2s_init(void)
+{
+ tegra_spi_i2s_device.platform_data = &spi_i2s_data;
+ /* cpld_gpio_dir1 and cpld_gpio_dir2*/
+ tegra_pinmux_set_tristate(TEGRA_PINGROUP_KBCB, TEGRA_TRI_NORMAL);
+ p852_spi_i2s_init();
+}
+#endif
+
+void __init p852_sku23_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_NAND_ENABLE;
+
+ p852_sku23_spi_init();
+ p852_sku23_i2s_init();
+ p852_sku23_uart_init();
+ p852_sku23_sdhci_init();
+ p852_sku23_i2c_init();
+ p852_sku23_display_init();
+
+ p852_common_init();
+
+#ifdef CONFIG_TEGRA_SPI_I2S
+ p852_sku23_spi_i2s_init();
+#endif
+}
+
diff --git a/arch/arm/mach-tegra/p852/board-p852-sku3.c b/arch/arm/mach-tegra/p852/board-p852-sku3.c
new file mode 100644
index 000000000000..380df9a7439a
--- /dev/null
+++ b/arch/arm/mach-tegra/p852/board-p852-sku3.c
@@ -0,0 +1,103 @@
+/*
+ * arch/arm/mach-tegra/board-p852-sku3.c
+ *
+ * Copyright (c) 2010-2011, NVIDIA Corporation.
+ *
+ * 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 "board-p852.h"
+
+static inline void p852_sku3_spi_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_SPI_ENABLE;
+ p852_spi_peripherals |=
+ ((P852_SPI_MASTER | P852_SPI_ENABLE) << P852_SPI1_SHIFT) |
+ ((P852_SPI_SLAVE | P852_SPI_ENABLE) << P852_SPI2_SHIFT) |
+ ((P852_SPI_SLAVE | P852_SPI_ENABLE) << P852_SPI3_SHIFT) |
+ ((P852_SPI_MASTER | P852_SPI_ENABLE) << P852_SPI4_SHIFT);
+}
+
+static inline void p852_sku3_i2s_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_I2S_ENABLE;
+ p852_i2s_peripherals |= (P852_I2S_ENABLE << P852_I2S1_SHIFT) |
+ (P852_I2S_ENABLE << P852_I2S2_SHIFT);
+}
+
+static inline void p852_sku3_sdhci_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_SDHCI_ENABLE;
+ p852_sdhci_peripherals |=
+ (P852_SDHCI_ENABLE << P852_SDHCI1_SHIFT) |
+ (P852_SDHCI_ENABLE << P852_SDHCI2_SHIFT);
+
+ p852_sdhci_platform_data[1].is_8bit = true;
+}
+
+static inline void p852_sku3_uart_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_UART_ENABLE;
+ p852_uart_peripherals |=
+ ((P852_UART_ENABLE | P852_UART_DB) << P852_UARTA_SHIFT) |
+ ((P852_UART_ENABLE | P852_UART_HS) << P852_UARTB_SHIFT) |
+ ((P852_UART_ENABLE | P852_UART_HS) << P852_UARTC_SHIFT);
+}
+
+static inline void p852_sku3_i2c_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_I2C_ENABLE;
+ p852_i2c_peripherals |=
+ ((P852_I2C_ENABLE) << P852_I2C1_SHIFT) |
+ ((P852_I2C_ENABLE) << P852_I2C2_SHIFT) |
+ ((P852_I2C_ENABLE) << P852_I2C3_SHIFT) |
+ ((P852_I2C_ENABLE) << P852_I2C4_SHIFT);
+ p852_i2c_set_default_clock(0, 40000);
+}
+
+#ifdef CONFIG_TEGRA_SPI_I2S
+static struct tegra_spi_i2s_platform_data spi_i2s_data = {
+ .gpio_i2s = {
+ .gpio_no = TEGRA_GPIO_PS3,
+ .active_state = 0,
+ },
+ .gpio_spi = {
+ .gpio_no = TEGRA_GPIO_PS4,
+ .active_state = 0,
+ },
+ .spi_i2s_timeout_ms = 25,
+};
+
+static inline void p852_sku3_spi_i2s_init(void)
+{
+ tegra_spi_i2s_device.platform_data = &spi_i2s_data;
+ /* cpld_gpio_dir1 and cpld_gpio_dir2*/
+ tegra_pinmux_set_tristate(TEGRA_PINGROUP_KBCB, TEGRA_TRI_NORMAL);
+ p852_spi_i2s_init();
+}
+#endif
+
+void __init p852_sku3_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_NAND_ENABLE;
+
+ p852_sku3_spi_init();
+ p852_sku3_i2s_init();
+ p852_sku3_uart_init();
+ p852_sku3_sdhci_init();
+ p852_sku3_i2c_init();
+
+ p852_common_init();
+
+#ifdef CONFIG_TEGRA_SPI_I2S
+ p852_sku3_spi_i2s_init();
+#endif
+}
diff --git a/arch/arm/mach-tegra/p852/board-p852-sku5-b00.c b/arch/arm/mach-tegra/p852/board-p852-sku5-b00.c
new file mode 100644
index 000000000000..59f6f13f7729
--- /dev/null
+++ b/arch/arm/mach-tegra/p852/board-p852-sku5-b00.c
@@ -0,0 +1,115 @@
+/*
+ * arch/arm/mach-tegra/board-p852-sku5_b00.c
+ *
+ * Copyright (c) 2010-2011, NVIDIA Corporation.
+ *
+ * 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 "board-p852.h"
+
+static inline void p852_sku5_b00_spi_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_SPI_ENABLE;
+ p852_spi_peripherals |=
+ ((P852_SPI_SLAVE | P852_SPI_ENABLE) << P852_SPI1_SHIFT) |
+ ((P852_SPI_SLAVE | P852_SPI_ENABLE) << P852_SPI2_SHIFT) |
+ ((P852_SPI_SLAVE | P852_SPI_ENABLE) << P852_SPI3_SHIFT) |
+ ((P852_SPI_SLAVE | P852_SPI_ENABLE) << P852_SPI4_SHIFT);
+}
+
+static inline void p852_sku5_b00_i2s_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_I2S_ENABLE;
+ p852_i2s_peripherals |=
+ ((P852_I2S_TDM | P852_I2S_ENABLE) << P852_I2S1_SHIFT) |
+ ((P852_I2S_TDM | P852_I2S_ENABLE) << P852_I2S2_SHIFT);
+}
+
+static inline void p852_sku5_b00_sdhci_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_SDHCI_ENABLE;
+
+ p852_sdhci_peripherals |=
+ ((P852_SDHCI_ENABLE << P852_SDHCI1_SHIFT) |
+ (P852_SDHCI_ENABLE << P852_SDHCI2_SHIFT));
+
+ p852_sdhci_platform_data[1].is_8bit = true;
+}
+
+static inline void p852_sku5_b00_uart_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_UART_ENABLE;
+ p852_uart_peripherals |=
+ ((P852_UART_ENABLE) << P852_UARTA_SHIFT) |
+ ((P852_UART_ENABLE | P852_UART_HS) << P852_UARTB_SHIFT) |
+ ((P852_UART_ENABLE | P852_UART_DB) << P852_UARTC_SHIFT);
+}
+
+static inline void p852_sku5_b00_display_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_DISPLAY_ENABLE;
+ p852_display_peripherals |=
+ (P852_DISP_ENABLE << P852_DISPA_SHIFT);
+}
+
+static inline void p852_sku5_b00_i2c_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_I2C_ENABLE;
+ p852_i2c_peripherals |=
+ ((P852_I2C_ENABLE) << P852_I2C1_SHIFT) |
+ ((P852_I2C_ENABLE) << P852_I2C2_SHIFT) |
+ ((P852_I2C_ENABLE) << P852_I2C3_SHIFT) |
+ ((P852_I2C_ENABLE) << P852_I2C4_SHIFT);
+}
+
+#ifdef CONFIG_TEGRA_SPI_I2S
+static struct tegra_spi_i2s_platform_data spi_i2s_data = {
+ .gpio_i2s = {
+ .gpio_no = TEGRA_GPIO_PT5,
+ .active_state = 1,
+ },
+ .gpio_spi = {
+ .gpio_no = TEGRA_GPIO_PV7,
+ .active_state = 1,
+ },
+ .spi_i2s_timeout_ms = 25,
+};
+
+static inline void p852_sku5_b00_spi_i2s_init(void)
+{
+ tegra_spi_i2s_device.platform_data = &spi_i2s_data;
+ /* cpld_gpio_dir1 */
+ tegra_pinmux_set_tristate(TEGRA_PINGROUP_PTA, TEGRA_TRI_NORMAL);
+ /* cpld_gpio_dir2 */
+ tegra_pinmux_set_tristate(TEGRA_PINGROUP_LVP0, TEGRA_TRI_NORMAL);
+ p852_spi_i2s_init();
+}
+#endif
+
+void __init p852_sku5_b00_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_NAND_ENABLE;
+
+ p852_sku5_b00_spi_init();
+ p852_sku5_b00_i2s_init();
+ p852_sku5_b00_uart_init();
+ p852_sku5_b00_sdhci_init();
+ p852_sku5_b00_i2c_init();
+ p852_sku5_b00_display_init();
+
+ p852_common_init();
+
+#ifdef CONFIG_TEGRA_SPI_I2S
+ p852_sku5_b00_spi_i2s_init();
+#endif
+}
+
diff --git a/arch/arm/mach-tegra/p852/board-p852-sku5-c01.c b/arch/arm/mach-tegra/p852/board-p852-sku5-c01.c
new file mode 100644
index 000000000000..f9c8e72911b6
--- /dev/null
+++ b/arch/arm/mach-tegra/p852/board-p852-sku5-c01.c
@@ -0,0 +1,93 @@
+/*
+ * arch/arm/mach-tegra/board-p852-sku5-c01.c
+ *
+ * Copyright (c) 2010-2011, NVIDIA Corporation.
+ *
+ * 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 "board-p852.h"
+
+static inline void p852_sku5_c01_spi_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_SPI_ENABLE;
+ p852_spi_peripherals |=
+ ((P852_SPI_SLAVE | P852_SPI_ENABLE) << P852_SPI1_SHIFT) |
+ ((P852_SPI_SLAVE | P852_SPI_ENABLE) << P852_SPI2_SHIFT) |
+ ((P852_SPI_SLAVE | P852_SPI_ENABLE) << P852_SPI3_SHIFT) |
+ ((P852_SPI_SLAVE | P852_SPI_ENABLE) << P852_SPI4_SHIFT);
+}
+
+static inline void p852_sku5_c01_i2s_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_I2S_ENABLE;
+ p852_i2s_peripherals |=
+ ((P852_I2S_TDM | P852_I2S_ENABLE) << P852_I2S1_SHIFT) |
+ ((P852_I2S_TDM | P852_I2S_ENABLE) << P852_I2S2_SHIFT);
+}
+
+static inline void p852_sku5_c01_sdhci_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_SDHCI_ENABLE;
+ p852_sdhci_peripherals |=
+ ((P852_SDHCI_ENABLE | P852_SDHCI_CD_EN | P852_SDHCI_WP_EN)
+ << P852_SDHCI1_SHIFT);
+
+ p852_sdhci_platform_data[0].cd_gpio = TEGRA_GPIO_PV0;
+ p852_sdhci_platform_data[0].wp_gpio = TEGRA_GPIO_PV1;
+}
+
+static inline void p852_sku5_c01_uart_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_UART_ENABLE;
+ p852_uart_peripherals |=
+ ((P852_UART_ENABLE) << P852_UARTA_SHIFT) |
+ ((P852_UART_ENABLE | P852_UART_HS) << P852_UARTB_SHIFT) |
+ ((P852_UART_ENABLE | P852_UART_DB) << P852_UARTC_SHIFT);
+}
+
+static inline void p852_sku5_c01_display_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_DISPLAY_ENABLE;
+ p852_display_peripherals |=
+ (P852_DISP_ENABLE << P852_DISPA_SHIFT);
+}
+
+static inline void p852_sku5_c01_i2c_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_I2C_ENABLE;
+ p852_i2c_peripherals |=
+ ((P852_I2C_ENABLE) << P852_I2C1_SHIFT) |
+ ((P852_I2C_ENABLE) << P852_I2C2_SHIFT) |
+ ((P852_I2C_ENABLE) << P852_I2C3_SHIFT) |
+ ((P852_I2C_ENABLE) << P852_I2C4_SHIFT);
+}
+
+static inline void p852_sku5_c01_ulpi_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_ULPI_DISABLE;
+}
+
+void __init p852_sku5_c01_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_NAND_ENABLE;
+
+ p852_sku5_c01_spi_init();
+ p852_sku5_c01_i2s_init();
+ p852_sku5_c01_uart_init();
+ p852_sku5_c01_sdhci_init();
+ p852_sku5_c01_i2c_init();
+ p852_sku5_c01_display_init();
+ p852_sku5_c01_ulpi_init();
+
+ p852_common_init();
+}
+
diff --git a/arch/arm/mach-tegra/p852/board-p852-sku8-b00.c b/arch/arm/mach-tegra/p852/board-p852-sku8-b00.c
new file mode 100644
index 000000000000..4cc4d53d980f
--- /dev/null
+++ b/arch/arm/mach-tegra/p852/board-p852-sku8-b00.c
@@ -0,0 +1,88 @@
+/*
+ * arch/arm/mach-tegra/board-p852-sku8-b00.c
+ *
+ * Copyright (C) 2010-2011, NVIDIA Corporation.
+ *
+ * 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 "board-p852.h"
+
+static inline void p852_sku8_b00_spi_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_SPI_ENABLE;
+ p852_spi_peripherals |=
+ ((P852_SPI_MASTER | P852_SPI_ENABLE) << P852_SPI1_SHIFT) |
+ ((P852_SPI_MASTER | P852_SPI_ENABLE) << P852_SPI4_SHIFT);
+}
+
+static inline void p852_sku8_b00_i2s_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_I2S_ENABLE;
+ p852_i2s_peripherals |= ((P852_I2S_ENABLE | P852_I2S_TDM)
+ << P852_I2S1_SHIFT) | ((P852_I2S_ENABLE | P852_I2S_TDM)
+ << P852_I2S2_SHIFT);
+}
+
+static inline void p852_sku8_b00_sdhci_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_SDHCI_ENABLE;
+ p852_sdhci_peripherals |=
+ ((P852_SDHCI_ENABLE)
+ << P852_SDHCI4_SHIFT) |
+ ((P852_SDHCI_ENABLE | P852_SDHCI_CD_EN | P852_SDHCI_WP_EN)
+ << P852_SDHCI1_SHIFT) |
+ ((P852_SDHCI_ENABLE | P852_SDHCI_CD_EN | P852_SDHCI_WP_EN)
+ << P852_SDHCI3_SHIFT);
+
+ p852_sdhci_platform_data[0].cd_gpio = TEGRA_GPIO_PV0;
+ p852_sdhci_platform_data[0].wp_gpio = TEGRA_GPIO_PV1;
+ p852_sdhci_platform_data[2].cd_gpio = TEGRA_GPIO_PD7;
+ p852_sdhci_platform_data[2].wp_gpio = TEGRA_GPIO_PT4;
+}
+
+static inline void p852_sku8_b00_uart_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_UART_ENABLE;
+ p852_uart_peripherals |=
+ ((P852_UART_ENABLE | P852_UART_DB) << P852_UARTD_SHIFT) |
+ ((P852_UART_ENABLE | P852_UART_HS) << P852_UARTB_SHIFT);
+}
+
+static inline void p852_sku8_b00_display_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_DISPLAY_ENABLE;
+}
+
+static inline void p852_sku8_b00_i2c_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_I2C_ENABLE;
+ p852_i2c_peripherals |=
+ ((P852_I2C_ENABLE) << P852_I2C1_SHIFT) |
+ ((P852_I2C_ENABLE) << P852_I2C2_SHIFT) |
+ ((P852_I2C_ENABLE) << P852_I2C4_SHIFT);
+}
+
+
+void __init p852_sku8_b00_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_NOR_ENABLE;
+
+ p852_sku8_b00_spi_init();
+ p852_sku8_b00_i2s_init();
+ p852_sku8_b00_uart_init();
+ p852_sku8_b00_sdhci_init();
+ p852_sku8_b00_display_init();
+ p852_sku8_b00_i2c_init();
+
+ p852_common_init();
+}
+
diff --git a/arch/arm/mach-tegra/p852/board-p852-sku8-c01.c b/arch/arm/mach-tegra/p852/board-p852-sku8-c01.c
new file mode 100644
index 000000000000..71210cd12b90
--- /dev/null
+++ b/arch/arm/mach-tegra/p852/board-p852-sku8-c01.c
@@ -0,0 +1,87 @@
+/*
+ * arch/arm/mach-tegra/board-p852-sku8-c00.c
+ *
+ * Copyright (C) 2010-2011, NVIDIA Corporation.
+ *
+ * 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 "board-p852.h"
+
+static inline void p852_sku8_c00_spi_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_SPI_ENABLE;
+ p852_spi_peripherals |=
+ ((P852_SPI_MASTER | P852_SPI_ENABLE) << P852_SPI1_SHIFT) |
+ ((P852_SPI_MASTER | P852_SPI_ENABLE) << P852_SPI4_SHIFT);
+}
+
+static inline void p852_sku8_c00_i2s_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_I2S_ENABLE;
+ p852_i2s_peripherals |= ((P852_I2S_ENABLE | P852_I2S_TDM)
+ << P852_I2S1_SHIFT) | ((P852_I2S_ENABLE | P852_I2S_TDM)
+ << P852_I2S2_SHIFT);
+}
+
+static inline void p852_sku8_c00_sdhci_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_SDHCI_ENABLE;
+ p852_sdhci_peripherals |=
+ ((P852_SDHCI_ENABLE)
+ << P852_SDHCI4_SHIFT) |
+ ((P852_SDHCI_ENABLE | P852_SDHCI_CD_EN | P852_SDHCI_WP_EN)
+ << P852_SDHCI1_SHIFT) |
+ ((P852_SDHCI_ENABLE | P852_SDHCI_CD_EN | P852_SDHCI_WP_EN)
+ << P852_SDHCI3_SHIFT);
+
+ p852_sdhci_platform_data[0].cd_gpio = TEGRA_GPIO_PV0;
+ p852_sdhci_platform_data[0].wp_gpio = TEGRA_GPIO_PV1;
+ p852_sdhci_platform_data[2].cd_gpio = TEGRA_GPIO_PD7;
+ p852_sdhci_platform_data[2].wp_gpio = TEGRA_GPIO_PT4;
+}
+
+static inline void p852_sku8_c00_uart_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_UART_ENABLE;
+ p852_uart_peripherals |=
+ ((P852_UART_ENABLE | P852_UART_DB) << P852_UARTD_SHIFT) |
+ ((P852_UART_ENABLE | P852_UART_HS) << P852_UARTB_SHIFT);
+}
+
+static inline void p852_sku8_c00_display_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_DISPLAY_ENABLE;
+}
+
+static inline void p852_sku8_c00_i2c_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_I2C_ENABLE;
+ p852_i2c_peripherals |=
+ ((P852_I2C_ENABLE) << P852_I2C1_SHIFT) |
+ ((P852_I2C_ENABLE) << P852_I2C2_SHIFT) |
+ ((P852_I2C_ENABLE) << P852_I2C4_SHIFT);
+}
+
+
+void __init p852_sku8_c00_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_NOR_ENABLE;
+
+ p852_sku8_c00_spi_init();
+ p852_sku8_c00_i2s_init();
+ p852_sku8_c00_uart_init();
+ p852_sku8_c00_sdhci_init();
+ p852_sku8_c00_display_init();
+ p852_sku8_c00_i2c_init();
+
+ p852_common_init();
+}
diff --git a/arch/arm/mach-tegra/p852/board-p852-sku9-b00.c b/arch/arm/mach-tegra/p852/board-p852-sku9-b00.c
new file mode 100644
index 000000000000..7c3d9c3d9a3d
--- /dev/null
+++ b/arch/arm/mach-tegra/p852/board-p852-sku9-b00.c
@@ -0,0 +1,93 @@
+/*
+ * arch/arm/mach-tegra/board-p852-sku9-b00.c
+ *
+ * Copyright (C) 2010-2011, NVIDIA Corporation.
+ *
+ * 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 "board-p852.h"
+
+static inline void p852_sku9_b00_spi_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_SPI_ENABLE;
+ p852_spi_peripherals |=
+ ((P852_SPI_MASTER | P852_SPI_ENABLE) << P852_SPI1_SHIFT) |
+ ((P852_SPI_MASTER | P852_SPI_ENABLE) << P852_SPI4_SHIFT);
+}
+
+static inline void p852_sku9_b00_i2s_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_I2S_ENABLE;
+ p852_i2s_peripherals |= ((P852_I2S_ENABLE | P852_I2S_TDM)
+ << P852_I2S1_SHIFT) | ((P852_I2S_ENABLE | P852_I2S_TDM)
+ << P852_I2S2_SHIFT);
+}
+
+static inline void p852_sku9_b00_sdhci_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_SDHCI_ENABLE;
+ p852_sdhci_peripherals |=
+ ((P852_SDHCI_ENABLE)
+ << P852_SDHCI4_SHIFT) |
+ ((P852_SDHCI_ENABLE | P852_SDHCI_CD_EN | P852_SDHCI_WP_EN)
+ << P852_SDHCI1_SHIFT) |
+ ((P852_SDHCI_ENABLE | P852_SDHCI_CD_EN | P852_SDHCI_WP_EN)
+ << P852_SDHCI3_SHIFT);
+
+ p852_sdhci_platform_data[0].cd_gpio = TEGRA_GPIO_PV0;
+ p852_sdhci_platform_data[0].wp_gpio = TEGRA_GPIO_PV1;
+ p852_sdhci_platform_data[2].cd_gpio = TEGRA_GPIO_PD7;
+ p852_sdhci_platform_data[2].wp_gpio = TEGRA_GPIO_PT4;
+}
+
+static inline void p852_sku9_b00_uart_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_UART_ENABLE;
+ p852_uart_peripherals |=
+ ((P852_UART_ENABLE | P852_UART_DB) << P852_UARTD_SHIFT) |
+ ((P852_UART_ENABLE | P852_UART_HS) << P852_UARTB_SHIFT);
+}
+
+static inline void p852_sku9_b00_display_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_DISPLAY_ENABLE;
+}
+
+static inline void p852_sku9_b00_ulpi_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_ULPI_DISABLE;
+}
+
+static inline void p852_sku9_b00_i2c_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_I2C_ENABLE;
+ p852_i2c_peripherals |=
+ ((P852_I2C_ENABLE) << P852_I2C1_SHIFT) |
+ ((P852_I2C_ENABLE) << P852_I2C2_SHIFT) |
+ ((P852_I2C_ENABLE) << P852_I2C4_SHIFT);
+}
+
+void __init p852_sku9_b00_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_NOR_ENABLE;
+
+ p852_sku9_b00_spi_init();
+ p852_sku9_b00_i2s_init();
+ p852_sku9_b00_uart_init();
+ p852_sku9_b00_sdhci_init();
+ p852_sku9_b00_display_init();
+ p852_sku9_b00_ulpi_init();
+ p852_sku9_b00_i2c_init();
+
+ p852_common_init();
+}
+
diff --git a/arch/arm/mach-tegra/p852/board-p852-sku9-c01.c b/arch/arm/mach-tegra/p852/board-p852-sku9-c01.c
new file mode 100644
index 000000000000..94c79294fb47
--- /dev/null
+++ b/arch/arm/mach-tegra/p852/board-p852-sku9-c01.c
@@ -0,0 +1,92 @@
+/*
+ * arch/arm/mach-tegra/board-p852-sku9-c00.c
+ *
+ * Copyright (C) 2010-2011, NVIDIA Corporation.
+ *
+ * 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 "board-p852.h"
+
+static inline void p852_sku9_c00_spi_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_SPI_ENABLE;
+ p852_spi_peripherals |=
+ ((P852_SPI_MASTER | P852_SPI_ENABLE) << P852_SPI1_SHIFT) |
+ ((P852_SPI_MASTER | P852_SPI_ENABLE) << P852_SPI4_SHIFT);
+}
+
+static inline void p852_sku9_c00_i2s_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_I2S_ENABLE;
+ p852_i2s_peripherals |= ((P852_I2S_ENABLE | P852_I2S_TDM)
+ << P852_I2S1_SHIFT) | ((P852_I2S_ENABLE | P852_I2S_TDM)
+ << P852_I2S2_SHIFT);
+}
+
+static inline void p852_sku9_c00_sdhci_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_SDHCI_ENABLE;
+ p852_sdhci_peripherals |=
+ ((P852_SDHCI_ENABLE)
+ << P852_SDHCI4_SHIFT) |
+ ((P852_SDHCI_ENABLE | P852_SDHCI_CD_EN | P852_SDHCI_WP_EN)
+ << P852_SDHCI1_SHIFT) |
+ ((P852_SDHCI_ENABLE | P852_SDHCI_CD_EN | P852_SDHCI_WP_EN)
+ << P852_SDHCI3_SHIFT);
+
+ p852_sdhci_platform_data[0].cd_gpio = TEGRA_GPIO_PV0;
+ p852_sdhci_platform_data[0].wp_gpio = TEGRA_GPIO_PV1;
+ p852_sdhci_platform_data[2].cd_gpio = TEGRA_GPIO_PD7;
+ p852_sdhci_platform_data[2].wp_gpio = TEGRA_GPIO_PT4;
+}
+
+static inline void p852_sku9_c00_uart_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_UART_ENABLE;
+ p852_uart_peripherals |=
+ ((P852_UART_ENABLE | P852_UART_DB) << P852_UARTD_SHIFT) |
+ ((P852_UART_ENABLE | P852_UART_HS) << P852_UARTB_SHIFT);
+}
+
+static inline void p852_sku9_c00_display_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_DISPLAY_ENABLE;
+}
+
+static inline void p852_sku9_c00_ulpi_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_ULPI_DISABLE;
+}
+
+static inline void p852_sku9_c00_i2c_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_I2C_ENABLE;
+ p852_i2c_peripherals |=
+ ((P852_I2C_ENABLE) << P852_I2C1_SHIFT) |
+ ((P852_I2C_ENABLE) << P852_I2C2_SHIFT) |
+ ((P852_I2C_ENABLE) << P852_I2C4_SHIFT);
+}
+
+void __init p852_sku9_c00_init(void)
+{
+ p852_sku_peripherals |= P852_SKU_NOR_ENABLE;
+
+ p852_sku9_c00_spi_init();
+ p852_sku9_c00_i2s_init();
+ p852_sku9_c00_uart_init();
+ p852_sku9_c00_sdhci_init();
+ p852_sku9_c00_display_init();
+ p852_sku9_c00_ulpi_init();
+ p852_sku9_c00_i2c_init();
+
+ p852_common_init();
+}
diff --git a/arch/arm/mach-tegra/p852/board-p852.c b/arch/arm/mach-tegra/p852/board-p852.c
new file mode 100644
index 000000000000..61988bc5f55d
--- /dev/null
+++ b/arch/arm/mach-tegra/p852/board-p852.c
@@ -0,0 +1,810 @@
+/*
+ * arch/arm/mach-tegra/board-p852.c
+ *
+ * Copyright (c) 2010-2011, NVIDIA Corporation.
+ *
+ * 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 "board-p852.h"
+#include <mach/spdif.h>
+
+unsigned int p852_sku_peripherals;
+unsigned int p852_spi_peripherals;
+unsigned int p852_i2s_peripherals;
+unsigned int p852_uart_peripherals;
+unsigned int p852_i2c_peripherals;
+unsigned int p852_sdhci_peripherals;
+unsigned int p852_display_peripherals;
+
+/* If enable_usb3 can have two options ehci3=eth or usb*/
+static char enable_usb3[4];
+
+int __init parse_enable_usb3(char *arg)
+{
+ if (!arg)
+ return 0;
+
+ strncpy(enable_usb3, arg, sizeof(enable_usb3));
+ return 0;
+}
+
+early_param("ehci3", parse_enable_usb3);
+
+static __initdata struct tegra_clk_init_table p852_clk_init_table[] = {
+ /* name parent rate enabled */
+ {"uarta", "pll_p", 216000000, true},
+ {"uartb", "pll_p", 216000000, true},
+ {"uartc", "pll_p", 216000000, true},
+ {"uartd", "pll_p", 216000000, true},
+ {"pll_m", "clk_m", 600000000, true},
+ {"pll_m_out1", "pll_m", 240000000, true},
+ {"pll_p_out4", "pll_p", 240000000, true},
+ {"host1x", "pll_p", 166000000, true},
+ {"disp1", "pll_p", 216000000, true},
+ {"vi", "pll_m", 100000000, true},
+ {"csus", "pll_m", 100000000, true},
+ {"emc", "pll_m", 600000000, true},
+ {"pll_c", "clk_m", 600000000, true},
+ {"pll_c_out1", "pll_c", 240000000, true},
+ {"pwm", "clk_32k", 32768, false},
+ {"clk_32k", NULL, 32768, true},
+ {"pll_a", NULL, 56448000, true},
+ {"pll_a_out0", "pll_a", 11289600, true},
+ {"audio", "pll_a_out0", 11289600, true},
+ {"audio_2x", "audio", 22579200, false},
+ {"vde", "pll_c", 240000000, false},
+ {"vi_sensor", "pll_m", 111000000, true},
+ {"epp", "pll_m", 111000000, true},
+ {"mpe", "pll_m", 111000000, true},
+ {"i2s1", "pll_a_out0", 11289600, true},
+ {"i2s2", "pll_a_out0", 11289600, true},
+ {"ndflash", "pll_p", 86500000, true},
+ {"sbc1", "pll_p", 12000000, false},
+ {"spdif_in", "pll_m", 22579000, true},
+ {"spdif_out", "pll_a_out0", 5644800, true},
+ {"sbc2", "pll_p", 12000000, false},
+ {"sbc3", "pll_p", 12000000, false},
+ {"sbc4", "pll_p", 12000000, false},
+ {"nor", "pll_p", 86500000, true},
+ {NULL, NULL, 0, 0},
+};
+
+static struct tegra_nand_chip_parms nand_chip_parms[] = {
+ /* Micron 29F4G08ABADA */
+ [0] = {
+ .vendor_id = 0x2C,
+ .device_id = 0xDC,
+ .capacity = 512,
+ .read_id_fourth_byte = 0x95,
+ .timing = {
+ .trp = 1,
+ .trh = 1,
+ .twp = 12,
+ .twh = 12,
+ .tcs = 24,
+ .twhr = 58,
+ .tcr_tar_trr = 12,
+ .twb = 116,
+ .trp_resp = 24,
+ .tadl = 24,
+ },
+ },
+ /* Micron 29F4G16ABADA */
+ [1] = {
+ .vendor_id = 0x2C,
+ .device_id = 0xCC,
+ .capacity = 512,
+ .read_id_fourth_byte = 0xD5,
+ .timing = {
+ .trp = 10,
+ .trh = 7,
+ .twp = 10,
+ .twh = 7,
+ .tcs = 15,
+ .twhr = 60,
+ .tcr_tar_trr = 20,
+ .twb = 100,
+ .trp_resp = 20,
+ .tadl = 70,
+ },
+ },
+ /* Hynix HY27UF084G2B */
+ [2] = {
+ .vendor_id = 0xAD,
+ .device_id = 0xDC,
+ .read_id_fourth_byte = 0x95,
+ .capacity = 512,
+ .timing = {
+ .trp = 12,
+ .trh = 1,
+ .twp = 12,
+ .twh = 0,
+ .tcs = 24,
+ .twhr = 58,
+ .tcr_tar_trr = 0,
+ .twb = 116,
+ .trp_resp = 24,
+ .tadl = 24,
+ },
+ },
+};
+
+struct tegra_nand_platform p852_nand_data = {
+ .max_chips = 8,
+ .chip_parms = nand_chip_parms,
+ .nr_chip_parms = ARRAY_SIZE(nand_chip_parms),
+ .wp_gpio = TEGRA_GPIO_PC7,
+};
+
+static struct resource resources_nand[] = {
+ [0] = {
+ .start = INT_NANDFLASH,
+ .end = INT_NANDFLASH,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device p852_nand_device = {
+ .name = "tegra_nand",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(resources_nand),
+ .resource = resources_nand,
+ .dev = {
+ .platform_data = &p852_nand_data,
+ },
+};
+
+unsigned int p852_uart_irqs[] = {
+ INT_UARTA,
+ INT_UARTB,
+ INT_UARTC,
+ INT_UARTD,
+};
+
+unsigned int p852_uart_bases[] = {
+ TEGRA_UARTA_BASE,
+ TEGRA_UARTB_BASE,
+ TEGRA_UARTC_BASE,
+ TEGRA_UARTD_BASE,
+};
+
+static struct platform_device *p852_spi_devices[] __initdata = {
+ &tegra_spi_device1,
+ &tegra_spi_device2,
+ &tegra_spi_device3,
+ &tegra_spi_device4,
+};
+
+static struct plat_serial8250_port debug_uart_platform_data[] = {
+ {
+ .flags = UPF_BOOT_AUTOCONF,
+ .iotype = UPIO_MEM,
+ .regshift = 2,
+ .uartclk = 216000000,
+ },
+ {
+ .flags = 0,
+ }
+};
+
+#define DEF_8250_PLATFORM_DATA(_base, _irq) { \
+ .flags = UPF_BOOT_AUTOCONF, \
+ .iotype = UPIO_MEM, \
+ .membase = IO_ADDRESS(_base), \
+ .mapbase = _base, \
+ .irq = _irq, \
+ .regshift = 2, \
+ .uartclk = 216000000, \
+}
+
+static struct plat_serial8250_port tegra_8250_uarta_platform_data[] = {
+ DEF_8250_PLATFORM_DATA(TEGRA_UARTA_BASE, INT_UARTA),
+ {
+ .flags = 0,
+ }
+};
+
+static struct plat_serial8250_port tegra_8250_uartb_platform_data[] = {
+ DEF_8250_PLATFORM_DATA(TEGRA_UARTB_BASE, INT_UARTB),
+ {
+ .flags = 0,
+ }
+};
+
+static struct plat_serial8250_port tegra_8250_uartc_platform_data[] = {
+ DEF_8250_PLATFORM_DATA(TEGRA_UARTC_BASE, INT_UARTC),
+ {
+ .flags = 0,
+ }
+};
+
+static struct plat_serial8250_port tegra_8250_uartd_platform_data[] = {
+ DEF_8250_PLATFORM_DATA(TEGRA_UARTD_BASE, INT_UARTD),
+ {
+ .flags = 0,
+ }
+};
+
+static struct plat_serial8250_port tegra_8250_uarte_platform_data[] = {
+ DEF_8250_PLATFORM_DATA(TEGRA_UARTE_BASE, INT_UARTE),
+ {
+ .flags = 0,
+ }
+};
+
+struct platform_device tegra_8250_uarta_device = {
+ .name = "serial8250",
+ .id = PLAT8250_DEV_PLATFORM,
+ .dev = {
+ .platform_data = tegra_8250_uarta_platform_data,
+ },
+};
+
+struct platform_device tegra_8250_uartb_device = {
+ .name = "serial8250",
+ .id = PLAT8250_DEV_PLATFORM1,
+ .dev = {
+ .platform_data = tegra_8250_uartb_platform_data,
+ },
+};
+
+struct platform_device tegra_8250_uartc_device = {
+ .name = "serial8250",
+ .id = PLAT8250_DEV_PLATFORM2,
+ .dev = {
+ .platform_data = tegra_8250_uartc_platform_data,
+ },
+};
+
+struct platform_device tegra_8250_uartd_device = {
+ .name = "serial8250",
+ .id = PLAT8250_DEV_FOURPORT,
+ .dev = {
+ .platform_data = tegra_8250_uartd_platform_data,
+ },
+};
+
+struct platform_device tegra_8250_uarte_device = {
+ .name = "serial8250",
+ .id = PLAT8250_DEV_ACCENT,
+ .dev = {
+ .platform_data = tegra_8250_uarte_platform_data,
+ },
+};
+
+static struct platform_device debug_uart = {
+ .name = "serial8250",
+ .id = PLAT8250_DEV_PLATFORM,
+ .dev = {
+ .platform_data = debug_uart_platform_data,
+ },
+};
+
+static struct tegra_utmip_config utmi_phy_config[] = {
+ [0] = {
+ .hssync_start_delay = 0,
+ .idle_wait_delay = 17,
+ .elastic_limit = 16,
+ .term_range_adj = 6,
+ .xcvr_setup = 15,
+ .xcvr_lsfslew = 2,
+ .xcvr_lsrslew = 2,
+ },
+ [1] = {
+ .hssync_start_delay = 0,
+ .idle_wait_delay = 17,
+ .elastic_limit = 16,
+ .term_range_adj = 6,
+ .xcvr_setup = 8,
+ .xcvr_lsfslew = 2,
+ .xcvr_lsrslew = 2,
+ },
+};
+
+static struct tegra_ulpi_config ulpi_usb2_config = {
+ .reset_gpio = TEGRA_GPIO_PI5,
+};
+
+static struct tegra_ehci_platform_data tegra_ehci_pdata[] = {
+ [0] = {
+ .phy_config = &utmi_phy_config[0],
+ .operating_mode = TEGRA_USB_HOST,
+ .power_down_on_bus_suspend = 0,
+ },
+ [1] = {
+ .phy_config = &ulpi_usb2_config,
+ .operating_mode = TEGRA_USB_HOST,
+ .power_down_on_bus_suspend = 0,
+ .phy_type = TEGRA_USB_PHY_TYPE_LINK_ULPI,
+ },
+ [2] = {
+ .phy_config = &utmi_phy_config[1],
+ .operating_mode = TEGRA_USB_HOST,
+ .power_down_on_bus_suspend = 0,
+ },
+};
+
+static void p852_usb_gpio_config(void)
+{
+ unsigned int usbeth_mux_gpio = 0, usb_ena_val;
+ unsigned int has_onboard_ethernet = 0;
+ unsigned int p852_eth_reset = TEGRA_GPIO_PD3;
+
+ switch (system_rev) {
+ case P852_SKU13_B00:
+ case P852_SKU23_B00:
+ case P852_SKU23_C01:
+ case P852_SKU8_B00:
+ case P852_SKU8_C01:
+ case P852_SKU9_B00:
+ case P852_SKU9_C01:
+ {
+ usbeth_mux_gpio = TEGRA_GPIO_PS3;
+ has_onboard_ethernet = 1;
+ usb_ena_val = 1;
+ }
+ break;
+ case P852_SKU5_B00:
+ case P852_SKU5_C01:
+ {
+ usb_ena_val = 1;
+ has_onboard_ethernet = 0;
+ }
+ break;
+ case P852_SKU1:
+ {
+ has_onboard_ethernet = 0;
+ usb_ena_val = 0;
+ strncpy(enable_usb3, "usb", sizeof(enable_usb3));
+ }
+ break;
+ case P852_SKU1_B00:
+ case P852_SKU1_C0X:
+ {
+ has_onboard_ethernet = 0;
+ usb_ena_val = 1;
+ strncpy(enable_usb3, "usb", sizeof(enable_usb3));
+ }
+ break;
+ default:
+ {
+ usbeth_mux_gpio = TEGRA_GPIO_PD4;
+ has_onboard_ethernet = 1;
+ usb_ena_val = 0;
+ }
+ }
+
+ if (has_onboard_ethernet) {
+ gpio_request_one(usbeth_mux_gpio, GPIOF_OUT_INIT_LOW,
+ "eth_ena");
+ tegra_gpio_enable(usbeth_mux_gpio);
+
+ /* eth reset */
+ gpio_request_one(p852_eth_reset, GPIOF_OUT_INIT_LOW,
+ "eth_reset");
+ tegra_gpio_enable(p852_eth_reset);
+ udelay(1);
+ gpio_direction_output(p852_eth_reset, 1);
+
+ if (!strcmp(enable_usb3, "eth"))
+ gpio_direction_output(usbeth_mux_gpio, 1);
+
+ /* exporting usbeth_mux_gpio */
+ gpio_export(usbeth_mux_gpio, true);
+ }
+
+ if (!strcmp(enable_usb3, "usb")) {
+ gpio_direction_output(TEGRA_GPIO_PB2, usb_ena_val);
+ gpio_direction_output(TEGRA_GPIO_PW1, usb_ena_val);
+ }
+}
+
+static struct platform_device *p852_uart_devices[] __initdata = {
+ &tegra_uarta_device,
+ &tegra_uartb_device,
+ &tegra_uartc_device,
+ &tegra_uartd_device,
+};
+
+static struct platform_device *p852_8250_uart_devices[] __initdata = {
+ &tegra_8250_uarta_device,
+ &tegra_8250_uartb_device,
+ &tegra_8250_uartc_device,
+ &tegra_8250_uartd_device,
+ &tegra_8250_uarte_device,
+};
+
+static struct platform_device tegra_itu656 = {
+ .name = "tegra_itu656",
+ .id = -1,
+};
+
+static struct platform_device *p852_devices[] __initdata = {
+ &tegra_gart_device,
+ &tegra_avp_device,
+ &tegra_itu656,
+};
+
+#ifdef CONFIG_MTD_NOR_TEGRA
+static struct tegra_nor_chip_parms nor_chip_parms[] = {
+ [0] = {
+ .timing_default = {
+ .pg_rdy = 120,
+ .pg_seq = 35,
+ .mux = 5,
+ .hold = 25,
+ .adv = 50,
+ .ce = 35,
+ .we = 50,
+ .oe = 50,
+ .wait = 70,
+ },
+ .timing_read = {
+ .pg_rdy = 120,
+ .pg_seq = 25,
+ .mux = 5,
+ .hold = 25,
+ .adv = 50,
+ .ce = 35,
+ .we = 5,
+ .oe = 120,
+ .wait = 5,
+ },
+ },
+};
+
+static struct flash_platform_data p852_flash = {
+ .map_name = "cfi_probe",
+ .width = 2,
+};
+
+static struct tegra_nor_platform p852_nor_data = {
+ .flash = &p852_flash,
+ .chip_parms = nor_chip_parms,
+ .nr_chip_parms = ARRAY_SIZE(nor_chip_parms),
+};
+
+static struct resource resources_nor[] = {
+ [0] = {
+ .start = INT_SNOR,
+ .end = INT_SNOR,
+ .flags = IORESOURCE_IRQ,
+ },
+ [1] = {
+ /* Map the size of flash */
+ .start = 0xd0000000,
+ .end = 0xd0000000 + SZ_64M - 1,
+ .flags = IORESOURCE_MEM,
+ }
+};
+
+struct platform_device p852_nor_device = {
+ .name = "tegra_nor",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(resources_nor),
+ .resource = resources_nor,
+ .dev = {
+ .platform_data = &p852_nor_data,
+ .coherent_dma_mask = 0xffffffff,
+ },
+};
+#endif
+
+#ifdef CONFIG_TEGRA_SPI_I2S
+struct spi_board_info tegra_spi_i2s_device __initdata = {
+ .modalias = "spi_i2s_pcm",
+ .bus_num = 2,
+ .chip_select = 2,
+ .mode = SPI_MODE_0,
+ .max_speed_hz = 18000000,
+ .platform_data = NULL,
+ .irq = 0,
+};
+
+void __init p852_spi_i2s_init(void)
+{
+ struct tegra_spi_i2s_platform_data *pdata;
+
+ pdata = (struct tegra_spi_i2s_platform_data *)
+ tegra_spi_i2s_device.platform_data;
+ if (pdata->gpio_i2s.active_state) {
+ gpio_request_one(pdata->gpio_i2s.gpio_no, GPIOF_OUT_INIT_LOW,
+ "i2s_cpld_dir1");
+ } else {
+ gpio_request_one(pdata->gpio_i2s.gpio_no, GPIOF_OUT_INIT_HIGH,
+ "i2s_cpld_dir1");
+ }
+ tegra_gpio_enable(pdata->gpio_i2s.gpio_no);
+ if (pdata->gpio_spi.active_state) {
+ gpio_request_one(pdata->gpio_spi.gpio_no, GPIOF_OUT_INIT_LOW,
+ "spi_cpld_dir2");
+ } else {
+ gpio_request_one(pdata->gpio_spi.gpio_no, GPIOF_OUT_INIT_HIGH,
+ "spi_cpld_dir2");
+ }
+
+ tegra_gpio_enable(pdata->gpio_spi.gpio_no);
+ spi_register_board_info(&tegra_spi_i2s_device, 1);
+}
+#endif
+
+#if defined(CONFIG_SPI_TEGRA) && defined(CONFIG_SPI_SPIDEV)
+static struct spi_board_info tegra_spi_devices[] __initdata = {
+ {
+ .modalias = "spidev",
+ .bus_num = 0,
+ .chip_select = 0,
+ .mode = SPI_MODE_0,
+ .max_speed_hz = 18000000,
+ .platform_data = NULL,
+ .irq = 0,
+ },
+ {
+ .modalias = "spidev",
+ .bus_num = 1,
+ .chip_select = 1,
+ .mode = SPI_MODE_0,
+ .max_speed_hz = 18000000,
+ .platform_data = NULL,
+ .irq = 0,
+ },
+ {
+ .modalias = "spidev",
+ .bus_num = 3,
+ .chip_select = 1,
+ .mode = SPI_MODE_0,
+ .max_speed_hz = 18000000,
+ .platform_data = NULL,
+ .irq = 0,
+ },
+};
+
+static void __init p852_register_spidev(void)
+{
+ spi_register_board_info(tegra_spi_devices,
+ ARRAY_SIZE(tegra_spi_devices));
+}
+#else
+#define p852_register_spidev() do {} while (0)
+#endif
+
+static void __init p852_usb_init(void)
+{
+
+ p852_usb_gpio_config();
+ /*
+ if (system_rev == P852_SKU8)
+ {
+ platform_device_register(&tegra_udc_device);
+ }
+ else
+ */
+ {
+ tegra_ehci1_device.dev.platform_data = &tegra_ehci_pdata[0];
+ platform_device_register(&tegra_ehci1_device);
+ }
+
+ if (!(p852_sku_peripherals & P852_SKU_ULPI_DISABLE)) {
+ tegra_ehci2_device.dev.platform_data = &tegra_ehci_pdata[1];
+ platform_device_register(&tegra_ehci2_device);
+ }
+
+ tegra_ehci3_device.dev.platform_data = &tegra_ehci_pdata[2];
+ platform_device_register(&tegra_ehci3_device);
+}
+
+static void __init spi3_pingroup_clear_tristate(void)
+{
+ /* spi3 mosi, miso, cs, clk */
+ tegra_pinmux_set_tristate(TEGRA_PINGROUP_LSDI, TEGRA_TRI_NORMAL);
+ tegra_pinmux_set_tristate(TEGRA_PINGROUP_LSDA, TEGRA_TRI_NORMAL);
+ tegra_pinmux_set_tristate(TEGRA_PINGROUP_LCSN, TEGRA_TRI_NORMAL);
+ tegra_pinmux_set_tristate(TEGRA_PINGROUP_LSCK, TEGRA_TRI_NORMAL);
+}
+
+static void __init p852_spi_init(void)
+{
+ if (p852_sku_peripherals & P852_SKU_SPI_ENABLE) {
+ int i = 0;
+ unsigned int spi_config = 0;
+ unsigned int spi3_config =
+ (p852_spi_peripherals >> P852_SPI3_SHIFT) & P852_SPI_MASK;
+
+ for (i = 0; i < P852_MAX_SPI; i++) {
+ spi_config =
+ (p852_spi_peripherals >> (P852_SPI_SHIFT * i)) &
+ P852_SPI_MASK;
+ if (spi_config & P852_SPI_ENABLE) {
+ if (spi_config & P852_SPI_SLAVE)
+ p852_spi_devices[i]->name =
+ "tegra_spi_slave";
+ platform_device_register(p852_spi_devices[i]);
+ }
+ }
+ /* Default spi3 pingroups are in tristate */
+ if (spi3_config & P852_SPI_ENABLE)
+ spi3_pingroup_clear_tristate();
+ }
+}
+
+static void __init p852_uart_init(void)
+{
+ if (p852_sku_peripherals & P852_SKU_UART_ENABLE) {
+ int i = 0;
+ unsigned int uart_config = 0, uart8250Id = 0;
+ int debug_console = -1;
+
+ /* register the debug console as the first serial console */
+ for (i = 0; i < P852_MAX_UART; i++) {
+ uart_config =
+ (p852_uart_peripherals >> (P852_UART_SHIFT * i));
+ if (uart_config & P852_UART_DB) {
+ debug_console = i;
+ debug_uart_platform_data[0].membase =
+ IO_ADDRESS(p852_uart_bases[i]);
+ debug_uart_platform_data[0].mapbase =
+ p852_uart_bases[i];
+ debug_uart_platform_data[0].irq =
+ p852_uart_irqs[i];
+ uart8250Id++;
+ platform_device_register(&debug_uart);
+ break;
+ }
+ }
+
+ /* register remaining UARTS */
+ for (i = 0; i < P852_MAX_UART; i++) {
+ uart_config =
+ (p852_uart_peripherals >> (P852_UART_SHIFT * i)) &
+ P852_UART_MASK;
+ if ((uart_config & P852_UART_ENABLE)
+ && i != debug_console) {
+ if (uart_config & P852_UART_HS) {
+ platform_device_register
+ (p852_uart_devices[i]);
+ } else {
+ p852_8250_uart_devices[i]->id =
+ uart8250Id++;
+ platform_device_register
+ (p852_8250_uart_devices[i]);
+ }
+ }
+ }
+ }
+}
+
+static struct platform_device generic_codec_driver = {
+ .name = "generic-dit",
+};
+
+static void __init p852_flash_init(void)
+{
+ if (p852_sku_peripherals & P852_SKU_NAND_ENABLE)
+ platform_device_register(&p852_nand_device);
+#ifdef CONFIG_MTD_NOR_TEGRA
+ if (p852_sku_peripherals & P852_SKU_NOR_ENABLE)
+ platform_device_register(&p852_nor_device);
+#endif
+}
+
+void __init p852_common_init(void)
+{
+ tegra_clk_init_from_table(p852_clk_init_table);
+
+ p852_pinmux_init();
+
+ p852_i2c_init();
+
+ p852_regulator_init();
+
+ p852_uart_init();
+
+ p852_flash_init();
+
+ platform_add_devices(p852_devices, ARRAY_SIZE(p852_devices));
+
+ //p852_panel_init();
+
+ p852_spi_init();
+
+ p852_register_spidev();
+
+ p852_usb_init();
+
+ p852_sdhci_init();
+
+ p852_gpio_init();
+
+ p852_power_off_init();
+}
+
+void __init tegra_p852_init(void)
+{
+ switch (system_rev) {
+ case P852_SKU3:
+ p852_sku3_init();
+ break;
+ case P852_SKU13:
+ p852_sku13_init();
+ break;
+ case P852_SKU13_B00:
+ case P852_SKU13_C01:
+ p852_sku13_b00_init();
+ break;
+ case P852_SKU23:
+ p852_sku23_init();
+ break;
+ case P852_SKU23_B00:
+ p852_sku23_b00_init();
+ break;
+ case P852_SKU23_C01:
+ p852_sku23_c01_init();
+ break;
+ case P852_SKU1:
+ p852_sku1_init();
+ break;
+ case P852_SKU11:
+ case P852_SKU1_B00:
+ p852_sku1_b00_init();
+ break;
+ case P852_SKU1_C0X:
+ p852_sku1_c0x_init();
+ break;
+ case P852_SKU5_B00:
+ p852_sku5_b00_init();
+ break;
+ case P852_SKU5_C01:
+ p852_sku5_c01_init();
+ break;
+ case P852_SKU8_B00:
+ p852_sku8_b00_init();
+ break;
+ case P852_SKU8_C01:
+ p852_sku8_c00_init();
+ break;
+ case P852_SKU9_B00:
+ p852_sku9_b00_init();
+ break;
+ case P852_SKU9_C01:
+ p852_sku9_c00_init();
+ break;
+ default:
+ printk(KERN_ERR "Unknow Board Revision\n");
+ break;
+ }
+}
+
+static void __init tegra_p852_reserve(void)
+{
+ switch (system_rev) {
+ case P852_SKU3:
+ case P852_SKU5_B00:
+ case P852_SKU5_C01:
+ case P852_SKU9_B00:
+ case P852_SKU9_C01:
+ tegra_reserve(SZ_64M + SZ_16M, SZ_8M, 0);
+ break;
+ default:
+ tegra_reserve(SZ_128M, SZ_8M, 0);
+ break;
+ }
+}
+
+MACHINE_START(P852, "Tegra P852")
+ .boot_params = 0x00000100,
+ .map_io = tegra_map_common_io,
+ .reserve = tegra_p852_reserve,
+ .init_early = tegra_init_early,
+ .init_irq = tegra_init_irq,
+ .timer = &tegra_timer,
+ .init_machine = tegra_p852_init,
+MACHINE_END
diff --git a/arch/arm/mach-tegra/p852/board-p852.h b/arch/arm/mach-tegra/p852/board-p852.h
new file mode 100644
index 000000000000..a17f4faf0564
--- /dev/null
+++ b/arch/arm/mach-tegra/p852/board-p852.h
@@ -0,0 +1,300 @@
+/*
+ * arch/arm/mach-tegra/board-p852.h
+ *
+ * Copyright (c) 2010-2011, NVIDIA Corporation.
+ *
+ * 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 _MACH_TEGRA_BOARD_P852M_H
+#define _MACH_TEGRA_BOARD_P852M_H
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/serial_8250.h>
+#include <linux/clk.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/dma-mapping.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/spi/spi.h>
+#include <linux/i2c-tegra.h>
+#include <linux/platform_data/tegra_usb.h>
+#include <linux/gpio.h>
+
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/time.h>
+#include <asm/setup.h>
+#include <asm/mach-types.h>
+#include <asm/mach/flash.h>
+
+#include <mach/sdhci.h>
+#include <mach/iomap.h>
+#include <mach/irqs.h>
+#include <mach/nand.h>
+#include <mach/usb_phy.h>
+#include <mach/clk.h>
+#include <mach/i2s.h>
+#include <mach/audio.h>
+
+#include "../clock.h"
+#include "../board.h"
+#include "../pm.h"
+#include "../devices.h"
+#include "../gpio-names.h"
+#include "../wakeups-t2.h"
+
+
+#define P852_SKU3 0x030000UL
+#define P852_SKU13 0x130000UL
+#define P852_SKU13_B00 0x130200UL
+#define P852_SKU13_C01 0x130401UL
+#define P852_SKU23 0x230000UL
+#define P852_SKU23_B00 0x230200UL
+#define P852_SKU23_C01 0x230401UL
+#define P852_SKU1 0x010000UL
+#define P852_SKU1_B00 0x010200UL
+#define P852_SKU1_C0X 0x010400UL
+#define P852_SKU11 0x110000UL
+#define P852_SKU5_B00 0x040200UL
+#define P852_SKU5_C01 0x050401UL
+#define P852_SKU8_B00 0x080200UL
+#define P852_SKU8_C01 0x080401UL
+#define P852_SKU9_B00 0x090200UL
+#define P852_SKU9_C01 0x090401UL
+
+int p852_regulator_init(void);
+int p852_panel_init(void);
+void p852_sdhci_init(void);
+void p852_i2c_init(void);
+void p852_i2c_set_default_clock(int adapter, unsigned long clock);
+void p852_pinmux_init(void);
+void p852_gpio_init(void);
+void p852_power_off_init(void);
+
+void p852_sku1_init(void);
+void p852_sku1_b00_init(void);
+void p852_sku1_c0x_init(void);
+void p852_sku3_init(void);
+void p852_sku5_b00_init(void);
+void p852_sku5_c01_init(void);
+void p852_sku8_b00_init(void);
+void p852_sku8_c00_init(void);
+void p852_sku9_b00_init(void);
+void p852_sku9_c00_init(void);
+void p852_sku13_init(void);
+void p852_sku13_b00_init(void);
+void p852_sku23_init(void);
+void p852_sku23_b00_init(void);
+void p852_sku23_c01_init(void);
+
+#ifndef CONFIG_P852_SKU1
+void p852_sku1_init(void);
+#endif
+#ifndef CONFIG_P852_SKU1_B00
+void p852_sku1_b00_init(void);
+#endif
+#ifndef CONFIG_P852_SKU1_C0x
+void p852_sku1_c0x_init(void);
+#endif
+#ifndef CONFIG_P852_SKU3
+void p852_sku3_init(void);
+#endif
+#ifndef CONFIG_P852_SKU5_B00
+void p852_sku5_b00_init(void){};
+#endif
+#ifndef CONFIG_P852_SKU5_C01
+void p852_sku5_c01_init(void){};
+#endif
+#ifndef CONFIG_P852_SKU8_B00
+void p852_sku8_b00_init(void){};
+#endif
+#ifndef CONFIG_P852_SKU8_C01
+void p852_sku8_c00_init(void){};
+#endif
+#ifndef CONFIG_P852_SKU9_B00
+void p852_sku9_b00_init(void){};
+#endif
+#ifndef CONFIG_P852_SKU9_C01
+void p852_sku9_c00_init(void){};
+#endif
+#ifndef CONFIG_P852_SKU13
+void p852_sku13_init(void){};
+#endif
+#ifndef CONFIG_P852_SKU13_B00
+void p852_sku13_b00_init(void){};
+#endif
+#ifndef CONFIG_P852_SKU23
+void p852_sku23_init(void){};
+#endif
+#ifndef CONFIG_P852_SKU23_B00
+void p852_sku23_b00_init(void){};
+#endif
+#ifndef CONFIG_P852_SKU23_C01
+void p852_sku23_c01_init(void){};
+#endif
+
+extern unsigned int system_rev;
+extern unsigned int p852_sku_peripherals;
+extern unsigned int p852_spi_peripherals;
+extern unsigned int p852_i2s_peripherals;
+extern unsigned int p852_uart_peripherals;
+extern unsigned int p852_sdhci_peripherals;
+extern unsigned int p852_display_peripherals;
+extern unsigned int p852_i2c_peripherals;
+extern struct tegra_sdhci_platform_data p852_sdhci_platform_data[];
+extern struct platform_device tegra_8250_uarta_device;
+extern struct platform_device tegra_8250_uartb_device;
+extern struct platform_device tegra_8250_uartc_device;
+extern struct platform_device tegra_8250_uartd_device;
+extern struct platform_device tegra_8250_uarte_device;
+
+#ifdef CONFIG_TEGRA_SPI_I2S
+extern void p852_spi_i2s_init(void);
+extern struct spi_board_info tegra_spi_i2s_device;
+#endif
+
+void tegra_p852_fixup(struct machine_desc *desc,
+ struct tag *tags, char **cmdline, struct meminfo *mi);
+
+void p852_common_init(void);
+
+#define P852_SDIO3_PINMUX_ENABLE 0x01
+
+#define P852_SKU_SPI_SHIFT 0x00
+#define P852_SKU_SPI_ENABLE (1 << P852_SKU_SPI_SHIFT)
+#define P852_SKU_SPI_MASK (1 << P852_SKU_SPI_SHIFT)
+
+#define P852_SKU_I2S_SHIFT 0x01
+#define P852_SKU_I2S_ENABLE (1 << P852_SKU_I2S_SHIFT)
+#define P852_SKU_I2S_MASK (1 << P852_SKU_I2S_SHIFT)
+
+#define P852_SKU_SDHCI_SHIFT 0x02
+#define P852_SKU_SDHCI_ENABLE (1 << P852_SKU_SDHCI_SHIFT)
+#define P852_SKU_SDHCI_MASK (1 << P852_SKU_SDHCI_SHIFT)
+
+#define P852_SKU_UART_SHIFT 0x03
+#define P852_SKU_UART_ENABLE (1 << P852_SKU_UART_SHIFT)
+#define P852_SKU_UART_MASK (1 << P852_SKU_UART_SHIFT)
+
+#define P852_SKU_NAND_SHIFT 0x04
+#define P852_SKU_NAND_ENABLE (1 << P852_SKU_NAND_SHIFT)
+#define P852_SKU_NAND_MASK (1 << P852_SKU_NAND_SHIFT)
+
+#define P852_SKU_NOR_SHIFT 0x05
+#define P852_SKU_NOR_ENABLE (1 << P852_SKU_NOR_SHIFT)
+#define P852_SKU_NOR_MASK (1 << P852_SKU_NOR_SHIFT)
+
+#define P852_SKU_DISPLAY_SHIFT 0x06
+#define P852_SKU_DISPLAY_ENABLE (1 << P852_SKU_DISPLAY_SHIFT)
+#define P852_SKU_DISPLAY_MASK (1 << P852_SKU_DISPLAY_SHIFT)
+
+#define P852_SKU_ULPI_SHIFT 0x07
+#define P852_SKU_ULPI_DISABLE (1 << P852_SKU_ULPI_SHIFT)
+
+#define P852_SKU_I2C_SHIFT 0x08
+#define P852_SKU_I2C_ENABLE (1 << P852_SKU_I2C_SHIFT)
+#define P852_SKU_I2C_MASK (1 << P852_SKU_I2C_SHIFT)
+
+#define P852_MAX_DISP 0x2
+#define P852_DISP_SHIFT 0x16
+#define P852_DISPA_SHIFT 0x0
+#define P852_DISPB_SHIFT 0x16
+
+#define P852_DISP_MASK 0x1
+#define P852_DISP_ENABLE 0x1
+#define P852_DISPA_MASK (P852_DISP_MASK << P852_DISPA_SHIFT)
+#define P852_DISPB_MASK (P852_DISP_MASK << P852_DISPB_SHIFT)
+
+#define P852_MAX_SPI 0x04
+#define P852_SPI_SHIFT 0x03
+#define P852_SPI1_SHIFT 0x00
+#define P852_SPI2_SHIFT 0x03
+#define P852_SPI3_SHIFT 0x06
+#define P852_SPI4_SHIFT 0x09
+
+#define P852_SPI_MASK 0x07
+#define P852_SPI1_MASK (P852_SPI_MASK << P852_SPI1_SHIFT)
+#define P852_SPI2_MASK (P852_SPI_MASK << P852_SPI2_SHIFT)
+#define P852_SPI3_MASK (P852_SPI_MASK << P852_SPI3_SHIFT)
+#define P852_SPI4_MASK (P852_SPI_MASK << P852_SPI4_SHIFT)
+
+#define P852_SPI_ENABLE 0x01
+#define P852_SPI_MASTER 0x02
+#define P852_SPI_SLAVE 0x04
+
+#define P852_I2S_SHIFT 0x05
+#define P852_I2S1_SHIFT 0x00
+#define P852_I2S2_SHIFT 0x05
+
+#define P852_I2S_MASK 0x1F
+#define P852_I2S1_MASK (P852_I2S_MASK << P852_I2S1_SHIFT)
+#define P852_I2S2_MASK (P852_I2S_MASK << P852_I2S2_SHIFT)
+
+#define P852_I2S_ENABLE 0x10
+#define P852_I2S_TDM 0x08
+#define P852_MAX_SDHCI 0x04
+#define P852_SDHCI_SHIFT 0x04
+#define P852_SDHCI1_SHIFT 0x00
+#define P852_SDHCI2_SHIFT 0x04
+#define P852_SDHCI3_SHIFT 0x08
+#define P852_SDHCI4_SHIFT 0x0C
+
+#define P852_SDHCI_MASK 0x0F
+#define P852_SDHCI1_MASK (P852_SDHCI_MASK << P852_SDHCI1_SHIFT)
+#define P852_SDHCI2_MASK (P852_SDHCI_MASK << P852_SDHCI2_SHIFT)
+#define P852_SDHCI3_MASK (P852_SDHCI_MASK << P852_SDHCI3_SHIFT)
+#define P852_SDHCI4_MASK (P852_SDHCI_MASK << P852_SDHCI4_SHIFT)
+
+#define P852_SDHCI_ENABLE 0x01
+#define P852_SDHCI_CD_EN 0x02
+#define P852_SDHCI_WP_EN 0x04
+#define P852_SDHCI_PW_EN 0x08
+
+#define P852_UART_SHIFT 0x04
+#define P852_UARTA_SHIFT 0x00
+#define P852_UARTB_SHIFT 0x04
+#define P852_UARTC_SHIFT 0x08
+#define P852_UARTD_SHIFT 0x0C
+
+#define P852_UART_MASK 0x0F
+#define P852_UARTA_MASK (P852_UART_MASK << P852_UARTA_SHIFT)
+#define P852_UARTB_MASK (P852_UART_MASK << P852_UARTB_SHIFT)
+#define P852_UARTC_MASK (P852_UART_MASK << P852_UARTC_SHIFT)
+#define P852_UARTD_MASK (P852_UART_MASK << P852_UARTD_SHIFT)
+
+#define P852_MAX_UART 0x4
+#define P852_UART_ALT_PIN_CFG 0x8
+#define P852_UART_ENABLE 0x4
+#define P852_UART_DB 0x1
+#define P852_UART_HS 0x2
+
+#define P852_MAX_I2C 0x4
+#define P852_I2C_SHIFT 0x01
+#define P852_I2C1_SHIFT 0x00
+#define P852_I2C2_SHIFT 0x01
+#define P852_I2C3_SHIFT 0x02
+#define P852_I2C4_SHIFT 0x03
+
+
+#define P852_I2C_MASK 0x01
+#define P852_I2C1_MASK (P852_I2C_MASK << P852_I2C1_SHIFT)
+#define P852_I2C2_MASK (P852_I2C_MASK << P852_I2C2_SHIFT)
+#define P852_I2C3_MASK (P852_I2C_MASK << P852_I2C3_SHIFT)
+#define P852_I2C4_MASK (P852_I2C_MASK << P852_I2C4_SHIFT)
+
+#define P852_I2C_ENABLE 0x01
+
+#endif
diff --git a/arch/arm/mach-tegra/pcie.c b/arch/arm/mach-tegra/pcie.c
index f1f699d86c32..6665a3d8ed34 100644
--- a/arch/arm/mach-tegra/pcie.c
+++ b/arch/arm/mach-tegra/pcie.c
@@ -689,7 +689,7 @@ static void tegra_pcie_power_off(void)
tegra_periph_reset_assert(tegra_pcie.afi_clk);
tegra_periph_reset_assert(tegra_pcie.pex_clk);
- tegra_powergate_power_off(TEGRA_POWERGATE_PCIE);
+ tegra_powergate_partition(TEGRA_POWERGATE_PCIE);
tegra_pcie_xclk_clamp(true);
}
@@ -704,8 +704,7 @@ static int tegra_pcie_power_regate(void)
tegra_periph_reset_assert(tegra_pcie.pcie_xclk);
tegra_periph_reset_assert(tegra_pcie.afi_clk);
- err = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_PCIE,
- tegra_pcie.pex_clk);
+ err = tegra_unpowergate_partition(TEGRA_POWERGATE_PCIE);
if (err) {
pr_err("PCIE: powerup sequence failed: %d\n", err);
return err;
diff --git a/arch/arm/mach-tegra/pinmux-t2-tables.c b/arch/arm/mach-tegra/pinmux-t2-tables.c
index a475367befa3..3a39f45cb57e 100644
--- a/arch/arm/mach-tegra/pinmux-t2-tables.c
+++ b/arch/arm/mach-tegra/pinmux-t2-tables.c
@@ -26,59 +26,83 @@
#include <linux/io.h>
#include <linux/init.h>
#include <linux/string.h>
+#include <linux/syscore_ops.h>
#include <mach/iomap.h>
#include <mach/pinmux.h>
-#include <mach/suspend.h>
+#include "gpio-names.h"
-#define DRIVE_PINGROUP(pg_name, r) \
- [TEGRA_DRIVE_PINGROUP_ ## pg_name] = { \
- .name = #pg_name, \
- .reg = r \
+#define SET_DRIVE_PINGROUP(pg_name, r, drv_down_offset, drv_down_mask, drv_up_offset, drv_up_mask, \
+ slew_rise_offset, slew_rise_mask, slew_fall_offset, slew_fall_mask) \
+ [TEGRA_DRIVE_PINGROUP_ ## pg_name] = { \
+ .name = #pg_name, \
+ .reg = r, \
+ .drvup_offset = drv_up_offset, \
+ .drvup_mask = drv_up_mask, \
+ .drvdown_offset = drv_down_offset, \
+ .drvdown_mask = drv_down_mask, \
+ .slewrise_offset = slew_rise_offset, \
+ .slewrise_mask = slew_rise_mask, \
+ .slewfall_offset = slew_fall_offset, \
+ .slewfall_mask = slew_fall_mask, \
+ }
+
+#define DEFAULT_DRIVE_PINGROUP(pg_name, r) \
+ [TEGRA_DRIVE_PINGROUP_ ## pg_name] = { \
+ .name = #pg_name, \
+ .reg = r, \
+ .drvup_offset = 20, \
+ .drvup_mask = 0x1f, \
+ .drvdown_offset = 12, \
+ .drvdown_mask = 0x1f, \
+ .slewrise_offset = 28, \
+ .slewrise_mask = 0x3, \
+ .slewfall_offset = 30, \
+ .slewfall_mask = 0x3, \
}
const struct tegra_drive_pingroup_desc tegra_soc_drive_pingroups[TEGRA_MAX_DRIVE_PINGROUP] = {
- DRIVE_PINGROUP(AO1, 0x868),
- DRIVE_PINGROUP(AO2, 0x86c),
- DRIVE_PINGROUP(AT1, 0x870),
- DRIVE_PINGROUP(AT2, 0x874),
- DRIVE_PINGROUP(CDEV1, 0x878),
- DRIVE_PINGROUP(CDEV2, 0x87c),
- DRIVE_PINGROUP(CSUS, 0x880),
- DRIVE_PINGROUP(DAP1, 0x884),
- DRIVE_PINGROUP(DAP2, 0x888),
- DRIVE_PINGROUP(DAP3, 0x88c),
- DRIVE_PINGROUP(DAP4, 0x890),
- DRIVE_PINGROUP(DBG, 0x894),
- DRIVE_PINGROUP(LCD1, 0x898),
- DRIVE_PINGROUP(LCD2, 0x89c),
- DRIVE_PINGROUP(SDMMC2, 0x8a0),
- DRIVE_PINGROUP(SDMMC3, 0x8a4),
- DRIVE_PINGROUP(SPI, 0x8a8),
- DRIVE_PINGROUP(UAA, 0x8ac),
- DRIVE_PINGROUP(UAB, 0x8b0),
- DRIVE_PINGROUP(UART2, 0x8b4),
- DRIVE_PINGROUP(UART3, 0x8b8),
- DRIVE_PINGROUP(VI1, 0x8bc),
- DRIVE_PINGROUP(VI2, 0x8c0),
- DRIVE_PINGROUP(XM2A, 0x8c4),
- DRIVE_PINGROUP(XM2C, 0x8c8),
- DRIVE_PINGROUP(XM2D, 0x8cc),
- DRIVE_PINGROUP(XM2CLK, 0x8d0),
- DRIVE_PINGROUP(MEMCOMP, 0x8d4),
- DRIVE_PINGROUP(SDIO1, 0x8e0),
- DRIVE_PINGROUP(CRT, 0x8ec),
- DRIVE_PINGROUP(DDC, 0x8f0),
- DRIVE_PINGROUP(GMA, 0x8f4),
- DRIVE_PINGROUP(GMB, 0x8f8),
- DRIVE_PINGROUP(GMC, 0x8fc),
- DRIVE_PINGROUP(GMD, 0x900),
- DRIVE_PINGROUP(GME, 0x904),
- DRIVE_PINGROUP(OWR, 0x908),
- DRIVE_PINGROUP(UAD, 0x90c),
+ DEFAULT_DRIVE_PINGROUP(AO1, 0x868),
+ DEFAULT_DRIVE_PINGROUP(AO2, 0x86c),
+ DEFAULT_DRIVE_PINGROUP(AT1, 0x870),
+ DEFAULT_DRIVE_PINGROUP(AT2, 0x874),
+ DEFAULT_DRIVE_PINGROUP(CDEV1, 0x878),
+ DEFAULT_DRIVE_PINGROUP(CDEV2, 0x87c),
+ DEFAULT_DRIVE_PINGROUP(CSUS, 0x880),
+ DEFAULT_DRIVE_PINGROUP(DAP1, 0x884),
+ DEFAULT_DRIVE_PINGROUP(DAP2, 0x888),
+ DEFAULT_DRIVE_PINGROUP(DAP3, 0x88c),
+ DEFAULT_DRIVE_PINGROUP(DAP4, 0x890),
+ DEFAULT_DRIVE_PINGROUP(DBG, 0x894),
+ DEFAULT_DRIVE_PINGROUP(LCD1, 0x898),
+ DEFAULT_DRIVE_PINGROUP(LCD2, 0x89c),
+ DEFAULT_DRIVE_PINGROUP(SDMMC2, 0x8a0),
+ DEFAULT_DRIVE_PINGROUP(SDMMC3, 0x8a4),
+ DEFAULT_DRIVE_PINGROUP(SPI, 0x8a8),
+ DEFAULT_DRIVE_PINGROUP(UAA, 0x8ac),
+ DEFAULT_DRIVE_PINGROUP(UAB, 0x8b0),
+ DEFAULT_DRIVE_PINGROUP(UART2, 0x8b4),
+ DEFAULT_DRIVE_PINGROUP(UART3, 0x8b8),
+ DEFAULT_DRIVE_PINGROUP(VI1, 0x8bc),
+ DEFAULT_DRIVE_PINGROUP(VI2, 0x8c0),
+ DEFAULT_DRIVE_PINGROUP(XM2A, 0x8c4),
+ DEFAULT_DRIVE_PINGROUP(XM2C, 0x8c8),
+ DEFAULT_DRIVE_PINGROUP(XM2D, 0x8cc),
+ DEFAULT_DRIVE_PINGROUP(XM2CLK, 0x8d0),
+ DEFAULT_DRIVE_PINGROUP(MEMCOMP, 0x8d4),
+ DEFAULT_DRIVE_PINGROUP(SDIO1, 0x8e0),
+ DEFAULT_DRIVE_PINGROUP(CRT, 0x8ec),
+ DEFAULT_DRIVE_PINGROUP(DDC, 0x8f0),
+ DEFAULT_DRIVE_PINGROUP(GMA, 0x8f4),
+ DEFAULT_DRIVE_PINGROUP(GMB, 0x8f8),
+ DEFAULT_DRIVE_PINGROUP(GMC, 0x8fc),
+ DEFAULT_DRIVE_PINGROUP(GMD, 0x900),
+ DEFAULT_DRIVE_PINGROUP(GME, 0x904),
+ DEFAULT_DRIVE_PINGROUP(OWR, 0x908),
+ DEFAULT_DRIVE_PINGROUP(UAD, 0x90c),
};
-#define PINGROUP(pg_name, vdd, f0, f1, f2, f3, f_safe, \
+#define PINGROUP(pg_name, gpio_nr, vdd, f0, f1, f2, f3, f_safe, \
tri_r, tri_b, mux_r, mux_b, pupd_r, pupd_b) \
[TEGRA_PINGROUP_ ## pg_name] = { \
.name = #pg_name, \
@@ -88,7 +112,8 @@ const struct tegra_drive_pingroup_desc tegra_soc_drive_pingroups[TEGRA_MAX_DRIVE
TEGRA_MUX_ ## f1, \
TEGRA_MUX_ ## f2, \
TEGRA_MUX_ ## f3, \
- }, \
+ }, \
+ .gpionr = TEGRA_GPIO_ ## gpio_nr, \
.func_safe = TEGRA_MUX_ ## f_safe, \
.tri_reg = tri_r, \
.tri_bit = tri_b, \
@@ -96,129 +121,148 @@ const struct tegra_drive_pingroup_desc tegra_soc_drive_pingroups[TEGRA_MAX_DRIVE
.mux_bit = mux_b, \
.pupd_reg = pupd_r, \
.pupd_bit = pupd_b, \
- }
+ .io_default = 0, \
+ .od_bit = -1, \
+ .lock_bit = -1, \
+ .ioreset_bit = -1, \
+}
+
+#define PINGROUPS \
+ /* pg_name,gpio_nr, vdd, f0, f1, f2, f3, f_safe, tri_r, tri_b, mux_r, mux_b, pupd_r, pupd_b*/\
+ PINGROUP(ATA, PI3, NAND, IDE, NAND, GMI, RSVD, IDE, 0x14, 0, 0x80, 24, 0xA0, 0),\
+ PINGROUP(ATB, PI2, NAND, IDE, NAND, GMI, SDIO4, IDE, 0x14, 1, 0x80, 16, 0xA0, 2),\
+ PINGROUP(ATC, PI5, NAND, IDE, NAND, GMI, SDIO4, IDE, 0x14, 2, 0x80, 22, 0xA0, 4),\
+ PINGROUP(ATD, PH0, NAND, IDE, NAND, GMI, SDIO4, IDE, 0x14, 3, 0x80, 20, 0xA0, 6),\
+ PINGROUP(ATE, PH4, NAND, IDE, NAND, GMI, RSVD, IDE, 0x18, 25, 0x80, 12, 0xA0, 8),\
+ PINGROUP(CDEV1, PW4, AUDIO, OSC, PLLA_OUT, PLLM_OUT1, AUDIO_SYNC, OSC, 0x14, 4, 0x88, 2, 0xA8, 0),\
+ PINGROUP(CDEV2, PW5, AUDIO, OSC, AHB_CLK, APB_CLK, PLLP_OUT4, OSC, 0x14, 5, 0x88, 4, 0xA8, 2),\
+ PINGROUP(CRTP, INVALID, LCD, CRT, RSVD, RSVD, RSVD, RSVD, 0x20, 14, 0x98, 20, 0xA4, 24),\
+ PINGROUP(CSUS, PT1, VI, PLLC_OUT1, PLLP_OUT2, PLLP_OUT3, VI_SENSOR_CLK, PLLC_OUT1, 0x14, 6, 0x88, 6, 0xAC, 24),\
+ PINGROUP(DAP1, PN0, AUDIO, DAP1, RSVD, GMI, SDIO2, DAP1, 0x14, 7, 0x88, 20, 0xA0, 10),\
+ PINGROUP(DAP2, PA2, AUDIO, DAP2, TWC, RSVD, GMI, DAP2, 0x14, 8, 0x88, 22, 0xA0, 12),\
+ PINGROUP(DAP3, PP0, BB, DAP3, RSVD, RSVD, RSVD, DAP3, 0x14, 9, 0x88, 24, 0xA0, 14),\
+ PINGROUP(DAP4, PP4, UART, DAP4, RSVD, GMI, RSVD, DAP4, 0x14, 10, 0x88, 26, 0xA0, 16),\
+ PINGROUP(DDC, INVALID, LCD, I2C2, RSVD, RSVD, RSVD, RSVD, 0x18, 31, 0x88, 0, 0xB0, 28),\
+ PINGROUP(DTA, PT4, VI, RSVD, SDIO2, VI, RSVD, RSVD4, 0x14, 11, 0x84, 20, 0xA0, 18),\
+ PINGROUP(DTB, PT2, VI, RSVD, RSVD, VI, SPI1, RSVD1, 0x14, 12, 0x84, 22, 0xA0, 20),\
+ PINGROUP(DTC, PD6, VI, RSVD, RSVD, VI, RSVD, RSVD1, 0x14, 13, 0x84, 26, 0xA0, 22),\
+ PINGROUP(DTD, PT0, VI, RSVD, SDIO2, VI, RSVD, RSVD1, 0x14, 14, 0x84, 28, 0xA0, 24),\
+ PINGROUP(DTE, PBB1, VI, RSVD, RSVD, VI, SPI1, RSVD1, 0x14, 15, 0x84, 30, 0xA0, 26),\
+ PINGROUP(DTF, PBB2, VI, I2C3, RSVD, VI, RSVD, RSVD4, 0x20, 12, 0x98, 30, 0xA0, 28),\
+ PINGROUP(GMA, PAA0, NAND, UARTE, SPI3, GMI, SDIO4, SPI3, 0x14, 28, 0x84, 0, 0xB0, 20),\
+ PINGROUP(GMB, PC7, NAND, IDE, NAND, GMI, GMI_INT, GMI, 0x18, 29, 0x88, 28, 0xB0, 22),\
+ PINGROUP(GMC, PJ7, NAND, UARTD, SPI4, GMI, SFLASH, SPI4, 0x14, 29, 0x84, 2, 0xB0, 24),\
+ PINGROUP(GMD, PJ0, NAND, RSVD, NAND, GMI, SFLASH, GMI, 0x18, 30, 0x88, 30, 0xB0, 26),\
+ PINGROUP(GME, PAA4, NAND, RSVD, DAP5, GMI, SDIO4, GMI, 0x18, 0, 0x8C, 0, 0xA8, 24),\
+ PINGROUP(GPU, PU0, UART, PWM, UARTA, GMI, RSVD, RSVD4, 0x14, 16, 0x8C, 4, 0xA4, 20),\
+ PINGROUP(GPU7, PU7, SYS, RTCK, RSVD, RSVD, RSVD, RTCK, 0x20, 11, 0x98, 28, 0xA4, 6),\
+ PINGROUP(GPV, PV4, SD, PCIE, RSVD, RSVD, RSVD, PCIE, 0x14, 17, 0x8C, 2, 0xA0, 30),\
+ PINGROUP(HDINT, PN7, LCD, HDMI, RSVD, RSVD, RSVD, HDMI, 0x1C, 23, 0x84, 4, 0xAC, 22),\
+ PINGROUP(I2CP, PZ6, SYS, I2C, RSVD, RSVD, RSVD, RSVD4, 0x14, 18, 0x88, 8, 0xA4, 2),\
+ PINGROUP(IRRX, PJ6, UART, UARTA, UARTB, GMI, SPI4, UARTB, 0x14, 20, 0x88, 18, 0xA8, 22),\
+ PINGROUP(IRTX, PJ5, UART, UARTA, UARTB, GMI, SPI4, UARTB, 0x14, 19, 0x88, 16, 0xA8, 20),\
+ PINGROUP(KBCA, PR0, SYS, KBC, NAND, SDIO2, EMC_TEST0_DLL, KBC, 0x14, 22, 0x88, 10, 0xA4, 8),\
+ PINGROUP(KBCB, PR7, SYS, KBC, NAND, SDIO2, MIO, KBC, 0x14, 21, 0x88, 12, 0xA4, 10),\
+ PINGROUP(KBCC, PQ0, SYS, KBC, NAND, TRACE, EMC_TEST1_DLL, KBC, 0x18, 26, 0x88, 14, 0xA4, 12),\
+ PINGROUP(KBCD, PR3, SYS, KBC, NAND, SDIO2, MIO, KBC, 0x20, 10, 0x98, 26, 0xA4, 14),\
+ PINGROUP(KBCE, PQ7, SYS, KBC, NAND, OWR, RSVD, KBC, 0x14, 26, 0x80, 28, 0xB0, 2),\
+ PINGROUP(KBCF, PQ2, SYS, KBC, NAND, TRACE, MIO, KBC, 0x14, 27, 0x80, 26, 0xB0, 0),\
+ PINGROUP(LCSN, PN4, LCD, DISPLAYA, DISPLAYB, SPI3, RSVD, RSVD4, 0x1C, 31, 0x90, 12, 0xAC, 20),\
+ PINGROUP(LD0, PE0, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x1C, 0, 0x94, 0, 0xAC, 12),\
+ PINGROUP(LD1, PE1, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x1C, 1, 0x94, 2, 0xAC, 12),\
+ PINGROUP(LD10, PF2, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x1C, 10, 0x94, 20, 0xAC, 12),\
+ PINGROUP(LD11, PF3, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x1C, 11, 0x94, 22, 0xAC, 12),\
+ PINGROUP(LD12, PF4, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x1C, 12, 0x94, 24, 0xAC, 12),\
+ PINGROUP(LD13, PF5, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x1C, 13, 0x94, 26, 0xAC, 12),\
+ PINGROUP(LD14, PF6, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x1C, 14, 0x94, 28, 0xAC, 12),\
+ PINGROUP(LD15, PF7, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x1C, 15, 0x94, 30, 0xAC, 12),\
+ PINGROUP(LD16, PM0, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x1C, 16, 0x98, 0, 0xAC, 12),\
+ PINGROUP(LD17, PM1, LCD, DISPLAYA, DISPLAYB, RSVD, RSVD, RSVD4, 0x1C, 17, 0x98, 2, 0xAC, 12),\
+ PINGROUP(LD2, PE2, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x1C, 2, 0x94, 4, 0xAC, 12),\
+ PINGROUP(LD3, PE3, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x1C, 3, 0x94, 6, 0xAC, 12),\
+ PINGROUP(LD4, PE4, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x1C, 4, 0x94, 8, 0xAC, 12),\
+ PINGROUP(LD5, PE5, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x1C, 5, 0x94, 10, 0xAC, 12),\
+ PINGROUP(LD6, PE6, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x1C, 6, 0x94, 12, 0xAC, 12),\
+ PINGROUP(LD7, PE7, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x1C, 7, 0x94, 14, 0xAC, 12),\
+ PINGROUP(LD8, PF0, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x1C, 8, 0x94, 16, 0xAC, 12),\
+ PINGROUP(LD9, PF1, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x1C, 9, 0x94, 18, 0xAC, 12),\
+ PINGROUP(LDC, PN6, LCD, DISPLAYA, DISPLAYB, RSVD, RSVD, RSVD4, 0x1C, 30, 0x90, 14, 0xAC, 20),\
+ PINGROUP(LDI, PM6, LCD, DISPLAYA, DISPLAYB, RSVD, RSVD, RSVD4, 0x20, 6, 0x98, 16, 0xAC, 18),\
+ PINGROUP(LHP0, PM5, LCD, DISPLAYA, DISPLAYB, RSVD, RSVD, RSVD4, 0x1C, 18, 0x98, 10, 0xAC, 16),\
+ PINGROUP(LHP1, PM2, LCD, DISPLAYA, DISPLAYB, RSVD, RSVD, RSVD4, 0x1C, 19, 0x98, 4, 0xAC, 14),\
+ PINGROUP(LHP2, PM3, LCD, DISPLAYA, DISPLAYB, RSVD, RSVD, RSVD4, 0x1C, 20, 0x98, 6, 0xAC, 14),\
+ PINGROUP(LHS, PJ3, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x20, 7, 0x90, 22, 0xAC, 22),\
+ PINGROUP(LM0, PW0, LCD, DISPLAYA, DISPLAYB, SPI3, RSVD, RSVD4, 0x1C, 24, 0x90, 26, 0xAC, 22),\
+ PINGROUP(LM1, PW1, LCD, DISPLAYA, DISPLAYB, RSVD, CRT, RSVD3, 0x1C, 25, 0x90, 28, 0xAC, 22),\
+ PINGROUP(LPP, PM7, LCD, DISPLAYA, DISPLAYB, RSVD, RSVD, RSVD4, 0x20, 8, 0x98, 14, 0xAC, 18),\
+ PINGROUP(LPW0, PB2, LCD, DISPLAYA, DISPLAYB, SPI3, HDMI, DISPLAYA, 0x20, 3, 0x90, 0, 0xAC, 20),\
+ PINGROUP(LPW1, PC1, LCD, DISPLAYA, DISPLAYB, RSVD, RSVD, RSVD4, 0x20, 4, 0x90, 2, 0xAC, 20),\
+ PINGROUP(LPW2, PC6, LCD, DISPLAYA, DISPLAYB, SPI3, HDMI, DISPLAYA, 0x20, 5, 0x90, 4, 0xAC, 20),\
+ PINGROUP(LSC0, PB3, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x1C, 27, 0x90, 18, 0xAC, 22),\
+ PINGROUP(LSC1, PZ3, LCD, DISPLAYA, DISPLAYB, SPI3, HDMI, DISPLAYA, 0x1C, 28, 0x90, 20, 0xAC, 20),\
+ PINGROUP(LSCK, PZ4, LCD, DISPLAYA, DISPLAYB, SPI3, HDMI, DISPLAYA, 0x1C, 29, 0x90, 16, 0xAC, 20),\
+ PINGROUP(LSDA, PN5, LCD, DISPLAYA, DISPLAYB, SPI3, HDMI, DISPLAYA, 0x20, 1, 0x90, 8, 0xAC, 20),\
+ PINGROUP(LSDI, PZ2, LCD, DISPLAYA, DISPLAYB, SPI3, RSVD, DISPLAYA, 0x20, 2, 0x90, 6, 0xAC, 20),\
+ PINGROUP(LSPI, PJ1, LCD, DISPLAYA, DISPLAYB, XIO, HDMI, DISPLAYA, 0x20, 0, 0x90, 10, 0xAC, 22),\
+ PINGROUP(LVP0, PV7, LCD, DISPLAYA, DISPLAYB, RSVD, RSVD, RSVD4, 0x1C, 21, 0x90, 30, 0xAC, 22),\
+ PINGROUP(LVP1, PM4, LCD, DISPLAYA, DISPLAYB, RSVD, RSVD, RSVD4, 0x1C, 22, 0x98, 8, 0xAC, 16),\
+ PINGROUP(LVS, PJ4, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x1C, 26, 0x90, 24, 0xAC, 22),\
+ PINGROUP(OWC, INVALID, SYS, OWR, RSVD, RSVD, RSVD, OWR, 0x14, 31, 0x84, 8, 0xB0, 30),\
+ PINGROUP(PMC, PBB0, SYS, PWR_ON, PWR_INTR, RSVD, RSVD, PWR_ON, 0x14, 23, 0x98, 18, -1, -1),\
+ PINGROUP(PTA, PT5, NAND, I2C2, HDMI, GMI, RSVD, RSVD, 0x14, 24, 0x98, 22, 0xA4, 4),\
+ PINGROUP(RM, PC5, UART, I2C, RSVD, RSVD, RSVD, RSVD4, 0x14, 25, 0x80, 14, 0xA4, 0),\
+ PINGROUP(SDB, PA7, SD, UARTA, PWM, SDIO3, SPI2, PWM, 0x20, 15, 0x8C, 10, -1, -1),\
+ PINGROUP(SDC, PB7, SD, PWM, TWC, SDIO3, SPI3, TWC, 0x18, 1, 0x8C, 12, 0xAC, 28),\
+ PINGROUP(SDD, PA6, SD, UARTA, PWM, SDIO3, SPI3, PWM, 0x18, 2, 0x8C, 14, 0xAC, 30),\
+ PINGROUP(SDIO1, PZ0, BB, SDIO1, RSVD, UARTE, UARTA, RSVD2, 0x14, 30, 0x80, 30, 0xB0, 18),\
+ PINGROUP(SLXA, PD1, SD, PCIE, SPI4, SDIO3, SPI2, PCIE, 0x18, 3, 0x84, 6, 0xA4, 22),\
+ PINGROUP(SLXC, PD3, SD, SPDIF, SPI4, SDIO3, SPI2, SPI4, 0x18, 5, 0x84, 10, 0xA4, 26),\
+ PINGROUP(SLXD, PD4, SD, SPDIF, SPI4, SDIO3, SPI2, SPI4, 0x18, 6, 0x84, 12, 0xA4, 28),\
+ PINGROUP(SLXK, PD0, SD, PCIE, SPI4, SDIO3, SPI2, PCIE, 0x18, 7, 0x84, 14, 0xA4, 30),\
+ PINGROUP(SPDI, PK6, AUDIO, SPDIF, RSVD, I2C, SDIO2, RSVD2, 0x18, 8, 0x8C, 8, 0xA4, 16),\
+ PINGROUP(SPDO, PK5, AUDIO, SPDIF, RSVD, I2C, SDIO2, RSVD2, 0x18, 9, 0x8C, 6, 0xA4, 18),\
+ PINGROUP(SPIA, PX0, AUDIO, SPI1, SPI2, SPI3, GMI, GMI, 0x18, 10, 0x8C, 30, 0xA8, 4),\
+ PINGROUP(SPIB, PX1, AUDIO, SPI1, SPI2, SPI3, GMI, GMI, 0x18, 11, 0x8C, 28, 0xA8, 6),\
+ PINGROUP(SPIC, PX2, AUDIO, SPI1, SPI2, SPI3, GMI, GMI, 0x18, 12, 0x8C, 26, 0xA8, 8),\
+ PINGROUP(SPID, PX4, AUDIO, SPI2, SPI1, SPI2_ALT, GMI, GMI, 0x18, 13, 0x8C, 24, 0xA8, 10),\
+ PINGROUP(SPIE, PX5, AUDIO, SPI2, SPI1, SPI2_ALT, GMI, GMI, 0x18, 14, 0x8C, 22, 0xA8, 12),\
+ PINGROUP(SPIF, PX7, AUDIO, SPI3, SPI1, SPI2, RSVD, RSVD4, 0x18, 15, 0x8C, 20, 0xA8, 14),\
+ PINGROUP(SPIG, PW2, AUDIO, SPI3, SPI2, SPI2_ALT, I2C, SPI2_ALT, 0x18, 16, 0x8C, 18, 0xA8, 16),\
+ PINGROUP(SPIH, PW3, AUDIO, SPI3, SPI2, SPI2_ALT, I2C, SPI2_ALT, 0x18, 17, 0x8C, 16, 0xA8, 18),\
+ PINGROUP(UAA, PO1, BB, SPI3, MIPI_HS, UARTA, ULPI, MIPI_HS, 0x18, 18, 0x80, 0, 0xAC, 0),\
+ PINGROUP(UAB, PO5, BB, SPI2, MIPI_HS, UARTA, ULPI, MIPI_HS, 0x18, 19, 0x80, 2, 0xAC, 2),\
+ PINGROUP(UAC, PV0, BB, OWR, RSVD, RSVD, RSVD, RSVD4, 0x18, 20, 0x80, 4, 0xAC, 4),\
+ PINGROUP(UAD, PC2, UART, IRDA, SPDIF, UARTA, SPI4, SPDIF, 0x18, 21, 0x80, 6, 0xAC, 6),\
+ PINGROUP(UCA, PW6, UART, UARTC, RSVD, GMI, RSVD, RSVD4, 0x18, 22, 0x84, 16, 0xAC, 8),\
+ PINGROUP(UCB, PC0, UART, UARTC, PWM, GMI, RSVD, RSVD4, 0x18, 23, 0x84, 18, 0xAC, 10),\
+ PINGROUP(UDA, PY0, BB, SPI1, RSVD, UARTD, ULPI, RSVD2, 0x20, 13, 0x80, 8, 0xB0, 16),\
+ /* these pin groups only have pullup and pull down control */\
+ PINGROUP(CK32, INVALID, SYS, RSVD, RSVD, RSVD, RSVD, RSVD, -1, -1, -1, -1, 0xB0, 14),\
+ PINGROUP(DDRC, INVALID, DDR, RSVD, RSVD, RSVD, RSVD, RSVD, -1, -1, -1, -1, 0xAC, 26),\
+ PINGROUP(PMCA, INVALID, SYS, RSVD, RSVD, RSVD, RSVD, RSVD, -1, -1, -1, -1, 0xB0, 4),\
+ PINGROUP(PMCB, INVALID, SYS, RSVD, RSVD, RSVD, RSVD, RSVD, -1, -1, -1, -1, 0xB0, 6),\
+ PINGROUP(PMCC, INVALID, SYS, RSVD, RSVD, RSVD, RSVD, RSVD, -1, -1, -1, -1, 0xB0, 8),\
+ PINGROUP(PMCD, INVALID, SYS, RSVD, RSVD, RSVD, RSVD, RSVD, -1, -1, -1, -1, 0xB0, 10),\
+ PINGROUP(PMCE, INVALID, SYS, RSVD, RSVD, RSVD, RSVD, RSVD, -1, -1, -1, -1, 0xB0, 12),\
+ PINGROUP(XM2C, INVALID, DDR, RSVD, RSVD, RSVD, RSVD, RSVD, -1, -1, -1, -1, 0xA8, 30),\
+ PINGROUP(XM2D, INVALID, DDR, RSVD, RSVD, RSVD, RSVD, RSVD, -1, -1, -1, -1, 0xA8, 28),\
+ /* END OF LIST */
const struct tegra_pingroup_desc tegra_soc_pingroups[TEGRA_MAX_PINGROUP] = {
- PINGROUP(ATA, NAND, IDE, NAND, GMI, RSVD, IDE, 0x14, 0, 0x80, 24, 0xA0, 0),
- PINGROUP(ATB, NAND, IDE, NAND, GMI, SDIO4, IDE, 0x14, 1, 0x80, 16, 0xA0, 2),
- PINGROUP(ATC, NAND, IDE, NAND, GMI, SDIO4, IDE, 0x14, 2, 0x80, 22, 0xA0, 4),
- PINGROUP(ATD, NAND, IDE, NAND, GMI, SDIO4, IDE, 0x14, 3, 0x80, 20, 0xA0, 6),
- PINGROUP(ATE, NAND, IDE, NAND, GMI, RSVD, IDE, 0x18, 25, 0x80, 12, 0xA0, 8),
- PINGROUP(CDEV1, AUDIO, OSC, PLLA_OUT, PLLM_OUT1, AUDIO_SYNC, OSC, 0x14, 4, 0x88, 2, 0xA8, 0),
- PINGROUP(CDEV2, AUDIO, OSC, AHB_CLK, APB_CLK, PLLP_OUT4, OSC, 0x14, 5, 0x88, 4, 0xA8, 2),
- PINGROUP(CRTP, LCD, CRT, RSVD, RSVD, RSVD, RSVD, 0x20, 14, 0x98, 20, 0xA4, 24),
- PINGROUP(CSUS, VI, PLLC_OUT1, PLLP_OUT2, PLLP_OUT3, VI_SENSOR_CLK, PLLC_OUT1, 0x14, 6, 0x88, 6, 0xAC, 24),
- PINGROUP(DAP1, AUDIO, DAP1, RSVD, GMI, SDIO2, DAP1, 0x14, 7, 0x88, 20, 0xA0, 10),
- PINGROUP(DAP2, AUDIO, DAP2, TWC, RSVD, GMI, DAP2, 0x14, 8, 0x88, 22, 0xA0, 12),
- PINGROUP(DAP3, BB, DAP3, RSVD, RSVD, RSVD, DAP3, 0x14, 9, 0x88, 24, 0xA0, 14),
- PINGROUP(DAP4, UART, DAP4, RSVD, GMI, RSVD, DAP4, 0x14, 10, 0x88, 26, 0xA0, 16),
- PINGROUP(DDC, LCD, I2C2, RSVD, RSVD, RSVD, RSVD4, 0x18, 31, 0x88, 0, 0xB0, 28),
- PINGROUP(DTA, VI, RSVD, SDIO2, VI, RSVD, RSVD4, 0x14, 11, 0x84, 20, 0xA0, 18),
- PINGROUP(DTB, VI, RSVD, RSVD, VI, SPI1, RSVD1, 0x14, 12, 0x84, 22, 0xA0, 20),
- PINGROUP(DTC, VI, RSVD, RSVD, VI, RSVD, RSVD1, 0x14, 13, 0x84, 26, 0xA0, 22),
- PINGROUP(DTD, VI, RSVD, SDIO2, VI, RSVD, RSVD1, 0x14, 14, 0x84, 28, 0xA0, 24),
- PINGROUP(DTE, VI, RSVD, RSVD, VI, SPI1, RSVD1, 0x14, 15, 0x84, 30, 0xA0, 26),
- PINGROUP(DTF, VI, I2C3, RSVD, VI, RSVD, RSVD4, 0x20, 12, 0x98, 30, 0xA0, 28),
- PINGROUP(GMA, NAND, UARTE, SPI3, GMI, SDIO4, SPI3, 0x14, 28, 0x84, 0, 0xB0, 20),
- PINGROUP(GMB, NAND, IDE, NAND, GMI, GMI_INT, GMI, 0x18, 29, 0x88, 28, 0xB0, 22),
- PINGROUP(GMC, NAND, UARTD, SPI4, GMI, SFLASH, SPI4, 0x14, 29, 0x84, 2, 0xB0, 24),
- PINGROUP(GMD, NAND, RSVD, NAND, GMI, SFLASH, GMI, 0x18, 30, 0x88, 30, 0xB0, 26),
- PINGROUP(GME, NAND, RSVD, DAP5, GMI, SDIO4, GMI, 0x18, 0, 0x8C, 0, 0xA8, 24),
- PINGROUP(GPU, UART, PWM, UARTA, GMI, RSVD, RSVD4, 0x14, 16, 0x8C, 4, 0xA4, 20),
- PINGROUP(GPU7, SYS, RTCK, RSVD, RSVD, RSVD, RTCK, 0x20, 11, 0x98, 28, 0xA4, 6),
- PINGROUP(GPV, SD, PCIE, RSVD, RSVD, RSVD, PCIE, 0x14, 17, 0x8C, 2, 0xA0, 30),
- PINGROUP(HDINT, LCD, HDMI, RSVD, RSVD, RSVD, HDMI, 0x1C, 23, 0x84, 4, 0xAC, 22),
- PINGROUP(I2CP, SYS, I2C, RSVD, RSVD, RSVD, RSVD4, 0x14, 18, 0x88, 8, 0xA4, 2),
- PINGROUP(IRRX, UART, UARTA, UARTB, GMI, SPI4, UARTB, 0x14, 20, 0x88, 18, 0xA8, 22),
- PINGROUP(IRTX, UART, UARTA, UARTB, GMI, SPI4, UARTB, 0x14, 19, 0x88, 16, 0xA8, 20),
- PINGROUP(KBCA, SYS, KBC, NAND, SDIO2, EMC_TEST0_DLL, KBC, 0x14, 22, 0x88, 10, 0xA4, 8),
- PINGROUP(KBCB, SYS, KBC, NAND, SDIO2, MIO, KBC, 0x14, 21, 0x88, 12, 0xA4, 10),
- PINGROUP(KBCC, SYS, KBC, NAND, TRACE, EMC_TEST1_DLL, KBC, 0x18, 26, 0x88, 14, 0xA4, 12),
- PINGROUP(KBCD, SYS, KBC, NAND, SDIO2, MIO, KBC, 0x20, 10, 0x98, 26, 0xA4, 14),
- PINGROUP(KBCE, SYS, KBC, NAND, OWR, RSVD, KBC, 0x14, 26, 0x80, 28, 0xB0, 2),
- PINGROUP(KBCF, SYS, KBC, NAND, TRACE, MIO, KBC, 0x14, 27, 0x80, 26, 0xB0, 0),
- PINGROUP(LCSN, LCD, DISPLAYA, DISPLAYB, SPI3, RSVD, RSVD4, 0x1C, 31, 0x90, 12, 0xAC, 20),
- PINGROUP(LD0, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x1C, 0, 0x94, 0, 0xAC, 12),
- PINGROUP(LD1, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x1C, 1, 0x94, 2, 0xAC, 12),
- PINGROUP(LD10, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x1C, 10, 0x94, 20, 0xAC, 12),
- PINGROUP(LD11, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x1C, 11, 0x94, 22, 0xAC, 12),
- PINGROUP(LD12, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x1C, 12, 0x94, 24, 0xAC, 12),
- PINGROUP(LD13, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x1C, 13, 0x94, 26, 0xAC, 12),
- PINGROUP(LD14, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x1C, 14, 0x94, 28, 0xAC, 12),
- PINGROUP(LD15, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x1C, 15, 0x94, 30, 0xAC, 12),
- PINGROUP(LD16, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x1C, 16, 0x98, 0, 0xAC, 12),
- PINGROUP(LD17, LCD, DISPLAYA, DISPLAYB, RSVD, RSVD, RSVD4, 0x1C, 17, 0x98, 2, 0xAC, 12),
- PINGROUP(LD2, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x1C, 2, 0x94, 4, 0xAC, 12),
- PINGROUP(LD3, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x1C, 3, 0x94, 6, 0xAC, 12),
- PINGROUP(LD4, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x1C, 4, 0x94, 8, 0xAC, 12),
- PINGROUP(LD5, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x1C, 5, 0x94, 10, 0xAC, 12),
- PINGROUP(LD6, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x1C, 6, 0x94, 12, 0xAC, 12),
- PINGROUP(LD7, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x1C, 7, 0x94, 14, 0xAC, 12),
- PINGROUP(LD8, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x1C, 8, 0x94, 16, 0xAC, 12),
- PINGROUP(LD9, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x1C, 9, 0x94, 18, 0xAC, 12),
- PINGROUP(LDC, LCD, DISPLAYA, DISPLAYB, RSVD, RSVD, RSVD4, 0x1C, 30, 0x90, 14, 0xAC, 20),
- PINGROUP(LDI, LCD, DISPLAYA, DISPLAYB, RSVD, RSVD, RSVD4, 0x20, 6, 0x98, 16, 0xAC, 18),
- PINGROUP(LHP0, LCD, DISPLAYA, DISPLAYB, RSVD, RSVD, RSVD4, 0x1C, 18, 0x98, 10, 0xAC, 16),
- PINGROUP(LHP1, LCD, DISPLAYA, DISPLAYB, RSVD, RSVD, RSVD4, 0x1C, 19, 0x98, 4, 0xAC, 14),
- PINGROUP(LHP2, LCD, DISPLAYA, DISPLAYB, RSVD, RSVD, RSVD4, 0x1C, 20, 0x98, 6, 0xAC, 14),
- PINGROUP(LHS, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x20, 7, 0x90, 22, 0xAC, 22),
- PINGROUP(LM0, LCD, DISPLAYA, DISPLAYB, SPI3, RSVD, RSVD4, 0x1C, 24, 0x90, 26, 0xAC, 22),
- PINGROUP(LM1, LCD, DISPLAYA, DISPLAYB, RSVD, CRT, RSVD3, 0x1C, 25, 0x90, 28, 0xAC, 22),
- PINGROUP(LPP, LCD, DISPLAYA, DISPLAYB, RSVD, RSVD, RSVD4, 0x20, 8, 0x98, 14, 0xAC, 18),
- PINGROUP(LPW0, LCD, DISPLAYA, DISPLAYB, SPI3, HDMI, DISPLAYA, 0x20, 3, 0x90, 0, 0xAC, 20),
- PINGROUP(LPW1, LCD, DISPLAYA, DISPLAYB, RSVD, RSVD, RSVD4, 0x20, 4, 0x90, 2, 0xAC, 20),
- PINGROUP(LPW2, LCD, DISPLAYA, DISPLAYB, SPI3, HDMI, DISPLAYA, 0x20, 5, 0x90, 4, 0xAC, 20),
- PINGROUP(LSC0, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x1C, 27, 0x90, 18, 0xAC, 22),
- PINGROUP(LSC1, LCD, DISPLAYA, DISPLAYB, SPI3, HDMI, DISPLAYA, 0x1C, 28, 0x90, 20, 0xAC, 20),
- PINGROUP(LSCK, LCD, DISPLAYA, DISPLAYB, SPI3, HDMI, DISPLAYA, 0x1C, 29, 0x90, 16, 0xAC, 20),
- PINGROUP(LSDA, LCD, DISPLAYA, DISPLAYB, SPI3, HDMI, DISPLAYA, 0x20, 1, 0x90, 8, 0xAC, 20),
- PINGROUP(LSDI, LCD, DISPLAYA, DISPLAYB, SPI3, RSVD, DISPLAYA, 0x20, 2, 0x90, 6, 0xAC, 20),
- PINGROUP(LSPI, LCD, DISPLAYA, DISPLAYB, XIO, HDMI, DISPLAYA, 0x20, 0, 0x90, 10, 0xAC, 22),
- PINGROUP(LVP0, LCD, DISPLAYA, DISPLAYB, RSVD, RSVD, RSVD4, 0x1C, 21, 0x90, 30, 0xAC, 22),
- PINGROUP(LVP1, LCD, DISPLAYA, DISPLAYB, RSVD, RSVD, RSVD4, 0x1C, 22, 0x98, 8, 0xAC, 16),
- PINGROUP(LVS, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x1C, 26, 0x90, 24, 0xAC, 22),
- PINGROUP(OWC, SYS, OWR, RSVD, RSVD, RSVD, OWR, 0x14, 31, 0x84, 8, 0xB0, 30),
- PINGROUP(PMC, SYS, PWR_ON, PWR_INTR, RSVD, RSVD, PWR_ON, 0x14, 23, 0x98, 18, -1, -1),
- PINGROUP(PTA, NAND, I2C2, HDMI, GMI, RSVD, RSVD4, 0x14, 24, 0x98, 22, 0xA4, 4),
- PINGROUP(RM, UART, I2C, RSVD, RSVD, RSVD, RSVD4, 0x14, 25, 0x80, 14, 0xA4, 0),
- PINGROUP(SDB, SD, UARTA, PWM, SDIO3, SPI2, PWM, 0x20, 15, 0x8C, 10, -1, -1),
- PINGROUP(SDC, SD, PWM, TWC, SDIO3, SPI3, TWC, 0x18, 1, 0x8C, 12, 0xAC, 28),
- PINGROUP(SDD, SD, UARTA, PWM, SDIO3, SPI3, PWM, 0x18, 2, 0x8C, 14, 0xAC, 30),
- PINGROUP(SDIO1, BB, SDIO1, RSVD, UARTE, UARTA, RSVD2, 0x14, 30, 0x80, 30, 0xB0, 18),
- PINGROUP(SLXA, SD, PCIE, SPI4, SDIO3, SPI2, PCIE, 0x18, 3, 0x84, 6, 0xA4, 22),
- PINGROUP(SLXC, SD, SPDIF, SPI4, SDIO3, SPI2, SPI4, 0x18, 5, 0x84, 10, 0xA4, 26),
- PINGROUP(SLXD, SD, SPDIF, SPI4, SDIO3, SPI2, SPI4, 0x18, 6, 0x84, 12, 0xA4, 28),
- PINGROUP(SLXK, SD, PCIE, SPI4, SDIO3, SPI2, PCIE, 0x18, 7, 0x84, 14, 0xA4, 30),
- PINGROUP(SPDI, AUDIO, SPDIF, RSVD, I2C, SDIO2, RSVD2, 0x18, 8, 0x8C, 8, 0xA4, 16),
- PINGROUP(SPDO, AUDIO, SPDIF, RSVD, I2C, SDIO2, RSVD2, 0x18, 9, 0x8C, 6, 0xA4, 18),
- PINGROUP(SPIA, AUDIO, SPI1, SPI2, SPI3, GMI, GMI, 0x18, 10, 0x8C, 30, 0xA8, 4),
- PINGROUP(SPIB, AUDIO, SPI1, SPI2, SPI3, GMI, GMI, 0x18, 11, 0x8C, 28, 0xA8, 6),
- PINGROUP(SPIC, AUDIO, SPI1, SPI2, SPI3, GMI, GMI, 0x18, 12, 0x8C, 26, 0xA8, 8),
- PINGROUP(SPID, AUDIO, SPI2, SPI1, SPI2_ALT, GMI, GMI, 0x18, 13, 0x8C, 24, 0xA8, 10),
- PINGROUP(SPIE, AUDIO, SPI2, SPI1, SPI2_ALT, GMI, GMI, 0x18, 14, 0x8C, 22, 0xA8, 12),
- PINGROUP(SPIF, AUDIO, SPI3, SPI1, SPI2, RSVD, RSVD4, 0x18, 15, 0x8C, 20, 0xA8, 14),
- PINGROUP(SPIG, AUDIO, SPI3, SPI2, SPI2_ALT, I2C, SPI2_ALT, 0x18, 16, 0x8C, 18, 0xA8, 16),
- PINGROUP(SPIH, AUDIO, SPI3, SPI2, SPI2_ALT, I2C, SPI2_ALT, 0x18, 17, 0x8C, 16, 0xA8, 18),
- PINGROUP(UAA, BB, SPI3, MIPI_HS, UARTA, ULPI, MIPI_HS, 0x18, 18, 0x80, 0, 0xAC, 0),
- PINGROUP(UAB, BB, SPI2, MIPI_HS, UARTA, ULPI, MIPI_HS, 0x18, 19, 0x80, 2, 0xAC, 2),
- PINGROUP(UAC, BB, OWR, RSVD, RSVD, RSVD, RSVD4, 0x18, 20, 0x80, 4, 0xAC, 4),
- PINGROUP(UAD, UART, IRDA, SPDIF, UARTA, SPI4, SPDIF, 0x18, 21, 0x80, 6, 0xAC, 6),
- PINGROUP(UCA, UART, UARTC, RSVD, GMI, RSVD, RSVD4, 0x18, 22, 0x84, 16, 0xAC, 8),
- PINGROUP(UCB, UART, UARTC, PWM, GMI, RSVD, RSVD4, 0x18, 23, 0x84, 18, 0xAC, 10),
- PINGROUP(UDA, BB, SPI1, RSVD, UARTD, ULPI, RSVD2, 0x20, 13, 0x80, 8, 0xB0, 16),
- /* these pin groups only have pullup and pull down control */
- PINGROUP(CK32, SYS, RSVD, RSVD, RSVD, RSVD, RSVD, -1, -1, -1, -1, 0xB0, 14),
- PINGROUP(DDRC, DDR, RSVD, RSVD, RSVD, RSVD, RSVD, -1, -1, -1, -1, 0xAC, 26),
- PINGROUP(PMCA, SYS, RSVD, RSVD, RSVD, RSVD, RSVD, -1, -1, -1, -1, 0xB0, 4),
- PINGROUP(PMCB, SYS, RSVD, RSVD, RSVD, RSVD, RSVD, -1, -1, -1, -1, 0xB0, 6),
- PINGROUP(PMCC, SYS, RSVD, RSVD, RSVD, RSVD, RSVD, -1, -1, -1, -1, 0xB0, 8),
- PINGROUP(PMCD, SYS, RSVD, RSVD, RSVD, RSVD, RSVD, -1, -1, -1, -1, 0xB0, 10),
- PINGROUP(PMCE, SYS, RSVD, RSVD, RSVD, RSVD, RSVD, -1, -1, -1, -1, 0xB0, 12),
- PINGROUP(XM2C, DDR, RSVD, RSVD, RSVD, RSVD, RSVD, -1, -1, -1, -1, 0xA8, 30),
- PINGROUP(XM2D, DDR, RSVD, RSVD, RSVD, RSVD, RSVD, -1, -1, -1, -1, 0xA8, 28),
+ PINGROUPS
+};
+
+#undef PINGROUP
+
+#define PINGROUP(pg_name, gpio_nr, vdd, f0, f1, f2, f3, f_safe, \
+ tri_r, tri_b, mux_r, mux_b, pupd_r, pupd_b) \
+ [TEGRA_GPIO_##gpio_nr] = TEGRA_PINGROUP_ ##pg_name\
+
+const int gpio_to_pingroup[TEGRA_MAX_GPIO] = {
+ PINGROUPS
};
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
#define TRISTATE_REG_A 0x14
#define TRISTATE_REG_NUM 4
#define PIN_MUX_CTL_REG_A 0x80
@@ -240,7 +284,7 @@ static inline void pg_writel(unsigned long value, unsigned long offset)
writel(value, IO_TO_VIRT(TEGRA_APB_MISC_BASE + offset));
}
-void tegra_pinmux_suspend(void)
+static int tegra_pinmux_suspend(void)
{
unsigned int i;
u32 *ctx = pinmux_reg;
@@ -256,9 +300,11 @@ void tegra_pinmux_suspend(void)
for (i = 0; i < ARRAY_SIZE(tegra_soc_drive_pingroups); i++)
*ctx++ = pg_readl(tegra_soc_drive_pingroups[i].reg);
+
+ return 0;
}
-void tegra_pinmux_resume(void)
+static void tegra_pinmux_resume(void)
{
unsigned int i;
u32 *ctx = pinmux_reg;
@@ -275,4 +321,18 @@ void tegra_pinmux_resume(void)
for (i = 0; i < ARRAY_SIZE(tegra_soc_drive_pingroups); i++)
pg_writel(*ctx++, tegra_soc_drive_pingroups[i].reg);
}
+
+static struct syscore_ops tegra_pinmux_syscore_ops = {
+ .suspend = tegra_pinmux_suspend,
+ .resume = tegra_pinmux_resume,
+};
+
+void __init tegra_init_pinmux(void)
+{
+ register_syscore_ops(&tegra_pinmux_syscore_ops);
+}
+#else
+void __init tegra_init_pinmux(void)
+{
+}
#endif
diff --git a/arch/arm/mach-tegra/pinmux-t3-tables.c b/arch/arm/mach-tegra/pinmux-t3-tables.c
new file mode 100644
index 000000000000..aaf1390933ed
--- /dev/null
+++ b/arch/arm/mach-tegra/pinmux-t3-tables.c
@@ -0,0 +1,478 @@
+/*
+ * linux/arch/arm/mach-tegra/pinmux-t3-tables.c
+ *
+ * Common pinmux configurations for Tegra 3 SoCs
+ *
+ * Copyright (C) 2010-2011 NVIDIA Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/spinlock.h>
+#include <linux/io.h>
+#include <linux/init.h>
+#include <linux/string.h>
+#include <linux/syscore_ops.h>
+
+#include <mach/iomap.h>
+#include <mach/pinmux.h>
+#include "gpio-names.h"
+
+#define SET_DRIVE_PINGROUP(pg_name, r, drv_down_offset, drv_down_mask, drv_up_offset, drv_up_mask, \
+ slew_rise_offset, slew_rise_mask, slew_fall_offset, slew_fall_mask) \
+ [TEGRA_DRIVE_PINGROUP_ ## pg_name] = { \
+ .name = #pg_name, \
+ .reg = r, \
+ .drvup_offset = drv_up_offset, \
+ .drvup_mask = drv_up_mask, \
+ .drvdown_offset = drv_down_offset, \
+ .drvdown_mask = drv_down_mask, \
+ .slewrise_offset = slew_rise_offset, \
+ .slewrise_mask = slew_rise_mask, \
+ .slewfall_offset = slew_fall_offset, \
+ .slewfall_mask = slew_fall_mask, \
+ }
+
+#define DEFAULT_DRIVE_PINGROUP(pg_name, r) \
+ [TEGRA_DRIVE_PINGROUP_ ## pg_name] = { \
+ .name = #pg_name, \
+ .reg = r, \
+ .drvup_offset = 20, \
+ .drvup_mask = 0x1f, \
+ .drvdown_offset = 12, \
+ .drvdown_mask = 0x1f, \
+ .slewrise_offset = 28, \
+ .slewrise_mask = 0x3, \
+ .slewfall_offset = 30, \
+ .slewfall_mask = 0x3, \
+ }
+
+const struct tegra_drive_pingroup_desc tegra_soc_drive_pingroups[TEGRA_MAX_DRIVE_PINGROUP] = {
+ DEFAULT_DRIVE_PINGROUP(AO1, 0x868),
+ DEFAULT_DRIVE_PINGROUP(AO2, 0x86c),
+ DEFAULT_DRIVE_PINGROUP(AT1, 0x870),
+ DEFAULT_DRIVE_PINGROUP(AT2, 0x874),
+ DEFAULT_DRIVE_PINGROUP(AT3, 0x878),
+ DEFAULT_DRIVE_PINGROUP(AT4, 0x87c),
+ DEFAULT_DRIVE_PINGROUP(AT5, 0x880),
+ DEFAULT_DRIVE_PINGROUP(CDEV1, 0x884),
+ DEFAULT_DRIVE_PINGROUP(CDEV2, 0x888),
+ DEFAULT_DRIVE_PINGROUP(CSUS, 0x88c),
+ DEFAULT_DRIVE_PINGROUP(DAP1, 0x890),
+ DEFAULT_DRIVE_PINGROUP(DAP2, 0x894),
+ DEFAULT_DRIVE_PINGROUP(DAP3, 0x898),
+ DEFAULT_DRIVE_PINGROUP(DAP4, 0x89c),
+ DEFAULT_DRIVE_PINGROUP(DBG, 0x8a0),
+ DEFAULT_DRIVE_PINGROUP(LCD1, 0x8a4),
+ DEFAULT_DRIVE_PINGROUP(LCD2, 0x8a8),
+ SET_DRIVE_PINGROUP(SDIO2, 0x8ac, 12, 0x7f, 20, 0x7f,
+ 28, 0x3, 30, 0x3),
+ SET_DRIVE_PINGROUP(SDIO3, 0x8b0, 12, 0x7f, 20, 0x7f,
+ 28, 0x3, 30, 0x3),
+ DEFAULT_DRIVE_PINGROUP(SPI, 0x8b4),
+ DEFAULT_DRIVE_PINGROUP(UAA, 0x8b8),
+ DEFAULT_DRIVE_PINGROUP(UAB, 0x8bc),
+ DEFAULT_DRIVE_PINGROUP(UART2, 0x8c0),
+ DEFAULT_DRIVE_PINGROUP(UART3, 0x8c4),
+ DEFAULT_DRIVE_PINGROUP(VI1, 0x8c8),
+ SET_DRIVE_PINGROUP(SDIO1, 0x8ec, 12, 0x7f, 20, 0x7f,
+ 28, 0x3, 30, 0x3),
+ DEFAULT_DRIVE_PINGROUP(CRT, 0x8f8),
+ DEFAULT_DRIVE_PINGROUP(DDC, 0x8fc),
+ SET_DRIVE_PINGROUP(GMA, 0x900, 14, 0x1f, 19, 0x1f,
+ 24, 0xf, 28, 0xf),
+ SET_DRIVE_PINGROUP(GMB, 0x904, 14, 0x1f, 19, 0x1f,
+ 24, 0xf, 28, 0xf),
+ SET_DRIVE_PINGROUP(GMC, 0x908, 14, 0x1f, 19, 0x1f,
+ 24, 0xf, 28, 0xf),
+ SET_DRIVE_PINGROUP(GMD, 0x90c, 14, 0x1f, 19, 0x1f,
+ 24, 0xf, 28, 0xf),
+ DEFAULT_DRIVE_PINGROUP(GME, 0x910),
+ DEFAULT_DRIVE_PINGROUP(GMF, 0x914),
+ DEFAULT_DRIVE_PINGROUP(GMG, 0x918),
+ DEFAULT_DRIVE_PINGROUP(GMH, 0x91c),
+ DEFAULT_DRIVE_PINGROUP(OWR, 0x920),
+ DEFAULT_DRIVE_PINGROUP(UAD, 0x924),
+ DEFAULT_DRIVE_PINGROUP(GPV, 0x928),
+ DEFAULT_DRIVE_PINGROUP(DEV3, 0x92c),
+ DEFAULT_DRIVE_PINGROUP(CEC, 0x938),
+};
+
+#define PINGROUP(pg_name, gpio_nr, vdd, f0, f1, f2, f3, fs, iod, reg) \
+ [TEGRA_PINGROUP_ ## pg_name] = { \
+ .name = #pg_name, \
+ .vddio = TEGRA_VDDIO_ ## vdd, \
+ .funcs = { \
+ TEGRA_MUX_ ## f0, \
+ TEGRA_MUX_ ## f1, \
+ TEGRA_MUX_ ## f2, \
+ TEGRA_MUX_ ## f3, \
+ }, \
+ .gpionr = TEGRA_GPIO_ ## gpio_nr, \
+ .func_safe = TEGRA_MUX_ ## fs, \
+ .tri_reg = reg, \
+ .tri_bit = 4, \
+ .mux_reg = reg, \
+ .mux_bit = 0, \
+ .pupd_reg = reg, \
+ .pupd_bit = 2, \
+ .io_default = TEGRA_PIN_ ## iod, \
+ .od_bit = 6, \
+ .lock_bit = 7, \
+ .ioreset_bit = 8, \
+ }
+
+/* !!!FIXME!!! FILL IN fSafe COLUMN IN TABLE ....... */
+
+#define PINGROUPS \
+ /* NAME GPIO VDD f0 f1 f2 f3 fSafe io reg */\
+ PINGROUP(ULPI_DATA0, PO1, BB, SPI3, HSI, UARTA, ULPI, RSVD, INPUT, 0x3000),\
+ PINGROUP(ULPI_DATA1, PO2, BB, SPI3, HSI, UARTA, ULPI, RSVD, INPUT, 0x3004),\
+ PINGROUP(ULPI_DATA2, PO3, BB, SPI3, HSI, UARTA, ULPI, RSVD, INPUT, 0x3008),\
+ PINGROUP(ULPI_DATA3, PO4, BB, SPI3, HSI, UARTA, ULPI, RSVD, INPUT, 0x300c),\
+ PINGROUP(ULPI_DATA4, PO5, BB, SPI2, HSI, UARTA, ULPI, RSVD, INPUT, 0x3010),\
+ PINGROUP(ULPI_DATA5, PO6, BB, SPI2, HSI, UARTA, ULPI, RSVD, INPUT, 0x3014),\
+ PINGROUP(ULPI_DATA6, PO7, BB, SPI2, HSI, UARTA, ULPI, RSVD, INPUT, 0x3018),\
+ PINGROUP(ULPI_DATA7, PO0, BB, SPI2, HSI, UARTA, ULPI, RSVD, INPUT, 0x301c),\
+ PINGROUP(ULPI_CLK, PY0, BB, SPI1, RSVD, UARTD, ULPI, RSVD, INPUT, 0x3020),\
+ PINGROUP(ULPI_DIR, PY1, BB, SPI1, RSVD, UARTD, ULPI, RSVD, INPUT, 0x3024),\
+ PINGROUP(ULPI_NXT, PY2, BB, SPI1, RSVD, UARTD, ULPI, RSVD, INPUT, 0x3028),\
+ PINGROUP(ULPI_STP, PY3, BB, SPI1, RSVD, UARTD, ULPI, RSVD, INPUT, 0x302c),\
+ PINGROUP(DAP3_FS, PP0, BB, I2S2, RSVD1, DISPLAYA, DISPLAYB, RSVD, INPUT, 0x3030),\
+ PINGROUP(DAP3_DIN, PP1, BB, I2S2, RSVD1, DISPLAYA, DISPLAYB, RSVD, INPUT, 0x3034),\
+ PINGROUP(DAP3_DOUT, PP2, BB, I2S2, RSVD1, DISPLAYA, DISPLAYB, RSVD, INPUT, 0x3038),\
+ PINGROUP(DAP3_SCLK, PP3, BB, I2S2, RSVD1, DISPLAYA, DISPLAYB, RSVD, INPUT, 0x303c),\
+ PINGROUP(GPIO_PV0, PV0, BB, RSVD, RSVD, RSVD, RSVD, RSVD, INPUT, 0x3040),\
+ PINGROUP(GPIO_PV1, PV1, BB, RSVD, RSVD, RSVD, RSVD, RSVD, INPUT, 0x3044),\
+ PINGROUP(SDMMC1_CLK, PZ0, SDMMC1, SDMMC1, RSVD1, RSVD2, INVALID, RSVD, INPUT, 0x3048),\
+ PINGROUP(SDMMC1_CMD, PZ1, SDMMC1, SDMMC1, RSVD1, RSVD2, INVALID, RSVD, INPUT, 0x304c),\
+ PINGROUP(SDMMC1_DAT3, PY4, SDMMC1, SDMMC1, RSVD1, UARTE, INVALID, RSVD, INPUT, 0x3050),\
+ PINGROUP(SDMMC1_DAT2, PY5, SDMMC1, SDMMC1, RSVD1, UARTE, INVALID, RSVD, INPUT, 0x3054),\
+ PINGROUP(SDMMC1_DAT1, PY6, SDMMC1, SDMMC1, RSVD1, UARTE, INVALID, RSVD, INPUT, 0x3058),\
+ PINGROUP(SDMMC1_DAT0, PY7, SDMMC1, SDMMC1, RSVD1, UARTE, INVALID, RSVD, INPUT, 0x305c),\
+ PINGROUP(GPIO_PV2, PV2, SDMMC1, OWR, RSVD1, RSVD2, RSVD3, RSVD, INPUT, 0x3060),\
+ PINGROUP(GPIO_PV3, PV3, SDMMC1, INVALID, RSVD1, RSVD2, RSVD3, RSVD, INPUT, 0x3064),\
+ PINGROUP(CLK2_OUT, PW5, SDMMC1, EXTPERIPH2, RSVD1, RSVD2, RSVD3, RSVD, INPUT, 0x3068),\
+ PINGROUP(CLK2_REQ, PCC5, SDMMC1, DAP, RSVD1, RSVD2, RSVD3, RSVD, INPUT, 0x306c),\
+ PINGROUP(LCD_PWR1, PC1, LCD, DISPLAYA, DISPLAYB, RSVD1, RSVD2, RSVD, OUTPUT, 0x3070),\
+ PINGROUP(LCD_PWR2, PC6, LCD, DISPLAYA, DISPLAYB, SPI5, INVALID, RSVD, OUTPUT, 0x3074),\
+ PINGROUP(LCD_SDIN, PZ2, LCD, DISPLAYA, DISPLAYB, SPI5, RSVD, RSVD, OUTPUT, 0x3078),\
+ PINGROUP(LCD_SDOUT, PN5, LCD, DISPLAYA, DISPLAYB, SPI5, INVALID, RSVD, OUTPUT, 0x307c),\
+ PINGROUP(LCD_WR_N, PZ3, LCD, DISPLAYA, DISPLAYB, SPI5, INVALID, RSVD, OUTPUT, 0x3080),\
+ PINGROUP(LCD_CS0_N, PN4, LCD, DISPLAYA, DISPLAYB, SPI5, RSVD, RSVD, OUTPUT, 0x3084),\
+ PINGROUP(LCD_DC0, PN6, LCD, DISPLAYA, DISPLAYB, RSVD1, RSVD2, RSVD, OUTPUT, 0x3088),\
+ PINGROUP(LCD_SCK, PZ4, LCD, DISPLAYA, DISPLAYB, SPI5, INVALID, RSVD, OUTPUT, 0x308c),\
+ PINGROUP(LCD_PWR0, PB2, LCD, DISPLAYA, DISPLAYB, SPI5, INVALID, RSVD, OUTPUT, 0x3090),\
+ PINGROUP(LCD_PCLK, PB3, LCD, DISPLAYA, DISPLAYB, RSVD1, RSVD2, RSVD, OUTPUT, 0x3094),\
+ PINGROUP(LCD_DE, PJ1, LCD, DISPLAYA, DISPLAYB, RSVD1, RSVD2, RSVD, OUTPUT, 0x3098),\
+ PINGROUP(LCD_HSYNC, PJ3, LCD, DISPLAYA, DISPLAYB, RSVD1, RSVD2, RSVD, OUTPUT, 0x309c),\
+ PINGROUP(LCD_VSYNC, PJ4, LCD, DISPLAYA, DISPLAYB, RSVD1, RSVD2, RSVD, OUTPUT, 0x30a0),\
+ PINGROUP(LCD_D0, PE0, LCD, DISPLAYA, DISPLAYB, RSVD1, RSVD2, RSVD, OUTPUT, 0x30a4),\
+ PINGROUP(LCD_D1, PE1, LCD, DISPLAYA, DISPLAYB, RSVD1, RSVD2, RSVD, OUTPUT, 0x30a8),\
+ PINGROUP(LCD_D2, PE2, LCD, DISPLAYA, DISPLAYB, RSVD1, RSVD2, RSVD, OUTPUT, 0x30ac),\
+ PINGROUP(LCD_D3, PE3, LCD, DISPLAYA, DISPLAYB, RSVD1, RSVD2, RSVD, OUTPUT, 0x30b0),\
+ PINGROUP(LCD_D4, PE4, LCD, DISPLAYA, DISPLAYB, RSVD1, RSVD2, RSVD, OUTPUT, 0x30b4),\
+ PINGROUP(LCD_D5, PE5, LCD, DISPLAYA, DISPLAYB, RSVD1, RSVD2, RSVD, OUTPUT, 0x30b8),\
+ PINGROUP(LCD_D6, PE6, LCD, DISPLAYA, DISPLAYB, RSVD1, RSVD2, RSVD, OUTPUT, 0x30bc),\
+ PINGROUP(LCD_D7, PE7, LCD, DISPLAYA, DISPLAYB, RSVD1, RSVD2, RSVD, OUTPUT, 0x30c0),\
+ PINGROUP(LCD_D8, PF0, LCD, DISPLAYA, DISPLAYB, RSVD1, RSVD2, RSVD, OUTPUT, 0x30c4),\
+ PINGROUP(LCD_D9, PF1, LCD, DISPLAYA, DISPLAYB, RSVD1, RSVD2, RSVD, OUTPUT, 0x30c8),\
+ PINGROUP(LCD_D10, PF2, LCD, DISPLAYA, DISPLAYB, RSVD1, RSVD2, RSVD, OUTPUT, 0x30cc),\
+ PINGROUP(LCD_D11, PF3, LCD, DISPLAYA, DISPLAYB, RSVD1, RSVD2, RSVD, OUTPUT, 0x30d0),\
+ PINGROUP(LCD_D12, PF4, LCD, DISPLAYA, DISPLAYB, RSVD1, RSVD2, RSVD, OUTPUT, 0x30d4),\
+ PINGROUP(LCD_D13, PF5, LCD, DISPLAYA, DISPLAYB, RSVD1, RSVD2, RSVD, OUTPUT, 0x30d8),\
+ PINGROUP(LCD_D14, PF6, LCD, DISPLAYA, DISPLAYB, RSVD1, RSVD2, RSVD, OUTPUT, 0x30dc),\
+ PINGROUP(LCD_D15, PF7, LCD, DISPLAYA, DISPLAYB, RSVD1, RSVD2, RSVD, OUTPUT, 0x30e0),\
+ PINGROUP(LCD_D16, PM0, LCD, DISPLAYA, DISPLAYB, RSVD1, RSVD2, RSVD, OUTPUT, 0x30e4),\
+ PINGROUP(LCD_D17, PM1, LCD, DISPLAYA, DISPLAYB, RSVD1, RSVD2, RSVD, OUTPUT, 0x30e8),\
+ PINGROUP(LCD_D18, PM2, LCD, DISPLAYA, DISPLAYB, RSVD1, RSVD2, RSVD, OUTPUT, 0x30ec),\
+ PINGROUP(LCD_D19, PM3, LCD, DISPLAYA, DISPLAYB, RSVD1, RSVD2, RSVD, OUTPUT, 0x30f0),\
+ PINGROUP(LCD_D20, PM4, LCD, DISPLAYA, DISPLAYB, RSVD1, RSVD2, RSVD, OUTPUT, 0x30f4),\
+ PINGROUP(LCD_D21, PM5, LCD, DISPLAYA, DISPLAYB, RSVD1, RSVD2, RSVD, OUTPUT, 0x30f8),\
+ PINGROUP(LCD_D22, PM6, LCD, DISPLAYA, DISPLAYB, RSVD1, RSVD2, RSVD, OUTPUT, 0x30fc),\
+ PINGROUP(LCD_D23, PM7, LCD, DISPLAYA, DISPLAYB, RSVD1, RSVD2, RSVD, OUTPUT, 0x3100),\
+ PINGROUP(LCD_CS1_N, PW0, LCD, DISPLAYA, DISPLAYB, SPI5, RSVD2, RSVD, OUTPUT, 0x3104),\
+ PINGROUP(LCD_M1, PW1, LCD, DISPLAYA, DISPLAYB, RSVD1, RSVD2, RSVD, OUTPUT, 0x3108),\
+ PINGROUP(LCD_DC1, PD2, LCD, DISPLAYA, DISPLAYB, RSVD1, RSVD2, RSVD, OUTPUT, 0x310c),\
+ PINGROUP(HDMI_INT, PN7, LCD, RSVD, RSVD, RSVD, RSVD, RSVD, INPUT, 0x3110),\
+ PINGROUP(DDC_SCL, PV4, LCD, I2C4, RSVD1, RSVD2, RSVD3, RSVD, INPUT, 0x3114),\
+ PINGROUP(DDC_SDA, PV5, LCD, I2C4, RSVD1, RSVD2, RSVD3, RSVD, INPUT, 0x3118),\
+ PINGROUP(CRT_HSYNC, PV6, LCD, CRT, RSVD1, RSVD2, RSVD3, RSVD, INPUT, 0x311c),\
+ PINGROUP(CRT_VSYNC, PV7, LCD, CRT, RSVD1, RSVD2, RSVD3, RSVD, INPUT, 0x3120),\
+ PINGROUP(VI_D0, PT4, VI, INVALID, RSVD1, VI, RSVD2, RSVD, INPUT, 0x3124),\
+ PINGROUP(VI_D1, PD5, VI, INVALID, SDMMC2, VI, RSVD1, RSVD, INPUT, 0x3128),\
+ PINGROUP(VI_D2, PL0, VI, INVALID, SDMMC2, VI, RSVD1, RSVD, INPUT, 0x312c),\
+ PINGROUP(VI_D3, PL1, VI, INVALID, SDMMC2, VI, RSVD1, RSVD, INPUT, 0x3130),\
+ PINGROUP(VI_D4, PL2, VI, INVALID, SDMMC2, VI, RSVD1, RSVD, INPUT, 0x3134),\
+ PINGROUP(VI_D5, PL3, VI, INVALID, SDMMC2, VI, RSVD1, RSVD, INPUT, 0x3138),\
+ PINGROUP(VI_D6, PL4, VI, INVALID, SDMMC2, VI, RSVD1, RSVD, INPUT, 0x313c),\
+ PINGROUP(VI_D7, PL5, VI, INVALID, SDMMC2, VI, RSVD1, RSVD, INPUT, 0x3140),\
+ PINGROUP(VI_D8, PL6, VI, INVALID, SDMMC2, VI, RSVD1, RSVD, INPUT, 0x3144),\
+ PINGROUP(VI_D9, PL7, VI, INVALID, SDMMC2, VI, RSVD1, RSVD, INPUT, 0x3148),\
+ PINGROUP(VI_D10, PT2, VI, INVALID, RSVD1, VI, RSVD2, RSVD, INPUT, 0x314c),\
+ PINGROUP(VI_D11, PT3, VI, INVALID, RSVD1, VI, RSVD2, RSVD, INPUT, 0x3150),\
+ PINGROUP(VI_PCLK, PT0, VI, RSVD1, SDMMC2, VI, RSVD2, RSVD, INPUT, 0x3154),\
+ PINGROUP(VI_MCLK, PT1, VI, INVALID, INVALID, INVALID, VI, RSVD, INPUT, 0x3158),\
+ PINGROUP(VI_VSYNC, PD6, VI, INVALID, RSVD1, VI, RSVD2, RSVD, INPUT, 0x315c),\
+ PINGROUP(VI_HSYNC, PD7, VI, INVALID, RSVD1, VI, RSVD2, RSVD, INPUT, 0x3160),\
+ PINGROUP(UART2_RXD, PC3, UART, IRDA, SPDIF, UARTA, SPI4, RSVD, INPUT, 0x3164),\
+ PINGROUP(UART2_TXD, PC2, UART, IRDA, SPDIF, UARTA, SPI4, RSVD, INPUT, 0x3168),\
+ PINGROUP(UART2_RTS_N, PJ6, UART, UARTA, UARTB, GMI, SPI4, RSVD, INPUT, 0x316c),\
+ PINGROUP(UART2_CTS_N, PJ5, UART, UARTA, UARTB, GMI, SPI4, RSVD, INPUT, 0x3170),\
+ PINGROUP(UART3_TXD, PW6, UART, UARTC, RSVD1, GMI, RSVD2, RSVD, INPUT, 0x3174),\
+ PINGROUP(UART3_RXD, PW7, UART, UARTC, RSVD1, GMI, RSVD2, RSVD, INPUT, 0x3178),\
+ PINGROUP(UART3_CTS_N, PA1, UART, UARTC, RSVD1, GMI, RSVD2, RSVD, INPUT, 0x317c),\
+ PINGROUP(UART3_RTS_N, PC0, UART, UARTC, PWM0, GMI, RSVD2, RSVD, INPUT, 0x3180),\
+ PINGROUP(GPIO_PU0, PU0, UART, OWR, UARTA, GMI, RSVD1, RSVD, INPUT, 0x3184),\
+ PINGROUP(GPIO_PU1, PU1, UART, RSVD1, UARTA, GMI, RSVD2, RSVD, INPUT, 0x3188),\
+ PINGROUP(GPIO_PU2, PU2, UART, RSVD1, UARTA, GMI, RSVD2, RSVD, INPUT, 0x318c),\
+ PINGROUP(GPIO_PU3, PU3, UART, PWM0, UARTA, GMI, RSVD1, RSVD, INPUT, 0x3190),\
+ PINGROUP(GPIO_PU4, PU4, UART, PWM1, UARTA, GMI, RSVD1, RSVD, INPUT, 0x3194),\
+ PINGROUP(GPIO_PU5, PU5, UART, PWM2, UARTA, GMI, RSVD1, RSVD, INPUT, 0x3198),\
+ PINGROUP(GPIO_PU6, PU6, UART, PWM3, UARTA, GMI, RSVD1, RSVD, INPUT, 0x319c),\
+ PINGROUP(GEN1_I2C_SDA, PC5, UART, I2C1, RSVD1, RSVD2, RSVD3, RSVD, INPUT, 0x31a0),\
+ PINGROUP(GEN1_I2C_SCL, PC4, UART, I2C1, RSVD1, RSVD2, RSVD3, RSVD, INPUT, 0x31a4),\
+ PINGROUP(DAP4_FS, PP4, UART, I2S3, RSVD1, GMI, RSVD2, RSVD, INPUT, 0x31a8),\
+ PINGROUP(DAP4_DIN, PP5, UART, I2S3, RSVD1, GMI, RSVD2, RSVD, INPUT, 0x31ac),\
+ PINGROUP(DAP4_DOUT, PP6, UART, I2S3, RSVD1, GMI, RSVD2, RSVD, INPUT, 0x31b0),\
+ PINGROUP(DAP4_SCLK, PP7, UART, I2S3, RSVD1, GMI, RSVD2, RSVD, INPUT, 0x31b4),\
+ PINGROUP(CLK3_OUT, PEE0, UART, EXTPERIPH3, RSVD1, RSVD2, RSVD3, RSVD, INPUT, 0x31b8),\
+ PINGROUP(CLK3_REQ, PEE1, UART, DEV3, RSVD1, RSVD2, RSVD3, RSVD, INPUT, 0x31bc),\
+ PINGROUP(GMI_WP_N, PC7, GMI, RSVD1, NAND, GMI, GMI_ALT, RSVD, INPUT, 0x31c0),\
+ PINGROUP(GMI_IORDY, PI5, GMI, RSVD1, NAND, GMI, RSVD2, RSVD, INPUT, 0x31c4),\
+ PINGROUP(GMI_WAIT, PI7, GMI, RSVD1, NAND, GMI, RSVD2, RSVD, INPUT, 0x31c8),\
+ PINGROUP(GMI_ADV_N, PK0, GMI, RSVD1, NAND, GMI, RSVD2, RSVD, INPUT, 0x31cc),\
+ PINGROUP(GMI_CLK, PK1, GMI, RSVD1, NAND, GMI, RSVD2, RSVD, INPUT, 0x31d0),\
+ PINGROUP(GMI_CS0_N, PJ0, GMI, RSVD1, NAND, GMI, INVALID, RSVD, INPUT, 0x31d4),\
+ PINGROUP(GMI_CS1_N, PJ2, GMI, RSVD1, NAND, GMI, DTV, RSVD, INPUT, 0x31d8),\
+ PINGROUP(GMI_CS2_N, PK3, GMI, RSVD1, NAND, GMI, RSVD2, RSVD, INPUT, 0x31dc),\
+ PINGROUP(GMI_CS3_N, PK4, GMI, RSVD1, NAND, GMI, GMI_ALT, RSVD, INPUT, 0x31e0),\
+ PINGROUP(GMI_CS4_N, PK2, GMI, RSVD1, NAND, GMI, RSVD2, RSVD, INPUT, 0x31e4),\
+ PINGROUP(GMI_CS6_N, PI3, GMI, NAND, NAND_ALT, GMI, SATA, RSVD, INPUT, 0x31e8),\
+ PINGROUP(GMI_CS7_N, PI6, GMI, NAND, NAND_ALT, GMI, GMI_ALT, RSVD, INPUT, 0x31ec),\
+ PINGROUP(GMI_AD0, PG0, GMI, RSVD1, NAND, GMI, RSVD2, RSVD, INPUT, 0x31f0),\
+ PINGROUP(GMI_AD1, PG1, GMI, RSVD1, NAND, GMI, RSVD2, RSVD, INPUT, 0x31f4),\
+ PINGROUP(GMI_AD2, PG2, GMI, RSVD1, NAND, GMI, RSVD2, RSVD, INPUT, 0x31f8),\
+ PINGROUP(GMI_AD3, PG3, GMI, RSVD1, NAND, GMI, RSVD2, RSVD, INPUT, 0x31fc),\
+ PINGROUP(GMI_AD4, PG4, GMI, RSVD1, NAND, GMI, RSVD2, RSVD, INPUT, 0x3200),\
+ PINGROUP(GMI_AD5, PG5, GMI, RSVD1, NAND, GMI, RSVD2, RSVD, INPUT, 0x3204),\
+ PINGROUP(GMI_AD6, PG6, GMI, RSVD1, NAND, GMI, RSVD2, RSVD, INPUT, 0x3208),\
+ PINGROUP(GMI_AD7, PG7, GMI, RSVD1, NAND, GMI, RSVD2, RSVD, INPUT, 0x320c),\
+ PINGROUP(GMI_AD8, PH0, GMI, PWM0, NAND, GMI, RSVD2, RSVD, INPUT, 0x3210),\
+ PINGROUP(GMI_AD9, PH1, GMI, PWM1, NAND, GMI, RSVD2, RSVD, INPUT, 0x3214),\
+ PINGROUP(GMI_AD10, PH2, GMI, PWM2, NAND, GMI, RSVD2, RSVD, INPUT, 0x3218),\
+ PINGROUP(GMI_AD11, PH3, GMI, PWM3, NAND, GMI, RSVD2, RSVD, INPUT, 0x321c),\
+ PINGROUP(GMI_AD12, PH4, GMI, RSVD1, NAND, GMI, RSVD2, RSVD, INPUT, 0x3220),\
+ PINGROUP(GMI_AD13, PH5, GMI, RSVD1, NAND, GMI, RSVD2, RSVD, INPUT, 0x3224),\
+ PINGROUP(GMI_AD14, PH6, GMI, RSVD1, NAND, GMI, RSVD2, RSVD, INPUT, 0x3228),\
+ PINGROUP(GMI_AD15, PH7, GMI, RSVD1, NAND, GMI, RSVD2, RSVD, INPUT, 0x322c),\
+ PINGROUP(GMI_A16, PJ7, GMI, UARTD, SPI4, GMI, GMI_ALT, RSVD, INPUT, 0x3230),\
+ PINGROUP(GMI_A17, PB0, GMI, UARTD, SPI4, GMI, INVALID, RSVD, INPUT, 0x3234),\
+ PINGROUP(GMI_A18, PB1, GMI, UARTD, SPI4, GMI, INVALID, RSVD, INPUT, 0x3238),\
+ PINGROUP(GMI_A19, PK7, GMI, UARTD, SPI4, GMI, RSVD3, RSVD, INPUT, 0x323c),\
+ PINGROUP(GMI_WR_N, PI0, GMI, RSVD1, NAND, GMI, RSVD3, RSVD, INPUT, 0x3240),\
+ PINGROUP(GMI_OE_N, PI1, GMI, RSVD1, NAND, GMI, RSVD3, RSVD, INPUT, 0x3244),\
+ PINGROUP(GMI_DQS, PI2, GMI, RSVD1, NAND, GMI, RSVD3, RSVD, INPUT, 0x3248),\
+ PINGROUP(GMI_RST_N, PI4, GMI, NAND, NAND_ALT, GMI, RSVD3, RSVD, INPUT, 0x324c),\
+ PINGROUP(GEN2_I2C_SCL, PT5, GMI, I2C2, INVALID, GMI, RSVD3, RSVD, INPUT, 0x3250),\
+ PINGROUP(GEN2_I2C_SDA, PT6, GMI, I2C2, INVALID, GMI, RSVD3, RSVD, INPUT, 0x3254),\
+ PINGROUP(SDMMC4_CLK, PCC4, SDMMC4, INVALID, NAND, GMI, SDMMC4, RSVD, INPUT, 0x3258),\
+ PINGROUP(SDMMC4_CMD, PT7, SDMMC4, I2C3, NAND, GMI, SDMMC4, RSVD, INPUT, 0x325c),\
+ PINGROUP(SDMMC4_DAT0, PAA0, SDMMC4, UARTE, SPI3, GMI, SDMMC4, RSVD, INPUT, 0x3260),\
+ PINGROUP(SDMMC4_DAT1, PAA1, SDMMC4, UARTE, SPI3, GMI, SDMMC4, RSVD, INPUT, 0x3264),\
+ PINGROUP(SDMMC4_DAT2, PAA2, SDMMC4, UARTE, SPI3, GMI, SDMMC4, RSVD, INPUT, 0x3268),\
+ PINGROUP(SDMMC4_DAT3, PAA3, SDMMC4, UARTE, SPI3, GMI, SDMMC4, RSVD, INPUT, 0x326c),\
+ PINGROUP(SDMMC4_DAT4, PAA4, SDMMC4, I2C3, I2S4, GMI, SDMMC4, RSVD, INPUT, 0x3270),\
+ PINGROUP(SDMMC4_DAT5, PAA5, SDMMC4, VGP3, I2S4, GMI, SDMMC4, RSVD, INPUT, 0x3274),\
+ PINGROUP(SDMMC4_DAT6, PAA6, SDMMC4, VGP4, I2S4, GMI, SDMMC4, RSVD, INPUT, 0x3278),\
+ PINGROUP(SDMMC4_DAT7, PAA7, SDMMC4, VGP5, I2S4, GMI, SDMMC4, RSVD, INPUT, 0x327c),\
+ PINGROUP(SDMMC4_RST_N, PCC3, SDMMC4, VGP6, RSVD1, RSVD2, POPSDMMC4, RSVD, INPUT, 0x3280),\
+ PINGROUP(CAM_MCLK, PCC0, CAM, VI, INVALID, VI_ALT2, POPSDMMC4, RSVD, INPUT, 0x3284),\
+ PINGROUP(GPIO_PCC1, PCC1, CAM, I2S4, RSVD1, RSVD2, POPSDMMC4, RSVD, INPUT, 0x3288),\
+ PINGROUP(GPIO_PBB0, PBB0, CAM, I2S4, RSVD1, RSVD2, POPSDMMC4, RSVD, INPUT, 0x328c),\
+ PINGROUP(CAM_I2C_SCL, PBB1, CAM, INVALID, I2C3, RSVD2, POPSDMMC4, RSVD, INPUT, 0x3290),\
+ PINGROUP(CAM_I2C_SDA, PBB2, CAM, INVALID, I2C3, RSVD2, POPSDMMC4, RSVD, INPUT, 0x3294),\
+ PINGROUP(GPIO_PBB3, PBB3, CAM, VGP3, DISPLAYA, DISPLAYB, POPSDMMC4, RSVD, INPUT, 0x3298),\
+ PINGROUP(GPIO_PBB4, PBB4, CAM, VGP4, DISPLAYA, DISPLAYB, POPSDMMC4, RSVD, INPUT, 0x329c),\
+ PINGROUP(GPIO_PBB5, PBB5, CAM, VGP5, DISPLAYA, DISPLAYB, POPSDMMC4, RSVD, INPUT, 0x32a0),\
+ PINGROUP(GPIO_PBB6, PBB6, CAM, VGP6, DISPLAYA, DISPLAYB, POPSDMMC4, RSVD, INPUT, 0x32a4),\
+ PINGROUP(GPIO_PBB7, PBB7, CAM, I2S4, RSVD1, RSVD2, POPSDMMC4, RSVD, INPUT, 0x32a8),\
+ PINGROUP(GPIO_PCC2, PCC2, CAM, I2S4, RSVD1, RSVD2, RSVD3, RSVD, INPUT, 0x32ac),\
+ PINGROUP(JTAG_RTCK, PU7, SYS, RTCK, RSVD1, RSVD2, RSVD3, RSVD, INPUT, 0x32b0),\
+ PINGROUP(PWR_I2C_SCL, PZ6, SYS, I2CPWR, RSVD1, RSVD2, RSVD3, RSVD, INPUT, 0x32b4),\
+ PINGROUP(PWR_I2C_SDA, PZ7, SYS, I2CPWR, RSVD1, RSVD2, RSVD3, RSVD, INPUT, 0x32b8),\
+ PINGROUP(KB_ROW0, PR0, SYS, KBC, INVALID, RSVD2, RSVD3, RSVD, INPUT, 0x32bc),\
+ PINGROUP(KB_ROW1, PR1, SYS, KBC, INVALID, RSVD2, RSVD3, RSVD, INPUT, 0x32c0),\
+ PINGROUP(KB_ROW2, PR2, SYS, KBC, INVALID, RSVD2, RSVD3, RSVD, INPUT, 0x32c4),\
+ PINGROUP(KB_ROW3, PR3, SYS, KBC, INVALID, RSVD2, INVALID, RSVD, INPUT, 0x32c8),\
+ PINGROUP(KB_ROW4, PR4, SYS, KBC, INVALID, TRACE, RSVD3, RSVD, INPUT, 0x32cc),\
+ PINGROUP(KB_ROW5, PR5, SYS, KBC, INVALID, TRACE, OWR, RSVD, INPUT, 0x32d0),\
+ PINGROUP(KB_ROW6, PR6, SYS, KBC, INVALID, SDMMC2, INVALID, RSVD, INPUT, 0x32d4),\
+ PINGROUP(KB_ROW7, PR7, SYS, KBC, INVALID, SDMMC2, INVALID, RSVD, INPUT, 0x32d8),\
+ PINGROUP(KB_ROW8, PS0, SYS, KBC, INVALID, SDMMC2, INVALID, RSVD, INPUT, 0x32dc),\
+ PINGROUP(KB_ROW9, PS1, SYS, KBC, INVALID, SDMMC2, INVALID, RSVD, INPUT, 0x32e0),\
+ PINGROUP(KB_ROW10, PS2, SYS, KBC, INVALID, SDMMC2, INVALID, RSVD, INPUT, 0x32e4),\
+ PINGROUP(KB_ROW11, PS3, SYS, KBC, INVALID, SDMMC2, INVALID, RSVD, INPUT, 0x32e8),\
+ PINGROUP(KB_ROW12, PS4, SYS, KBC, INVALID, SDMMC2, INVALID, RSVD, INPUT, 0x32ec),\
+ PINGROUP(KB_ROW13, PS5, SYS, KBC, INVALID, SDMMC2, INVALID, RSVD, INPUT, 0x32f0),\
+ PINGROUP(KB_ROW14, PS6, SYS, KBC, INVALID, SDMMC2, INVALID, RSVD, INPUT, 0x32f4),\
+ PINGROUP(KB_ROW15, PS7, SYS, KBC, INVALID, SDMMC2, INVALID, RSVD, INPUT, 0x32f8),\
+ PINGROUP(KB_COL0, PQ0, SYS, KBC, INVALID, TRACE, INVALID, RSVD, INPUT, 0x32fc),\
+ PINGROUP(KB_COL1, PQ1, SYS, KBC, INVALID, TRACE, INVALID, RSVD, INPUT, 0x3300),\
+ PINGROUP(KB_COL2, PQ2, SYS, KBC, INVALID, TRACE, RSVD, RSVD, INPUT, 0x3304),\
+ PINGROUP(KB_COL3, PQ3, SYS, KBC, INVALID, TRACE, RSVD, RSVD, INPUT, 0x3308),\
+ PINGROUP(KB_COL4, PQ4, SYS, KBC, INVALID, TRACE, RSVD, RSVD, INPUT, 0x330c),\
+ PINGROUP(KB_COL5, PQ5, SYS, KBC, INVALID, TRACE, RSVD, RSVD, INPUT, 0x3310),\
+ PINGROUP(KB_COL6, PQ6, SYS, KBC, INVALID, TRACE, INVALID, RSVD, INPUT, 0x3314),\
+ PINGROUP(KB_COL7, PQ7, SYS, KBC, INVALID, TRACE, INVALID, RSVD, INPUT, 0x3318),\
+ PINGROUP(CLK_32K_OUT, PA0, SYS, BLINK, RSVD1, RSVD2, RSVD3, RSVD, INPUT, 0x331c),\
+ PINGROUP(SYS_CLK_REQ, PZ5, SYS, SYSCLK, RSVD1, RSVD2, RSVD3, RSVD, INPUT, 0x3320),\
+ PINGROUP(CORE_PWR_REQ, INVALID, SYS, RSVD, RSVD, RSVD, RSVD, RSVD, INPUT, 0x3324),\
+ PINGROUP(CPU_PWR_REQ, INVALID, SYS, RSVD, RSVD, RSVD, RSVD, RSVD, INPUT, 0x3328),\
+ PINGROUP(PWR_INT_N, INVALID, SYS, RSVD, RSVD, RSVD, RSVD, RSVD, INPUT, 0x332c),\
+ PINGROUP(CLK_32K_IN, INVALID, SYS, RSVD, RSVD, RSVD, RSVD, RSVD, INPUT, 0x3330),\
+ PINGROUP(OWR, INVALID, SYS, OWR, RSVD, RSVD, RSVD, RSVD, INPUT, 0x3334),\
+ PINGROUP(DAP1_FS, PN0, AUDIO, I2S0, HDA, GMI, SDMMC2, RSVD, INPUT, 0x3338),\
+ PINGROUP(DAP1_DIN, PN1, AUDIO, I2S0, HDA, GMI, SDMMC2, RSVD, INPUT, 0x333c),\
+ PINGROUP(DAP1_DOUT, PN2, AUDIO, I2S0, HDA, GMI, SDMMC2, RSVD, INPUT, 0x3340),\
+ PINGROUP(DAP1_SCLK, PN3, AUDIO, I2S0, HDA, GMI, SDMMC2, RSVD, INPUT, 0x3344),\
+ PINGROUP(CLK1_REQ, PEE2, AUDIO, DAP, HDA, RSVD2, RSVD3, RSVD, INPUT, 0x3348),\
+ PINGROUP(CLK1_OUT, PW4, AUDIO, EXTPERIPH1, RSVD1, RSVD2, RSVD3, RSVD, INPUT, 0x334c),\
+ PINGROUP(SPDIF_IN, PK6, AUDIO, SPDIF, HDA, INVALID, DAPSDMMC2, RSVD, INPUT, 0x3350),\
+ PINGROUP(SPDIF_OUT, PK5, AUDIO, SPDIF, RSVD1, INVALID, DAPSDMMC2, RSVD, INPUT, 0x3354),\
+ PINGROUP(DAP2_FS, PA2, AUDIO, I2S1, HDA, RSVD2, GMI, RSVD, INPUT, 0x3358),\
+ PINGROUP(DAP2_DIN, PA4, AUDIO, I2S1, HDA, RSVD2, GMI, RSVD, INPUT, 0x335c),\
+ PINGROUP(DAP2_DOUT, PA5, AUDIO, I2S1, HDA, RSVD2, GMI, RSVD, INPUT, 0x3360),\
+ PINGROUP(DAP2_SCLK, PA3, AUDIO, I2S1, HDA, RSVD2, GMI, RSVD, INPUT, 0x3364),\
+ PINGROUP(SPI2_MOSI, PX0, AUDIO, SPI6, SPI2, INVALID, GMI, RSVD, INPUT, 0x3368),\
+ PINGROUP(SPI2_MISO, PX1, AUDIO, SPI6, SPI2, INVALID, GMI, RSVD, INPUT, 0x336c),\
+ PINGROUP(SPI2_CS0_N, PX3, AUDIO, SPI6, SPI2, INVALID, GMI, RSVD, INPUT, 0x3370),\
+ PINGROUP(SPI2_SCK, PX2, AUDIO, SPI6, SPI2, INVALID, GMI, RSVD, INPUT, 0x3374),\
+ PINGROUP(SPI1_MOSI, PX4, AUDIO, SPI2, SPI1, INVALID, GMI, RSVD, INPUT, 0x3378),\
+ PINGROUP(SPI1_SCK, PX5, AUDIO, SPI2, SPI1, INVALID, GMI, RSVD, INPUT, 0x337c),\
+ PINGROUP(SPI1_CS0_N, PX6, AUDIO, SPI2, SPI1, INVALID, GMI, RSVD, INPUT, 0x3380),\
+ PINGROUP(SPI1_MISO, PX7, AUDIO, INVALID, SPI1, INVALID, RSVD3, RSVD, INPUT, 0x3384),\
+ PINGROUP(SPI2_CS1_N, PW2, AUDIO, INVALID, SPI2, INVALID, INVALID, RSVD, INPUT, 0x3388),\
+ PINGROUP(SPI2_CS2_N, PW3, AUDIO, INVALID, SPI2, INVALID, INVALID, RSVD, INPUT, 0x338c),\
+ PINGROUP(SDMMC3_CLK, PA6, SDMMC3, UARTA, PWM2, SDMMC3, INVALID, RSVD, INPUT, 0x3390),\
+ PINGROUP(SDMMC3_CMD, PA7, SDMMC3, UARTA, PWM3, SDMMC3, INVALID, RSVD, INPUT, 0x3394),\
+ PINGROUP(SDMMC3_DAT0, PB7, SDMMC3, RSVD0, RSVD1, SDMMC3, INVALID, RSVD, INPUT, 0x3398),\
+ PINGROUP(SDMMC3_DAT1, PB6, SDMMC3, RSVD0, RSVD1, SDMMC3, INVALID, RSVD, INPUT, 0x339c),\
+ PINGROUP(SDMMC3_DAT2, PB5, SDMMC3, RSVD0, PWM1, SDMMC3, INVALID, RSVD, INPUT, 0x33a0),\
+ PINGROUP(SDMMC3_DAT3, PB4, SDMMC3, RSVD0, PWM0, SDMMC3, INVALID, RSVD, INPUT, 0x33a4),\
+ PINGROUP(SDMMC3_DAT4, PD1, SDMMC3, PWM1, INVALID, SDMMC3, INVALID, RSVD, INPUT, 0x33a8),\
+ PINGROUP(SDMMC3_DAT5, PD0, SDMMC3, PWM0, INVALID, SDMMC3, INVALID, RSVD, INPUT, 0x33ac),\
+ PINGROUP(SDMMC3_DAT6, PD3, SDMMC3, SPDIF, INVALID, SDMMC3, INVALID, RSVD, INPUT, 0x33b0),\
+ PINGROUP(SDMMC3_DAT7, PD4, SDMMC3, SPDIF, INVALID, SDMMC3, INVALID, RSVD, INPUT, 0x33b4),\
+ PINGROUP(PEX_L0_PRSNT_N, PDD0, PEXCTL, PCIE, HDA, RSVD2, RSVD3, RSVD, INPUT, 0x33b8),\
+ PINGROUP(PEX_L0_RST_N, PDD1, PEXCTL, PCIE, HDA, RSVD2, RSVD3, RSVD, INPUT, 0x33bc),\
+ PINGROUP(PEX_L0_CLKREQ_N, PDD2, PEXCTL, PCIE, HDA, RSVD2, RSVD3, RSVD, INPUT, 0x33c0),\
+ PINGROUP(PEX_WAKE_N, PDD3, PEXCTL, PCIE, HDA, RSVD2, RSVD3, RSVD, INPUT, 0x33c4),\
+ PINGROUP(PEX_L1_PRSNT_N, PDD4, PEXCTL, PCIE, HDA, RSVD2, RSVD3, RSVD, INPUT, 0x33c8),\
+ PINGROUP(PEX_L1_RST_N, PDD5, PEXCTL, PCIE, HDA, RSVD2, RSVD3, RSVD, INPUT, 0x33cc),\
+ PINGROUP(PEX_L1_CLKREQ_N, PDD6, PEXCTL, PCIE, HDA, RSVD2, RSVD3, RSVD, INPUT, 0x33d0),\
+ PINGROUP(PEX_L2_PRSNT_N, PDD7, PEXCTL, PCIE, HDA, RSVD2, RSVD3, RSVD, INPUT, 0x33d4),\
+ PINGROUP(PEX_L2_RST_N, PCC6, PEXCTL, PCIE, HDA, RSVD2, RSVD3, RSVD, INPUT, 0x33d8),\
+ PINGROUP(PEX_L2_CLKREQ_N, PCC7, PEXCTL, PCIE, HDA, RSVD2, RSVD3, RSVD, INPUT, 0x33dc),\
+ PINGROUP(HDMI_CEC, PEE3, SYS, CEC, RSVD1, RSVD2, RSVD3, RSVD, INPUT, 0x33e0),\
+ /* END OF LIST */
+
+const struct tegra_pingroup_desc tegra_soc_pingroups[TEGRA_MAX_PINGROUP] = {
+ PINGROUPS
+};
+
+#undef PINGROUP
+
+#define PINGROUP(pg_name, gpio_nr, vdd, f0, f1, f2, f3, fs, iod, reg) \
+ [TEGRA_GPIO_##gpio_nr] = TEGRA_PINGROUP_ ##pg_name\
+
+const int gpio_to_pingroup[TEGRA_MAX_GPIO] = {
+ PINGROUPS
+};
+
+#ifdef CONFIG_PM_SLEEP
+
+static u32 pinmux_reg[TEGRA_MAX_PINGROUP +
+ ARRAY_SIZE(tegra_soc_drive_pingroups)];
+
+static inline unsigned long pg_readl(unsigned long offset)
+{
+ return readl(IO_TO_VIRT(TEGRA_APB_MISC_BASE + offset));
+}
+
+static inline void pg_writel(unsigned long value, unsigned long offset)
+{
+ writel(value, IO_TO_VIRT(TEGRA_APB_MISC_BASE + offset));
+}
+
+static int tegra_pinmux_suspend(void)
+{
+ unsigned int i;
+ u32 *ctx = pinmux_reg;
+
+ for (i = 0; i < TEGRA_MAX_PINGROUP; i++)
+ *ctx++ = pg_readl(tegra_soc_pingroups[i].mux_reg);
+
+ for (i = 0; i < ARRAY_SIZE(tegra_soc_drive_pingroups); i++)
+ *ctx++ = pg_readl(tegra_soc_drive_pingroups[i].reg);
+
+ return 0;
+}
+
+static void tegra_pinmux_resume(void)
+{
+ unsigned int i;
+ u32 *ctx = pinmux_reg;
+
+ for (i = 0; i < TEGRA_MAX_PINGROUP; i++)
+ pg_writel(*ctx++, tegra_soc_pingroups[i].mux_reg);
+
+ for (i = 0; i < ARRAY_SIZE(tegra_soc_drive_pingroups); i++)
+ pg_writel(*ctx++, tegra_soc_drive_pingroups[i].reg);
+}
+
+static struct syscore_ops tegra_pinmux_syscore_ops = {
+ .suspend = tegra_pinmux_suspend,
+ .resume = tegra_pinmux_resume,
+};
+#endif
+
+#define SET_DRIVE(_name, _hsm, _schmitt, _drive, _pulldn_drive, _pullup_drive, _pulldn_slew, _pullup_slew) \
+ { \
+ .pingroup = TEGRA_DRIVE_PINGROUP_##_name, \
+ .hsm = TEGRA_HSM_##_hsm, \
+ .schmitt = TEGRA_SCHMITT_##_schmitt, \
+ .drive = TEGRA_DRIVE_##_drive, \
+ .pull_down = TEGRA_PULL_##_pulldn_drive, \
+ .pull_up = TEGRA_PULL_##_pullup_drive, \
+ .slew_rising = TEGRA_SLEW_##_pulldn_slew, \
+ .slew_falling = TEGRA_SLEW_##_pullup_slew, \
+ }
+
+static __initdata struct tegra_drive_pingroup_config t30_def_drive_pinmux[] = {
+ SET_DRIVE(DAP2, DISABLE, ENABLE, DIV_1, 31, 31, FASTEST, FASTEST),
+};
+
+void __init tegra_init_pinmux(void)
+{
+#ifdef CONFIG_PM_SLEEP
+ register_syscore_ops(&tegra_pinmux_syscore_ops);
+#endif
+
+ tegra_drive_pinmux_config_table(t30_def_drive_pinmux,
+ ARRAY_SIZE(t30_def_drive_pinmux));
+}
diff --git a/arch/arm/mach-tegra/pinmux.c b/arch/arm/mach-tegra/pinmux.c
index f80d507671bc..4b4ced06c15c 100644
--- a/arch/arm/mach-tegra/pinmux.c
+++ b/arch/arm/mach-tegra/pinmux.c
@@ -2,6 +2,7 @@
* linux/arch/arm/mach-tegra/pinmux.c
*
* Copyright (C) 2010 Google, Inc.
+ * Copyright (C) 2011 NVIDIA Corporation.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@@ -34,6 +35,8 @@
static const struct tegra_pingroup_desc *const pingroups = tegra_soc_pingroups;
static const struct tegra_drive_pingroup_desc *const drive_pingroups = tegra_soc_drive_pingroups;
+static const int *gpio_to_pingroups_map = gpio_to_pingroup;
+
static char *tegra_mux_names[TEGRA_MAX_MUX] = {
[TEGRA_MUX_AHB_CLK] = "AHB_CLK",
@@ -96,6 +99,51 @@ static char *tegra_mux_names[TEGRA_MAX_MUX] = {
[TEGRA_MUX_VI] = "VI",
[TEGRA_MUX_VI_SENSOR_CLK] = "VI_SENSOR_CLK",
[TEGRA_MUX_XIO] = "XIO",
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+ [TEGRA_MUX_BLINK] = "BLINK",
+ [TEGRA_MUX_CEC] = "CEC",
+ [TEGRA_MUX_CLK12] = "CLK12",
+ [TEGRA_MUX_DAP] = "DAP",
+ [TEGRA_MUX_DAPSDMMC2] = "DAPSDMMC2",
+ [TEGRA_MUX_DDR] = "DDR",
+ [TEGRA_MUX_DEV3] = "DEV3",
+ [TEGRA_MUX_DTV] = "DTV",
+ [TEGRA_MUX_VI_ALT1] = "VI_ALT1",
+ [TEGRA_MUX_VI_ALT2] = "VI_ALT2",
+ [TEGRA_MUX_VI_ALT3] = "VI_ALT3",
+ [TEGRA_MUX_EMC_DLL] = "EMC_DLL",
+ [TEGRA_MUX_EXTPERIPH1] = "EXTPERIPH1",
+ [TEGRA_MUX_EXTPERIPH2] = "EXTPERIPH2",
+ [TEGRA_MUX_EXTPERIPH3] = "EXTPERIPH3",
+ [TEGRA_MUX_GMI_ALT] = "GMI_ALT",
+ [TEGRA_MUX_HDA] = "HDA",
+ [TEGRA_MUX_HSI] = "HSI",
+ [TEGRA_MUX_I2C4] = "I2C4",
+ [TEGRA_MUX_I2C5] = "I2C5",
+ [TEGRA_MUX_I2CPWR] = "I2CPWR",
+ [TEGRA_MUX_I2S0] = "I2S0",
+ [TEGRA_MUX_I2S1] = "I2S1",
+ [TEGRA_MUX_I2S2] = "I2S2",
+ [TEGRA_MUX_I2S3] = "I2S3",
+ [TEGRA_MUX_I2S4] = "I2S4",
+ [TEGRA_MUX_NAND_ALT] = "NAND_ALT",
+ [TEGRA_MUX_POPSDIO4] = "POPSDIO4",
+ [TEGRA_MUX_POPSDMMC4] = "POPSDMMC4",
+ [TEGRA_MUX_PWM0] = "PWM0",
+ [TEGRA_MUX_PWM1] = "PWM1",
+ [TEGRA_MUX_PWM2] = "PWM2",
+ [TEGRA_MUX_PWM3] = "PWM3",
+ [TEGRA_MUX_SATA] = "SATA",
+ [TEGRA_MUX_SPI5] = "SPI5",
+ [TEGRA_MUX_SPI6] = "SPI6",
+ [TEGRA_MUX_SYSCLK] = "SYSCLK",
+ [TEGRA_MUX_VGP1] = "VGP1",
+ [TEGRA_MUX_VGP2] = "VGP2",
+ [TEGRA_MUX_VGP3] = "VGP3",
+ [TEGRA_MUX_VGP4] = "VGP4",
+ [TEGRA_MUX_VGP5] = "VGP5",
+ [TEGRA_MUX_VGP6] = "VGP6",
+#endif
[TEGRA_MUX_SAFE] = "<safe>",
};
@@ -140,6 +188,9 @@ static const char *func_name(enum tegra_mux_func func)
if (func == TEGRA_MUX_NONE)
return "NONE";
+ if (func == TEGRA_MUX_INVALID)
+ return "INVALID";
+
if (func < 0 || func >= TEGRA_MAX_MUX)
return "<UNKNOWN>";
@@ -169,21 +220,92 @@ static const char *pupd_name(unsigned long val)
}
}
+#if !defined(CONFIG_ARCH_TEGRA_2x_SOC)
+static const char *lock_name(unsigned long val)
+{
+ switch (val) {
+ case TEGRA_PIN_LOCK_DEFAULT:
+ return "LOCK_DEFUALT";
+
+ case TEGRA_PIN_LOCK_DISABLE:
+ return "LOCK_DISABLE";
+
+ case TEGRA_PIN_LOCK_ENABLE:
+ return "LOCK_ENABLE";
+ default:
+ return "LOCK_DEFAULT";
+ }
+}
+
+static const char *od_name(unsigned long val)
+{
+ switch (val) {
+ case TEGRA_PIN_OD_DEFAULT:
+ return "OD_DEFAULT";
+
+ case TEGRA_PIN_OD_DISABLE:
+ return "OD_DISABLE";
+
+ case TEGRA_PIN_OD_ENABLE:
+ return "OD_ENABLE";
+ default:
+ return "OD_DEFAULT";
+ }
+}
+
+static const char *ioreset_name(unsigned long val)
+{
+ switch (val) {
+ case TEGRA_PIN_IO_RESET_DEFAULT:
+ return "IO_RESET_DEFAULT";
+
+ case TEGRA_PIN_IO_RESET_DISABLE:
+ return "IO_RESET_DISABLE";
+
+ case TEGRA_PIN_IO_RESET_ENABLE:
+ return "IO_RESET_ENABLE";
+ default:
+ return "IO_RESET_DEFAULT";
+ }
+}
+#endif
+
+#if defined(TEGRA_PINMUX_HAS_IO_DIRECTION)
+static const char *io_name(unsigned long val)
+{
+ switch (val) {
+ case 0:
+ return "OUTPUT";
+
+ case 1:
+ return "INPUT";
+
+ default:
+ return "RSVD";
+ }
+}
+#endif
static inline unsigned long pg_readl(unsigned long offset)
{
- return readl(IO_TO_VIRT(TEGRA_APB_MISC_BASE + offset));
+ return readl(IO_TO_VIRT(TEGRA_APB_MISC_BASE) + offset);
}
static inline void pg_writel(unsigned long value, unsigned long offset)
{
- writel(value, IO_TO_VIRT(TEGRA_APB_MISC_BASE + offset));
+ writel(value, IO_TO_VIRT(TEGRA_APB_MISC_BASE) + offset);
+}
+
+int tegra_pinmux_get_pingroup(int gpio_nr)
+{
+ return gpio_to_pingroups_map[gpio_nr];
}
static int tegra_pinmux_set_func(const struct tegra_pingroup_config *config)
{
int mux = -1;
int i;
+ int find = 0;
unsigned long reg;
unsigned long flags;
enum tegra_pingroup pg = config->pingroup;
@@ -192,8 +314,15 @@ static int tegra_pinmux_set_func(const struct tegra_pingroup_config *config)
if (pg < 0 || pg >= TEGRA_MAX_PINGROUP)
return -ERANGE;
- if (pingroups[pg].mux_reg < 0)
+ if (pingroups[pg].mux_reg <= 0)
+ return -EINVAL;
+
+ if (func == TEGRA_MUX_INVALID) {
+ pr_err("The pingroup %s is not recommended for option %s\n",
+ pingroup_name(pg), func_name(func));
+ WARN_ON(1);
return -EINVAL;
+ }
if (func < 0)
return -ERANGE;
@@ -202,24 +331,47 @@ static int tegra_pinmux_set_func(const struct tegra_pingroup_config *config)
func = pingroups[pg].func_safe;
if (func & TEGRA_MUX_RSVD) {
- mux = func & 0x3;
+ for (i = 0; i < 4; i++) {
+ if (pingroups[pg].funcs[i] & TEGRA_MUX_RSVD)
+ mux = i;
+
+ if (pingroups[pg].funcs[i] == func) {
+ mux = i;
+ find = 1;
+ break;
+ }
+ }
} else {
for (i = 0; i < 4; i++) {
if (pingroups[pg].funcs[i] == func) {
mux = i;
+ find = 1;
break;
}
}
}
- if (mux < 0)
+ if (mux < 0) {
+ pr_err("The pingroup %s is not supported option %s\n",
+ pingroup_name(pg), func_name(func));
+ WARN_ON(1);
return -EINVAL;
+ }
+
+ if (!find)
+ pr_warn("The pingroup %s was configured to %s instead of %s\n",
+ pingroup_name(pg), func_name(pingroups[pg].funcs[mux]),
+ func_name(func));
spin_lock_irqsave(&mux_lock, flags);
reg = pg_readl(pingroups[pg].mux_reg);
reg &= ~(0x3 << pingroups[pg].mux_bit);
reg |= mux << pingroups[pg].mux_bit;
+#if defined(TEGRA_PINMUX_HAS_IO_DIRECTION)
+ reg &= ~(0x1 << 5);
+ reg |= ((config->io & 0x1) << 5);
+#endif
pg_writel(reg, pingroups[pg].mux_reg);
spin_unlock_irqrestore(&mux_lock, flags);
@@ -227,6 +379,28 @@ static int tegra_pinmux_set_func(const struct tegra_pingroup_config *config)
return 0;
}
+int tegra_pinmux_get_func(enum tegra_pingroup pg)
+{
+ int mux = -1;
+ unsigned long reg;
+ unsigned long flags;
+
+ if (pg < 0 || pg >= TEGRA_MAX_PINGROUP)
+ return -ERANGE;
+
+ if (pingroups[pg].mux_reg <= 0)
+ return -EINVAL;
+
+ spin_lock_irqsave(&mux_lock, flags);
+
+ reg = pg_readl(pingroups[pg].mux_reg);
+ mux = (reg >> pingroups[pg].mux_bit) & 0x3;
+
+ spin_unlock_irqrestore(&mux_lock, flags);
+
+ return mux;
+}
+
int tegra_pinmux_set_tristate(enum tegra_pingroup pg,
enum tegra_tristate tristate)
{
@@ -236,7 +410,7 @@ int tegra_pinmux_set_tristate(enum tegra_pingroup pg,
if (pg < 0 || pg >= TEGRA_MAX_PINGROUP)
return -ERANGE;
- if (pingroups[pg].tri_reg < 0)
+ if (pingroups[pg].tri_reg <= 0)
return -EINVAL;
spin_lock_irqsave(&mux_lock, flags);
@@ -252,6 +426,94 @@ int tegra_pinmux_set_tristate(enum tegra_pingroup pg,
return 0;
}
+#if !defined(CONFIG_ARCH_TEGRA_2x_SOC)
+static int tegra_pinmux_set_lock(enum tegra_pingroup pg,
+ enum tegra_pin_lock lock)
+{
+ unsigned long reg;
+ unsigned long flags;
+
+ if (pg < 0 || pg >= TEGRA_MAX_PINGROUP)
+ return -ERANGE;
+
+ if (pingroups[pg].mux_reg <= 0)
+ return -EINVAL;
+
+ if ((lock == TEGRA_PIN_LOCK_DEFAULT) || (pingroups[pg].lock_bit < 0))
+ return 0;
+
+ spin_lock_irqsave(&mux_lock, flags);
+
+ reg = pg_readl(pingroups[pg].mux_reg);
+ reg &= ~(0x1 << pingroups[pg].lock_bit);
+ if (lock == TEGRA_PIN_LOCK_ENABLE)
+ reg |= (0x1 << pingroups[pg].lock_bit);
+
+ pg_writel(reg, pingroups[pg].mux_reg);
+
+ spin_unlock_irqrestore(&mux_lock, flags);
+ return 0;
+}
+
+static int tegra_pinmux_set_od(enum tegra_pingroup pg,
+ enum tegra_pin_od od)
+{
+ unsigned long reg;
+ unsigned long flags;
+
+ if (pg < 0 || pg >= TEGRA_MAX_PINGROUP)
+ return -ERANGE;
+
+ if (pingroups[pg].mux_reg <= 0)
+ return -EINVAL;
+
+ if ((od == TEGRA_PIN_OD_DEFAULT) || (pingroups[pg].od_bit < 0))
+ return 0;
+
+ spin_lock_irqsave(&mux_lock, flags);
+
+ reg = pg_readl(pingroups[pg].mux_reg);
+ reg &= ~(0x1 << pingroups[pg].od_bit);
+ if (od == TEGRA_PIN_OD_ENABLE)
+ reg |= 1 << pingroups[pg].od_bit;
+
+ pg_writel(reg, pingroups[pg].mux_reg);
+
+ spin_unlock_irqrestore(&mux_lock, flags);
+
+ return 0;
+}
+
+static int tegra_pinmux_set_ioreset(enum tegra_pingroup pg,
+ enum tegra_pin_ioreset ioreset)
+{
+ unsigned long reg;
+ unsigned long flags;
+
+ if (pg < 0 || pg >= TEGRA_MAX_PINGROUP)
+ return -ERANGE;
+
+ if (pingroups[pg].mux_reg <= 0)
+ return -EINVAL;
+
+ if ((ioreset == TEGRA_PIN_IO_RESET_DEFAULT) || (pingroups[pg].ioreset_bit < 0))
+ return 0;
+
+ spin_lock_irqsave(&mux_lock, flags);
+
+ reg = pg_readl(pingroups[pg].mux_reg);
+ reg &= ~(0x1 << pingroups[pg].ioreset_bit);
+ if (ioreset == TEGRA_PIN_IO_RESET_ENABLE)
+ reg |= 1 << pingroups[pg].ioreset_bit;
+
+ pg_writel(reg, pingroups[pg].mux_reg);
+
+ spin_unlock_irqrestore(&mux_lock, flags);
+
+ return 0;
+}
+#endif
+
int tegra_pinmux_set_pullupdown(enum tegra_pingroup pg,
enum tegra_pullupdown pupd)
{
@@ -261,7 +523,7 @@ int tegra_pinmux_set_pullupdown(enum tegra_pingroup pg,
if (pg < 0 || pg >= TEGRA_MAX_PINGROUP)
return -ERANGE;
- if (pingroups[pg].pupd_reg < 0)
+ if (pingroups[pg].pupd_reg <= 0)
return -EINVAL;
if (pupd != TEGRA_PUPD_NORMAL &&
@@ -288,28 +550,56 @@ static void tegra_pinmux_config_pingroup(const struct tegra_pingroup_config *con
enum tegra_mux_func func = config->func;
enum tegra_pullupdown pupd = config->pupd;
enum tegra_tristate tristate = config->tristate;
+#if !defined(CONFIG_ARCH_TEGRA_2x_SOC)
+ enum tegra_pin_lock lock = config->lock;
+ enum tegra_pin_od od = config->od;
+ enum tegra_pin_ioreset ioreset = config->ioreset;
+#endif
int err;
- if (pingroups[pingroup].mux_reg >= 0) {
+ if (pingroups[pingroup].mux_reg > 0) {
err = tegra_pinmux_set_func(config);
if (err < 0)
pr_err("pinmux: can't set pingroup %s func to %s: %d\n",
pingroup_name(pingroup), func_name(func), err);
}
- if (pingroups[pingroup].pupd_reg >= 0) {
+ if (pingroups[pingroup].pupd_reg > 0) {
err = tegra_pinmux_set_pullupdown(pingroup, pupd);
if (err < 0)
pr_err("pinmux: can't set pingroup %s pullupdown to %s: %d\n",
pingroup_name(pingroup), pupd_name(pupd), err);
}
- if (pingroups[pingroup].tri_reg >= 0) {
+ if (pingroups[pingroup].tri_reg > 0) {
err = tegra_pinmux_set_tristate(pingroup, tristate);
if (err < 0)
pr_err("pinmux: can't set pingroup %s tristate to %s: %d\n",
pingroup_name(pingroup), tri_name(func), err);
}
+
+#if !defined(CONFIG_ARCH_TEGRA_2x_SOC)
+ if (pingroups[pingroup].mux_reg > 0) {
+ err = tegra_pinmux_set_lock(pingroup, lock);
+ if (err < 0)
+ pr_err("pinmux: can't set pingroup %s lock to %s: %d\n",
+ pingroup_name(pingroup), lock_name(func), err);
+ }
+
+ if (pingroups[pingroup].mux_reg > 0) {
+ err = tegra_pinmux_set_od(pingroup, od);
+ if (err < 0)
+ pr_err("pinmux: can't set pingroup %s od to %s: %d\n",
+ pingroup_name(pingroup), od_name(func), err);
+ }
+
+ if (pingroups[pingroup].mux_reg > 0) {
+ err = tegra_pinmux_set_ioreset(pingroup, ioreset);
+ if (err < 0)
+ pr_err("pinmux: can't set pingroup %s ioreset to %s: %d\n",
+ pingroup_name(pingroup), ioreset_name(func), err);
+ }
+#endif
}
void tegra_pinmux_config_table(const struct tegra_pingroup_config *config, int len)
@@ -427,6 +717,7 @@ static int tegra_drive_pinmux_set_pull_down(enum tegra_drive_pingroup pg,
{
unsigned long flags;
u32 reg;
+
if (pg < 0 || pg >= TEGRA_MAX_DRIVE_PINGROUP)
return -ERANGE;
@@ -436,8 +727,9 @@ static int tegra_drive_pinmux_set_pull_down(enum tegra_drive_pingroup pg,
spin_lock_irqsave(&mux_lock, flags);
reg = pg_readl(drive_pingroups[pg].reg);
- reg &= ~(0x1f << 12);
- reg |= pull_down << 12;
+ reg &= ~(drive_pingroups[pg].drvdown_mask <<
+ drive_pingroups[pg].drvdown_offset);
+ reg |= pull_down << drive_pingroups[pg].drvdown_offset;
pg_writel(reg, drive_pingroups[pg].reg);
spin_unlock_irqrestore(&mux_lock, flags);
@@ -450,6 +742,7 @@ static int tegra_drive_pinmux_set_pull_up(enum tegra_drive_pingroup pg,
{
unsigned long flags;
u32 reg;
+
if (pg < 0 || pg >= TEGRA_MAX_DRIVE_PINGROUP)
return -ERANGE;
@@ -459,8 +752,9 @@ static int tegra_drive_pinmux_set_pull_up(enum tegra_drive_pingroup pg,
spin_lock_irqsave(&mux_lock, flags);
reg = pg_readl(drive_pingroups[pg].reg);
- reg &= ~(0x1f << 12);
- reg |= pull_up << 12;
+ reg &= ~(drive_pingroups[pg].drvup_mask <<
+ drive_pingroups[pg].drvup_offset);
+ reg |= pull_up << drive_pingroups[pg].drvup_offset;
pg_writel(reg, drive_pingroups[pg].reg);
spin_unlock_irqrestore(&mux_lock, flags);
@@ -482,8 +776,9 @@ static int tegra_drive_pinmux_set_slew_rising(enum tegra_drive_pingroup pg,
spin_lock_irqsave(&mux_lock, flags);
reg = pg_readl(drive_pingroups[pg].reg);
- reg &= ~(0x3 << 28);
- reg |= slew_rising << 28;
+ reg &= ~(drive_pingroups[pg].slewrise_mask <<
+ drive_pingroups[pg].slewrise_offset);
+ reg |= slew_rising << drive_pingroups[pg].slewrise_offset;
pg_writel(reg, drive_pingroups[pg].reg);
spin_unlock_irqrestore(&mux_lock, flags);
@@ -505,8 +800,9 @@ static int tegra_drive_pinmux_set_slew_falling(enum tegra_drive_pingroup pg,
spin_lock_irqsave(&mux_lock, flags);
reg = pg_readl(drive_pingroups[pg].reg);
- reg &= ~(0x3 << 30);
- reg |= slew_falling << 30;
+ reg &= ~(drive_pingroups[pg].slewfall_mask <<
+ drive_pingroups[pg].slewfall_offset);
+ reg |= slew_falling << drive_pingroups[pg].slewfall_offset;
pg_writel(reg, drive_pingroups[pg].reg);
spin_unlock_irqrestore(&mux_lock, flags);
@@ -636,7 +932,7 @@ void tegra_pinmux_config_tristate_table(const struct tegra_pingroup_config *conf
for (i = 0; i < len; i++) {
pingroup = config[i].pingroup;
- if (pingroups[pingroup].tri_reg >= 0) {
+ if (pingroups[pingroup].tri_reg > 0) {
err = tegra_pinmux_set_tristate(pingroup, tristate);
if (err < 0)
pr_err("pinmux: can't set pingroup %s tristate"
@@ -655,7 +951,7 @@ void tegra_pinmux_config_pullupdown_table(const struct tegra_pingroup_config *co
for (i = 0; i < len; i++) {
pingroup = config[i].pingroup;
- if (pingroups[pingroup].pupd_reg >= 0) {
+ if (pingroups[pingroup].pupd_reg > 0) {
err = tegra_pinmux_set_pullupdown(pingroup, pupd);
if (err < 0)
pr_err("pinmux: can't set pingroup %s pullupdown"
@@ -690,18 +986,23 @@ static int dbg_pinmux_show(struct seq_file *s, void *unused)
seq_printf(s, "\t{TEGRA_PINGROUP_%s", pingroups[i].name);
len = strlen(pingroups[i].name);
- dbg_pad_field(s, 5 - len);
+ dbg_pad_field(s, 15 - len);
- if (pingroups[i].mux_reg < 0) {
+ if (pingroups[i].mux_reg <= 0) {
seq_printf(s, "TEGRA_MUX_NONE");
len = strlen("NONE");
} else {
mux = (pg_readl(pingroups[i].mux_reg) >>
pingroups[i].mux_bit) & 0x3;
- if (pingroups[i].funcs[mux] == TEGRA_MUX_RSVD) {
+ BUG_ON(pingroups[i].funcs[mux] == 0);
+ if (pingroups[i].funcs[mux] == TEGRA_MUX_INVALID) {
+ seq_printf(s, "TEGRA_MUX_INVALID");
+ len = 7;
+ } else if (pingroups[i].funcs[mux] & TEGRA_MUX_RSVD) {
seq_printf(s, "TEGRA_MUX_RSVD%1lu", mux+1);
len = 5;
} else {
+ BUG_ON(!tegra_mux_names[pingroups[i].funcs[mux]]);
seq_printf(s, "TEGRA_MUX_%s",
tegra_mux_names[pingroups[i].funcs[mux]]);
len = strlen(tegra_mux_names[pingroups[i].funcs[mux]]);
@@ -709,7 +1010,16 @@ static int dbg_pinmux_show(struct seq_file *s, void *unused)
}
dbg_pad_field(s, 13-len);
- if (pingroups[i].pupd_reg < 0) {
+#if defined(TEGRA_PINMUX_HAS_IO_DIRECTION)
+ {
+ unsigned long io;
+ io = (pg_readl(pingroups[i].mux_reg) >> 5) & 0x1;
+ seq_printf(s, "TEGRA_PIN_%s", io_name(io));
+ len = strlen(io_name(io));
+ dbg_pad_field(s, 6 - len);
+ }
+#endif
+ if (pingroups[i].pupd_reg <= 0) {
seq_printf(s, "TEGRA_PUPD_NORMAL");
len = strlen("NORMAL");
} else {
@@ -720,7 +1030,7 @@ static int dbg_pinmux_show(struct seq_file *s, void *unused)
}
dbg_pad_field(s, 9 - len);
- if (pingroups[i].tri_reg < 0) {
+ if (pingroups[i].tri_reg <= 0) {
seq_printf(s, "TEGRA_TRI_NORMAL");
} else {
tri = (pg_readl(pingroups[i].tri_reg) >>
diff --git a/arch/arm/mach-tegra/platsmp.c b/arch/arm/mach-tegra/platsmp.c
index 7d2b5d03c1df..582810c96fc2 100644
--- a/arch/arm/mach-tegra/platsmp.c
+++ b/arch/arm/mach-tegra/platsmp.c
@@ -7,103 +7,240 @@
* Copyright (C) 2009 Palm
* All Rights Reserved
*
+ * Copyright (C) 2010-2011 NVIDIA Corporation
+ *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
+
+#include <linux/kernel.h>
#include <linux/init.h>
-#include <linux/errno.h>
-#include <linux/delay.h>
-#include <linux/device.h>
-#include <linux/jiffies.h>
-#include <linux/smp.h>
#include <linux/io.h>
+#include <linux/smp.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/cpumask.h>
-#include <asm/cacheflush.h>
#include <asm/hardware/gic.h>
-#include <asm/mach-types.h>
#include <asm/smp_scu.h>
#include <mach/iomap.h>
+#include <mach/powergate.h>
-extern void tegra_secondary_startup(void);
+#include "pm.h"
+#include "clock.h"
+#include "reset.h"
+#include "sleep.h"
-static DEFINE_SPINLOCK(boot_lock);
-static void __iomem *scu_base = IO_ADDRESS(TEGRA_ARM_PERIF_BASE);
+bool tegra_all_cpus_booted;
+
+static DECLARE_BITMAP(tegra_cpu_init_bits, CONFIG_NR_CPUS) __read_mostly;
+const struct cpumask *const tegra_cpu_init_mask = to_cpumask(tegra_cpu_init_bits);
+#define tegra_cpu_init_map (*(cpumask_t *)tegra_cpu_init_mask)
-#define EVP_CPU_RESET_VECTOR \
- (IO_ADDRESS(TEGRA_EXCEPTION_VECTORS_BASE) + 0x100)
#define CLK_RST_CONTROLLER_CLK_CPU_CMPLX \
(IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x4c)
+#define CLK_RST_CONTROLLER_RST_CPU_CMPLX_SET \
+ (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x340)
#define CLK_RST_CONTROLLER_RST_CPU_CMPLX_CLR \
(IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x344)
-void __cpuinit platform_secondary_init(unsigned int cpu)
+#define CPU_CLOCK(cpu) (0x1<<(8+cpu))
+#define CPU_RESET(cpu) (0x1111ul<<(cpu))
+
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+#define CLK_RST_CONTROLLER_CLK_CPU_CMPLX_CLR \
+ (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x34c)
+#define CAR_BOND_OUT_V \
+ (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x390)
+#define CAR_BOND_OUT_V_CPU_G (1<<0)
+#endif
+
+static void __iomem *scu_base = IO_ADDRESS(TEGRA_ARM_PERIF_BASE);
+
+static unsigned int available_cpus(void)
{
- /*
- * if any interrupts are already enabled for the primary
- * core (e.g. timer irq), then they will not have been enabled
- * for us: do so
- */
- gic_secondary_init(0);
+ static unsigned int ncores;
- /*
- * Synchronise with the boot thread.
- */
- spin_lock(&boot_lock);
- spin_unlock(&boot_lock);
+ if (ncores == 0) {
+ ncores = scu_get_core_count(scu_base);
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+ if (ncores > 1) {
+ u32 fuse_sku = readl(FUSE_SKU_DIRECT_CONFIG);
+ ncores -= FUSE_SKU_NUM_DISABLED_CPUS(fuse_sku);
+ BUG_ON((int)ncores <= 0);
+ }
+#endif
+ }
+ return ncores;
}
-int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle)
+static int is_g_cluster_available(unsigned int cpu)
+{
+#ifdef CONFIG_TEGRA_CLUSTER_CONTROL
+ u32 fuse_sku = readl(FUSE_SKU_DIRECT_CONFIG);
+ u32 bond_out = readl(CAR_BOND_OUT_V);
+
+ /* Does the G CPU complex exist at all? */
+ if ((fuse_sku & FUSE_SKU_DISABLE_ALL_CPUS) ||
+ (bond_out & CAR_BOND_OUT_V_CPU_G))
+ return -EPERM;
+
+ if (cpu >= available_cpus())
+ return -EPERM;
+
+ /* FIXME: The G CPU can be unavailable for a number of reasons
+ * (e.g., low battery, over temperature, etc.). Add checks for
+ * these conditions. */
+ return 0;
+#else
+ return -EPERM;
+#endif
+}
+
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+static bool is_cpu_powered(unsigned int cpu)
+{
+ if (is_lp_cluster())
+ return true;
+ else
+ return tegra_powergate_is_powered(TEGRA_CPU_POWERGATE_ID(cpu));
+}
+#endif
+
+static int power_up_cpu(unsigned int cpu)
{
- unsigned long old_boot_vector;
- unsigned long boot_vector;
- unsigned long timeout;
u32 reg;
+ int ret = 0;
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+ unsigned long timeout;
+
+ BUG_ON(cpu == smp_processor_id());
+ BUG_ON(is_lp_cluster());
- /*
- * set synchronisation state between this boot processor
- * and the secondary one
+ /* If this cpu has booted this function is entered after
+ * CPU has been already un-gated by flow controller. Wait
+ * for confirmation that cpu is powered and remove clamps.
+ * On first boot entry do not wait - go to direct ungate.
*/
- spin_lock(&boot_lock);
+ if (cpu_isset(cpu, tegra_cpu_init_map)) {
+ timeout = jiffies + 5;
+ do {
+ if (is_cpu_powered(cpu))
+ goto remove_clamps;
+ udelay(10);
+ } while (time_before(jiffies, timeout));
+ }
+ /* First boot or Flow controller did not work as expected. Try to
+ directly toggle power gates. Error if direct power on also fails. */
+ if (!is_cpu_powered(cpu)) {
+ ret = tegra_unpowergate_partition(TEGRA_CPU_POWERGATE_ID(cpu));
+ if (ret)
+ goto fail;
- /* set the reset vector to point to the secondary_startup routine */
+ /* Wait for the power to come up. */
+ timeout = jiffies + 10*HZ;
- boot_vector = virt_to_phys(tegra_secondary_startup);
- old_boot_vector = readl(EVP_CPU_RESET_VECTOR);
- writel(boot_vector, EVP_CPU_RESET_VECTOR);
+ do {
+ if (is_cpu_powered(cpu))
+ goto remove_clamps;
+ udelay(10);
+ } while (time_before(jiffies, timeout));
+ ret = -ETIMEDOUT;
+ goto fail;
+ }
- /* enable cpu clock on cpu1 */
+remove_clamps:
+ /* CPU partition is powered. Enable the CPU clock. */
+ writel(CPU_CLOCK(cpu), CLK_RST_CONTROLLER_CLK_CPU_CMPLX_CLR);
+ reg = readl(CLK_RST_CONTROLLER_CLK_CPU_CMPLX_CLR);
+ udelay(10);
+
+ /* Remove I/O clamps. */
+ ret = tegra_powergate_remove_clamping(TEGRA_CPU_POWERGATE_ID(cpu));
+ udelay(10);
+fail:
+#else
+ /* Enable the CPU clock. */
+ reg = readl(CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
+ writel(reg & ~CPU_CLOCK(cpu), CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
+ barrier();
reg = readl(CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
- writel(reg & ~(1<<9), CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
+#endif
+ /* Clear flow controller CSR. */
+ flowctrl_writel(0, FLOW_CTRL_CPU_CSR(cpu));
+ return ret;
+}
- reg = (1<<13) | (1<<9) | (1<<5) | (1<<1);
- writel(reg, CLK_RST_CONTROLLER_RST_CPU_CMPLX_CLR);
+void __cpuinit platform_secondary_init(unsigned int cpu)
+{
+ gic_secondary_init(0);
- smp_wmb();
- flush_cache_all();
+ cpumask_set_cpu(cpu, to_cpumask(tegra_cpu_init_bits));
+ if (!tegra_all_cpus_booted)
+ if (cpumask_equal(tegra_cpu_init_mask, cpu_present_mask))
+ tegra_all_cpus_booted = true;
+}
- /* unhalt the cpu */
- writel(0, IO_ADDRESS(TEGRA_FLOW_CTRL_BASE) + 0x14);
+int boot_secondary(unsigned int cpu, struct task_struct *idle)
+{
+ int status;
- timeout = jiffies + (1 * HZ);
- while (time_before(jiffies, timeout)) {
- if (readl(EVP_CPU_RESET_VECTOR) != boot_vector)
- break;
- udelay(10);
+ /* Avoid timer calibration on slave cpus. Use the value calibrated
+ * on master cpu. This reduces the bringup time for each slave cpu
+ * by around 260ms.
+ */
+ preset_lpj = loops_per_jiffy;
+ if (is_lp_cluster()) {
+ struct clk *cpu_clk, *cpu_g_clk;
+
+ /* The G CPU may not be available for a variety of reasons. */
+ status = is_g_cluster_available(cpu);
+ if (status)
+ goto done;
+
+ cpu_clk = tegra_get_clock_by_name("cpu");
+ cpu_g_clk = tegra_get_clock_by_name("cpu_g");
+
+ /* Switch to G CPU before continuing. */
+ if (!cpu_clk || !cpu_g_clk) {
+ /* Early boot, clock infrastructure is not initialized
+ - CPU mode switch is not allowed */
+ status = -EINVAL;
+ } else
+ status = clk_set_parent(cpu_clk, cpu_g_clk);
+
+ if (status)
+ goto done;
}
- /* put the old boot vector back */
- writel(old_boot_vector, EVP_CPU_RESET_VECTOR);
+ smp_wmb();
- /*
- * now the secondary core is starting up let it run its
- * calibrations, then wait for it to finish
- */
- spin_unlock(&boot_lock);
+ /* Force the CPU into reset. The CPU must remain in reset when the
+ flow controller state is cleared (which will cause the flow
+ controller to stop driving reset if the CPU has been power-gated
+ via the flow controller). This will have no effect on first boot
+ of the CPU since it should already be in reset. */
+ writel(CPU_RESET(cpu), CLK_RST_CONTROLLER_RST_CPU_CMPLX_SET);
+ dmb();
- return 0;
+ /* Unhalt the CPU. If the flow controller was used to power-gate the
+ CPU this will cause the flow controller to stop driving reset.
+ The CPU will remain in reset because the clock and reset block
+ is now driving reset. */
+ flowctrl_writel(0, FLOW_CTRL_HALT_CPU(cpu));
+
+ status = power_up_cpu(cpu);
+ if (status)
+ goto done;
+
+ /* Take the CPU out of reset. */
+ writel(CPU_RESET(cpu), CLK_RST_CONTROLLER_RST_CPU_CMPLX_CLR);
+ wmb();
+done:
+ return status;
}
/*
@@ -112,7 +249,8 @@ int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle)
*/
void __init smp_init_cpus(void)
{
- unsigned int i, ncores = scu_get_core_count(scu_base);
+ unsigned int ncores = available_cpus();
+ unsigned int i;
if (ncores > nr_cpu_ids) {
pr_warn("SMP: %u cores greater than maximum (%u), clipping\n",
@@ -123,11 +261,31 @@ void __init smp_init_cpus(void)
for (i = 0; i < ncores; i++)
set_cpu_possible(i, true);
+ /* If only one CPU is possible, platform_smp_prepare_cpus() will
+ never get called. We must therefore initialize the reset handler
+ here. If there is more than one CPU, we must wait until after
+ the cpu_present_mask has been updated with all present CPUs in
+ platform_smp_prepare_cpus() before initializing the reset handler. */
+ if (ncores == 1) {
+ tegra_cpu_reset_handler_init();
+ tegra_all_cpus_booted = true;
+ }
+
set_smp_cross_call(gic_raise_softirq);
}
void __init platform_smp_prepare_cpus(unsigned int max_cpus)
{
+ /* Always mark the boot CPU as initialized. */
+ cpumask_set_cpu(0, to_cpumask(tegra_cpu_init_bits));
+
+ if (max_cpus == 1)
+ tegra_all_cpus_booted = true;
+
+ /* If we're here, it means that more than one CPU was found by
+ smp_init_cpus() which also means that it did not initialize the
+ reset handler. Do it now before the secondary CPUs are started. */
+ tegra_cpu_reset_handler_init();
scu_enable(scu_base);
}
diff --git a/arch/arm/mach-tegra/pm-irq.c b/arch/arm/mach-tegra/pm-irq.c
new file mode 100644
index 000000000000..d66e2cba8546
--- /dev/null
+++ b/arch/arm/mach-tegra/pm-irq.c
@@ -0,0 +1,362 @@
+/*
+ * Copyright (C) 2011 Google, Inc.
+ *
+ * Author:
+ * Colin Cross <ccross@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/kernel.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+#include <linux/moduleparam.h>
+#include <linux/seq_file.h>
+#include <linux/syscore_ops.h>
+
+#include <mach/iomap.h>
+
+#include "pm-irq.h"
+
+#define PMC_CTRL 0x0
+#define PMC_CTRL_LATCH_WAKEUPS (1 << 5)
+#define PMC_WAKE_MASK 0xc
+#define PMC_WAKE_LEVEL 0x10
+#define PMC_WAKE_STATUS 0x14
+#define PMC_SW_WAKE_STATUS 0x18
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+#define PMC_WAKE2_MASK 0x160
+#define PMC_WAKE2_LEVEL 0x164
+#define PMC_WAKE2_STATUS 0x168
+#define PMC_SW_WAKE2_STATUS 0x16C
+#endif
+
+#define PMC_MAX_WAKE_COUNT 64
+
+static void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE);
+
+static u64 tegra_lp0_wake_enb;
+static u64 tegra_lp0_wake_level;
+static u64 tegra_lp0_wake_level_any;
+static int tegra_prevent_lp0;
+
+static unsigned int tegra_wake_irq_count[PMC_MAX_WAKE_COUNT];
+
+static bool debug_lp0;
+module_param(debug_lp0, bool, S_IRUGO | S_IWUSR);
+
+static bool warn_prevent_lp0;
+module_param(warn_prevent_lp0, bool, S_IRUGO | S_IWUSR);
+
+bool tegra_pm_irq_lp0_allowed(void)
+{
+ return (tegra_prevent_lp0 == 0);
+}
+
+/* ensures that sufficient time is passed for a register write to
+ * serialize into the 32KHz domain */
+static void pmc_32kwritel(u32 val, unsigned long offs)
+{
+ writel(val, pmc + offs);
+ udelay(130);
+}
+
+static inline void write_pmc_wake_mask(u64 value)
+{
+ pr_info("Wake[31-0] enable=0x%x\n", (u32)(value & 0xFFFFFFFF));
+ writel((u32)value, pmc + PMC_WAKE_MASK);
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+ pr_info("Tegra3 wake[63-32] enable=0x%x\n", (u32)((value >> 32) &
+ 0xFFFFFFFF));
+ __raw_writel((u32)(value >> 32), pmc + PMC_WAKE2_MASK);
+#endif
+}
+
+static inline u64 read_pmc_wake_level(void)
+{
+ u64 reg;
+
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+ reg = readl(pmc + PMC_WAKE_LEVEL);
+#else
+ reg = __raw_readl(pmc + PMC_WAKE_LEVEL);
+ reg |= ((u64)readl(pmc + PMC_WAKE2_LEVEL)) << 32;
+#endif
+ return reg;
+}
+
+static inline void write_pmc_wake_level(u64 value)
+{
+ writel((u32)value, pmc + PMC_WAKE_LEVEL);
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+ __raw_writel((u32)(value >> 32), pmc + PMC_WAKE2_LEVEL);
+#endif
+}
+
+static inline u64 read_pmc_wake_status(void)
+{
+ u64 reg;
+
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+ reg = readl(pmc + PMC_WAKE_STATUS);
+#else
+ reg = __raw_readl(pmc + PMC_WAKE_STATUS);
+ reg |= ((u64)readl(pmc + PMC_WAKE2_STATUS)) << 32;
+#endif
+ return reg;
+}
+
+static inline u64 read_pmc_sw_wake_status(void)
+{
+ u64 reg;
+
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+ reg = readl(pmc + PMC_SW_WAKE_STATUS);
+#else
+ reg = __raw_readl(pmc + PMC_SW_WAKE_STATUS);
+ reg |= ((u64)readl(pmc + PMC_SW_WAKE2_STATUS)) << 32;
+#endif
+ return reg;
+}
+
+static inline void clear_pmc_sw_wake_status(void)
+{
+ pmc_32kwritel(0, PMC_SW_WAKE_STATUS);
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+ pmc_32kwritel(0, PMC_SW_WAKE2_STATUS);
+#endif
+}
+
+int tegra_pm_irq_set_wake(int irq, int enable)
+{
+ int wake = tegra_irq_to_wake(irq);
+
+ if (wake == -EALREADY) {
+ /* EALREADY means wakeup event already accounted for */
+ return 0;
+ } else if (wake == -ENOTSUPP) {
+ /* ENOTSUPP means LP0 not supported with this wake source */
+ WARN(enable && warn_prevent_lp0, "irq %d prevents lp0\n", irq);
+ if (enable)
+ tegra_prevent_lp0++;
+ else if (!WARN_ON(tegra_prevent_lp0 == 0))
+ tegra_prevent_lp0--;
+ return 0;
+ } else if (wake < 0) {
+ return -EINVAL;
+ }
+
+ if (enable) {
+ tegra_lp0_wake_enb |= 1ull << wake;
+ pr_info("Enabling wake%d\n", wake);
+ } else {
+ tegra_lp0_wake_enb &= ~(1ull << wake);
+ pr_info("Disabling wake%d\n", wake);
+ }
+
+ return 0;
+}
+
+int tegra_pm_irq_set_wake_type(int irq, int flow_type)
+{
+ int wake = tegra_irq_to_wake(irq);
+
+ if (wake < 0)
+ return 0;
+
+ switch (flow_type) {
+ case IRQF_TRIGGER_FALLING:
+ case IRQF_TRIGGER_LOW:
+ tegra_lp0_wake_level &= ~(1 << wake);
+ tegra_lp0_wake_level_any &= ~(1 << wake);
+ break;
+ case IRQF_TRIGGER_HIGH:
+ case IRQF_TRIGGER_RISING:
+ tegra_lp0_wake_level |= 1 << wake;
+ tegra_lp0_wake_level_any &= ~(1 << wake);
+ break;
+
+ case IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING:
+ tegra_lp0_wake_level_any |= 1 << wake;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* translate lp0 wake sources back into irqs to catch edge triggered wakeups */
+static void tegra_pm_irq_syscore_resume_helper(
+ unsigned long wake_status,
+ unsigned int index)
+{
+ int wake;
+ int irq;
+ struct irq_desc *desc;
+
+ for_each_set_bit(wake, &wake_status, sizeof(wake_status) * 8) {
+ irq = tegra_wake_to_irq(wake + 32 * index);
+ if (!irq) {
+ pr_info("Resume caused by WAKE%d\n",
+ (wake + 32 * index));
+ continue;
+ }
+
+ desc = irq_to_desc(irq);
+ if (!desc || !desc->action || !desc->action->name) {
+ pr_info("Resume caused by WAKE%d, irq %d\n",
+ (wake + 32 * index), irq);
+ continue;
+ }
+
+ pr_info("Resume caused by WAKE%d, %s\n", (wake + 32 * index),
+ desc->action->name);
+
+ tegra_wake_irq_count[wake + 32 * index]++;
+
+ generic_handle_irq(irq);
+ }
+}
+
+static void tegra_pm_irq_syscore_resume(void)
+{
+ unsigned long long wake_status = read_pmc_wake_status();
+
+ pr_info(" legacy wake status=0x%x\n", (u32)wake_status);
+ tegra_pm_irq_syscore_resume_helper((unsigned long)wake_status, 0);
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+ pr_info(" tegra3 wake status=0x%x\n", (u32)(wake_status >> 32));
+ tegra_pm_irq_syscore_resume_helper(
+ (unsigned long)(wake_status >> 32), 1);
+#endif
+}
+
+/* set up lp0 wake sources */
+static int tegra_pm_irq_syscore_suspend(void)
+{
+ u32 temp;
+ u64 status;
+ u64 lvl;
+ u64 wake_level;
+ u64 wake_enb;
+ static bool is_first = true;
+
+ clear_pmc_sw_wake_status();
+
+ temp = readl(pmc + PMC_CTRL);
+ temp |= PMC_CTRL_LATCH_WAKEUPS;
+ pmc_32kwritel(temp, PMC_CTRL);
+
+ temp &= ~PMC_CTRL_LATCH_WAKEUPS;
+ pmc_32kwritel(temp, PMC_CTRL);
+
+ status = read_pmc_sw_wake_status();
+
+ lvl = read_pmc_wake_level();
+
+ /* flip the wakeup trigger for any-edge triggered pads
+ * which are currently asserting as wakeups */
+ if (is_first)
+ is_first = false;
+ else
+ lvl ^= status;
+
+ lvl &= tegra_lp0_wake_level_any;
+
+ wake_level = lvl | tegra_lp0_wake_level;
+ wake_enb = tegra_lp0_wake_enb;
+
+ if (debug_lp0) {
+ wake_level = lvl ^ status;
+ wake_enb = 0xffffffff;
+ }
+
+ write_pmc_wake_level(wake_level);
+
+ write_pmc_wake_mask(wake_enb);
+
+ return 0;
+}
+
+static struct syscore_ops tegra_pm_irq_syscore_ops = {
+ .suspend = tegra_pm_irq_syscore_suspend,
+ .resume = tegra_pm_irq_syscore_resume,
+};
+
+static int tegra_pm_irq_syscore_init(void)
+{
+ register_syscore_ops(&tegra_pm_irq_syscore_ops);
+
+ return 0;
+}
+subsys_initcall(tegra_pm_irq_syscore_init);
+
+#ifdef CONFIG_DEBUG_FS
+static int tegra_pm_irq_debug_show(struct seq_file *s, void *data)
+{
+ int wake;
+ int irq;
+ struct irq_desc *desc;
+ const char *irq_name;
+
+ seq_printf(s, "wake irq count name\n");
+ seq_printf(s, "----------------------\n");
+ for (wake = 0; wake < PMC_MAX_WAKE_COUNT; wake++) {
+ irq = tegra_wake_to_irq(wake);
+ if (irq < 0)
+ continue;
+
+ desc = irq_to_desc(irq);
+ if (tegra_wake_irq_count[wake] == 0 && desc->action == NULL)
+ continue;
+
+ irq_name = (desc->action && desc->action->name) ?
+ desc->action->name : "???";
+
+ seq_printf(s, "%4d %3d %5d %s\n",
+ wake, irq, tegra_wake_irq_count[wake], irq_name);
+ }
+ return 0;
+}
+
+static int tegra_pm_irq_debug_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, tegra_pm_irq_debug_show, NULL);
+}
+
+static const struct file_operations tegra_pm_irq_debug_fops = {
+ .open = tegra_pm_irq_debug_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int __init tegra_pm_irq_debug_init(void)
+{
+ struct dentry *d;
+
+ d = debugfs_create_file("wake_irq", S_IRUGO, NULL, NULL,
+ &tegra_pm_irq_debug_fops);
+ if (!d) {
+ pr_err("Failed to create suspend_mode debug file\n");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+late_initcall(tegra_pm_irq_debug_init);
+#endif
diff --git a/arch/arm/mach-tegra/pm-irq.h b/arch/arm/mach-tegra/pm-irq.h
new file mode 100644
index 000000000000..8e87b4bba246
--- /dev/null
+++ b/arch/arm/mach-tegra/pm-irq.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2011 Google, Inc.
+ *
+ * Author:
+ * Colin Cross <ccross@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 _MACH_TERA_PM_IRQ_H_
+#define _MACH_TERA_PM_IRQ_H_
+
+#ifdef CONFIG_PM_SLEEP
+int tegra_pm_irq_set_wake(int irq, int enable);
+int tegra_pm_irq_set_wake_type(int irq, int flow_type);
+bool tegra_pm_irq_lp0_allowed(void);
+int tegra_irq_to_wake(int irq);
+int tegra_wake_to_irq(int wake);
+#else
+static inline int tegra_pm_irq_set_wake_type(int irq, int flow_type)
+{
+ return 0;
+}
+#endif
+#endif
diff --git a/arch/arm/mach-tegra/pm-t2.c b/arch/arm/mach-tegra/pm-t2.c
new file mode 100644
index 000000000000..0fbc433c2773
--- /dev/null
+++ b/arch/arm/mach-tegra/pm-t2.c
@@ -0,0 +1,355 @@
+/*
+ * arch/arm/mach-tegra/pm-t2.c
+ *
+ * Tegra 2 LP0 scratch register preservation
+ *
+ * Copyright (c) 2009-2011, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/io.h>
+
+#include <mach/iomap.h>
+#include <mach/irqs.h>
+
+#define PMC_SCRATCH3 0x5c
+#define PMC_SCRATCH5 0x64
+#define PMC_SCRATCH6 0x68
+#define PMC_SCRATCH7 0x6c
+#define PMC_SCRATCH8 0x70
+#define PMC_SCRATCH9 0x74
+#define PMC_SCRATCH10 0x78
+#define PMC_SCRATCH11 0x7c
+#define PMC_SCRATCH12 0x80
+#define PMC_SCRATCH13 0x84
+#define PMC_SCRATCH14 0x88
+#define PMC_SCRATCH15 0x8c
+#define PMC_SCRATCH16 0x90
+#define PMC_SCRATCH17 0x94
+#define PMC_SCRATCH18 0x98
+#define PMC_SCRATCH19 0x9c
+#define PMC_SCRATCH20 0xa0
+#define PMC_SCRATCH21 0xa4
+#define PMC_SCRATCH22 0xa8
+#define PMC_SCRATCH23 0xac
+#define PMC_SCRATCH25 0x100
+#define PMC_SCRATCH35 0x128
+#define PMC_SCRATCH36 0x12c
+#define PMC_SCRATCH40 0x13c
+
+struct pmc_scratch_field {
+ unsigned long addr;
+ unsigned int mask;
+ int shift_src;
+ int shift_dst;
+};
+
+#define field(reg, start, end, dst) \
+ { \
+ .addr = (reg), \
+ .mask = 0xfffffffful >> (31 - ((end) - (start))), \
+ .shift_src = (start), \
+ .shift_dst = (dst), \
+ }
+
+static const struct pmc_scratch_field pllx[] __initdata = {
+ field(TEGRA_CLK_RESET_BASE + 0xe0, 20, 22, 15), /* PLLX_DIVP */
+ field(TEGRA_CLK_RESET_BASE + 0xe0, 8, 17, 5), /* PLLX_DIVN */
+ field(TEGRA_CLK_RESET_BASE + 0xe0, 0, 4, 0), /* PLLX_DIVM */
+ field(TEGRA_CLK_RESET_BASE + 0xe4, 8, 11, 22), /* PLLX_CPCON */
+ field(TEGRA_CLK_RESET_BASE + 0xe4, 4, 7, 18), /* PLLX_LFCON */
+ field(TEGRA_APB_MISC_BASE + 0x8e4, 24, 27, 27), /* XM2CFGC_VREF_DQ */
+ field(TEGRA_APB_MISC_BASE + 0x8c8, 3, 3, 26), /* XM2CFGC_SCHMT_EN */
+ field(TEGRA_APB_MISC_BASE + 0x8d0, 2, 2, 31), /* XM2CLKCFG_PREEMP_EN */
+};
+
+static const struct pmc_scratch_field emc_0[] __initdata = {
+ field(TEGRA_EMC_BASE + 0x3c, 0, 4, 27), /* R2W */
+ field(TEGRA_EMC_BASE + 0x34, 0, 5, 15), /* RAS */
+ field(TEGRA_EMC_BASE + 0x2c, 0, 5, 0), /* RC */
+ field(TEGRA_EMC_BASE + 0x30, 0, 8, 6), /* RFC */
+ field(TEGRA_EMC_BASE + 0x38, 0, 5, 21), /* RP */
+};
+
+static const struct pmc_scratch_field emc_1[] __initdata = {
+ field(TEGRA_EMC_BASE + 0x44, 0, 4, 5), /* R2P */
+ field(TEGRA_EMC_BASE + 0x4c, 0, 5, 15), /* RD_RCD */
+ field(TEGRA_EMC_BASE + 0x54, 0, 3, 27), /* RRD */
+ field(TEGRA_EMC_BASE + 0x48, 0, 4, 10), /* W2P */
+ field(TEGRA_EMC_BASE + 0x40, 0, 4, 0), /* W2R */
+ field(TEGRA_EMC_BASE + 0x50, 0, 5, 21), /* WR_RCD */
+};
+
+static const struct pmc_scratch_field emc_2[] __initdata = {
+ field(TEGRA_EMC_BASE + 0x2b8, 2, 2, 31), /* CLKCHANGE_SR_ENABLE */
+ field(TEGRA_EMC_BASE + 0x2b8, 10, 10, 30), /* USE_ADDR_CLK */
+ field(TEGRA_EMC_BASE + 0x80, 0, 4, 25), /* PCHG2PDEN */
+ field(TEGRA_EMC_BASE + 0x64, 0, 3, 12), /* QRST */
+ field(TEGRA_EMC_BASE + 0x68, 0, 3, 16), /* QSAFE */
+ field(TEGRA_EMC_BASE + 0x60, 0, 3, 8), /* QUSE */
+ field(TEGRA_EMC_BASE + 0x6c, 0, 4, 20), /* RDV */
+ field(TEGRA_EMC_BASE + 0x58, 0, 3, 0), /* REXT */
+ field(TEGRA_EMC_BASE + 0x5c, 0, 3, 4), /* WDV */
+};
+
+static const struct pmc_scratch_field emc_3[] __initdata = {
+ field(TEGRA_EMC_BASE + 0x74, 0, 3, 16), /* BURST_REFRESH_NUM */
+ field(TEGRA_EMC_BASE + 0x7c, 0, 3, 24), /* PDEX2RD */
+ field(TEGRA_EMC_BASE + 0x78, 0, 3, 20), /* PDEX2WR */
+ field(TEGRA_EMC_BASE + 0x70, 0, 4, 0), /* REFRESH_LO */
+ field(TEGRA_EMC_BASE + 0x70, 5, 15, 5), /* REFRESH */
+ field(TEGRA_EMC_BASE + 0xa0, 0, 3, 28), /* TCLKSTABLE */
+};
+
+static const struct pmc_scratch_field emc_4[] __initdata = {
+ field(TEGRA_EMC_BASE + 0x84, 0, 4, 0), /* ACT2PDEN */
+ field(TEGRA_EMC_BASE + 0x88, 0, 4, 5), /* AR2PDEN */
+ field(TEGRA_EMC_BASE + 0x8c, 0, 5, 10), /* RW2PDEN */
+ field(TEGRA_EMC_BASE + 0x94, 0, 3, 28), /* TCKE */
+ field(TEGRA_EMC_BASE + 0x90, 0, 11, 16), /* TXSR */
+};
+
+static const struct pmc_scratch_field emc_5[] __initdata = {
+ field(TEGRA_EMC_BASE + 0x8, 10, 10, 30), /* AP_REQ_BUSY_CTRL */
+ field(TEGRA_EMC_BASE + 0x8, 24, 24, 31), /* CFG_PRIORITY */
+ field(TEGRA_EMC_BASE + 0x8, 2, 2, 26), /* FORCE_UPDATE */
+ field(TEGRA_EMC_BASE + 0x8, 4, 4, 27), /* MRS_WAIT */
+ field(TEGRA_EMC_BASE + 0x8, 5, 5, 28), /* PERIODIC_QRST */
+ field(TEGRA_EMC_BASE + 0x8, 9, 9, 29), /* READ_DQM_CTRL */
+ field(TEGRA_EMC_BASE + 0x8, 0, 0, 24), /* READ_MUX */
+ field(TEGRA_EMC_BASE + 0x8, 1, 1, 25), /* WRITE_MUX */
+ field(TEGRA_EMC_BASE + 0xa4, 0, 3, 6), /* TCLKSTOP */
+ field(TEGRA_EMC_BASE + 0xa8, 0, 13, 10), /* TREFBW */
+ field(TEGRA_EMC_BASE + 0x9c, 0, 5, 0), /* TRPAB */
+};
+
+static const struct pmc_scratch_field emc_6[] __initdata = {
+ field(TEGRA_EMC_BASE + 0xfc, 0, 1, 0), /* DQSIB_DLY_MSB_BYTE_0 */
+ field(TEGRA_EMC_BASE + 0xfc, 8, 9, 2), /* DQSIB_DLY_MSB_BYTE_1 */
+ field(TEGRA_EMC_BASE + 0xfc, 16, 17, 4), /* DQSIB_DLY_MSB_BYTE_2 */
+ field(TEGRA_EMC_BASE + 0xfc, 24, 25, 6), /* DQSIB_DLY_MSB_BYTE_3 */
+ field(TEGRA_EMC_BASE + 0x110, 0, 1, 8), /* QUSE_DLY_MSB_BYTE_0 */
+ field(TEGRA_EMC_BASE + 0x110, 8, 9, 10), /* QUSE_DLY_MSB_BYTE_1 */
+ field(TEGRA_EMC_BASE + 0x110, 16, 17, 12), /* QUSE_DLY_MSB_BYTE_2 */
+ field(TEGRA_EMC_BASE + 0x110, 24, 25, 14), /* QUSE_DLY_MSB_BYTE_3 */
+ field(TEGRA_EMC_BASE + 0xac, 0, 3, 22), /* QUSE_EXTRA */
+ field(TEGRA_EMC_BASE + 0x98, 0, 5, 16), /* TFAW */
+ field(TEGRA_APB_MISC_BASE + 0x8e4, 5, 5, 30), /* XM2CFGC_VREF_DQ_EN */
+ field(TEGRA_APB_MISC_BASE + 0x8e4, 16, 19, 26), /* XM2CFGC_VREF_DQS */
+};
+
+static const struct pmc_scratch_field emc_dqsib_dly[] __initdata = {
+ field(TEGRA_EMC_BASE + 0xf8, 0, 31, 0), /* DQSIB_DLY_BYTE_0 - DQSIB_DLY_BYTE_3*/
+};
+
+static const struct pmc_scratch_field emc_quse_dly[] __initdata = {
+ field(TEGRA_EMC_BASE + 0x10c, 0, 31, 0), /* QUSE_DLY_BYTE_0 - QUSE_DLY_BYTE_3*/
+};
+
+static const struct pmc_scratch_field emc_clktrim[] __initdata = {
+ field(TEGRA_EMC_BASE + 0x2d0, 0, 29, 0), /* DATA0_CLKTRIM - DATA3_CLKTRIM +
+ * MCLK_ADDR_CLKTRIM */
+};
+
+static const struct pmc_scratch_field emc_autocal_fbio[] __initdata = {
+ field(TEGRA_EMC_BASE + 0x2a4, 29, 29, 29), /* AUTO_CAL_ENABLE */
+ field(TEGRA_EMC_BASE + 0x2a4, 30, 30, 30), /* AUTO_CAL_OVERRIDE */
+ field(TEGRA_EMC_BASE + 0x2a4, 8, 12, 14), /* AUTO_CAL_PD_OFFSET */
+ field(TEGRA_EMC_BASE + 0x2a4, 0, 4, 9), /* AUTO_CAL_PU_OFFSET */
+ field(TEGRA_EMC_BASE + 0x2a4, 16, 25, 19), /* AUTO_CAL_STEP */
+ field(TEGRA_EMC_BASE + 0xf4, 16, 16, 0), /* CFG_DEN_EARLY */
+ field(TEGRA_EMC_BASE + 0x104, 8, 8, 8), /* CTT_TERMINATION */
+ field(TEGRA_EMC_BASE + 0x104, 7, 7, 7), /* DIFFERENTIAL_DQS */
+ field(TEGRA_EMC_BASE + 0x104, 9, 9, 31), /* DQS_PULLD */
+ field(TEGRA_EMC_BASE + 0x104, 0, 1, 4), /* DRAM_TYPE */
+ field(TEGRA_EMC_BASE + 0x104, 4, 4, 6), /* DRAM_WIDTH */
+ field(TEGRA_EMC_BASE + 0x114, 0, 2, 1), /* CFG_QUSE_LATE */
+};
+
+static const struct pmc_scratch_field emc_autocal_interval[] __initdata = {
+ field(TEGRA_EMC_BASE + 0x2a8, 0, 27, 0), /* AUTOCAL_INTERVAL */
+ field(TEGRA_EMC_BASE + 0x2b8, 1, 1, 29), /* CLKCHANGE_PD_ENABLE */
+ field(TEGRA_EMC_BASE + 0x2b8, 0, 0, 28), /* CLKCHANGE_REQ_ENABLE */
+ field(TEGRA_EMC_BASE + 0x2b8, 8, 9, 30), /* PIN_CONFIG */
+};
+
+static const struct pmc_scratch_field emc_cfgs[] __initdata = {
+ field(TEGRA_EMC_BASE + 0x10, 8, 9, 3), /* EMEM_BANKWIDTH */
+ field(TEGRA_EMC_BASE + 0x10, 0, 2, 0), /* EMEM_COLWIDTH */
+ field(TEGRA_EMC_BASE + 0x10, 16, 19, 5), /* EMEM_DEVSIZE */
+ field(TEGRA_EMC_BASE + 0x10, 24, 25, 9), /* EMEM_NUMDEV */
+ field(TEGRA_EMC_BASE + 0xc, 24, 24, 21), /* AUTO_PRE_RD */
+ field(TEGRA_EMC_BASE + 0xc, 25, 25, 22), /* AUTO_PRE_WR */
+ field(TEGRA_EMC_BASE + 0xc, 16, 16, 20), /* CLEAR_AP_PREV_SPREQ */
+ field(TEGRA_EMC_BASE + 0xc, 29, 29, 23), /* DRAM_ACPD */
+ field(TEGRA_EMC_BASE + 0xc, 30, 30, 24), /* DRAM_CLKSTOP_PDSR_ONLY */
+ field(TEGRA_EMC_BASE + 0xc, 31, 31, 25), /* DRAM_CLKSTOP */
+ field(TEGRA_EMC_BASE + 0xc, 8, 15, 12), /* PRE_IDLE_CYCLES */
+ field(TEGRA_EMC_BASE + 0xc, 0, 0, 11), /* PRE_IDLE_EN */
+ field(TEGRA_EMC_BASE + 0x2bc, 28, 29, 28), /* CFG_DLL_LOCK_LIMIT */
+ field(TEGRA_EMC_BASE + 0x2bc, 6, 7, 30), /* CFG_DLL_MODE */
+ field(TEGRA_MC_BASE + 0x10c, 0, 0, 26), /* LL_CTRL */
+ field(TEGRA_MC_BASE + 0x10c, 1, 1, 27), /* LL_SEND_BOTH */
+};
+
+static const struct pmc_scratch_field emc_adr_cfg1[] __initdata = {
+ field(TEGRA_EMC_BASE + 0x14, 8, 9, 8), /* EMEM1_BANKWIDTH */
+ field(TEGRA_EMC_BASE + 0x14, 0, 2, 5), /* EMEM1_COLWIDTH */
+ field(TEGRA_EMC_BASE + 0x14, 16, 19, 10), /* EMEM1_DEVSIZE */
+ field(TEGRA_EMC_BASE + 0x2dc, 24, 28, 0), /* TERM_DRVUP */
+ field(TEGRA_APB_MISC_BASE + 0x8d4, 0, 3, 14), /* XM2COMP_VREF_SEL */
+ field(TEGRA_APB_MISC_BASE + 0x8d8, 16, 18, 21), /* XM2VTTGEN_CAL_DRVDN */
+ field(TEGRA_APB_MISC_BASE + 0x8d8, 24, 26, 18), /* XM2VTTGEN_CAL_DRVUP */
+ field(TEGRA_APB_MISC_BASE + 0x8d8, 1, 1, 30), /* XM2VTTGEN_SHORT_PWRGND */
+ field(TEGRA_APB_MISC_BASE + 0x8d8, 0, 0, 31), /* XM2VTTGEN_SHORT */
+ field(TEGRA_APB_MISC_BASE + 0x8d8, 12, 14, 24), /* XM2VTTGEN_VAUXP_LEVEL */
+ field(TEGRA_APB_MISC_BASE + 0x8d8, 8, 10, 27), /* XM2VTTGEN_VCLAMP_LEVEL */
+};
+
+static const struct pmc_scratch_field emc_digital_dll[] __initdata = {
+ field(TEGRA_EMC_BASE + 0x2bc, 1, 1, 23), /* DLI_TRIMMER_EN */
+ field(TEGRA_EMC_BASE + 0x2bc, 0, 0, 22), /* DLL_EN */
+ field(TEGRA_EMC_BASE + 0x2bc, 5, 5, 27), /* DLL_LOWSPEED */
+ field(TEGRA_EMC_BASE + 0x2bc, 2, 2, 24), /* DLL_OVERRIDE_EN */
+ field(TEGRA_EMC_BASE + 0x2bc, 8, 11, 28), /* DLL_UDSET */
+ field(TEGRA_EMC_BASE + 0x2bc, 4, 4, 26), /* PERBYTE_TRIMMER_OVERRIDE */
+ field(TEGRA_EMC_BASE + 0x2bc, 3, 3, 25), /* USE_SINGLE_DLL */
+ field(TEGRA_MC_BASE + 0xc, 0, 21, 0), /* EMEM_SIZE_KB */
+};
+
+static const struct pmc_scratch_field emc_dqs_clktrim[] __initdata = {
+ field(TEGRA_EMC_BASE + 0x2d4, 0, 29, 0), /* DQS0_CLKTRIM - DQS3 + MCLK*/
+ field(TEGRA_APB_MISC_BASE + 0x8e4, 3, 3, 31), /* XM2CFGC_CTT_HIZ_EN */
+ field(TEGRA_APB_MISC_BASE + 0x8e4, 4, 4, 30), /* XM2CFGC_VREF_DQS_EN */
+};
+
+static const struct pmc_scratch_field emc_dq_clktrim[] __initdata = {
+ field(TEGRA_EMC_BASE + 0x2d8, 0, 29, 0),
+ field(TEGRA_APB_MISC_BASE + 0x8e4, 2, 2, 30), /* XM2CFGC_PREEMP_EN */
+ field(TEGRA_APB_MISC_BASE + 0x8e4, 0, 0, 31), /* XM2CFGC_RX_FT_REC_EN */
+};
+
+static const struct pmc_scratch_field emc_dll_xform_dqs[] __initdata = {
+ field(TEGRA_EMC_BASE + 0x2bc, 16, 25, 20), /* CFG_DLL_OVERRIDE_VAL */
+ field(TEGRA_EMC_BASE + 0x2c0, 0, 4, 0), /* DQS_MULT */
+ field(TEGRA_EMC_BASE + 0x2c0, 8, 22, 5), /* DQS_OFFS */
+ field(TEGRA_MC_BASE + 0x10c, 31, 31, 30), /* LL_DRAM_INTERLEAVE */
+};
+
+static const struct pmc_scratch_field emc_odt_rw[] __initdata = {
+ field(TEGRA_EMC_BASE + 0x2c4, 0, 4, 0), /* QUSE_MULT */
+ field(TEGRA_EMC_BASE + 0x2c4, 8, 22, 5), /* QUSE_OFF */
+ field(TEGRA_EMC_BASE + 0xb4, 31, 31, 29), /* DISABLE_ODT_DURING_READ */
+ field(TEGRA_EMC_BASE + 0xb4, 30, 30, 28), /* B4_READ */
+ field(TEGRA_EMC_BASE + 0xb4, 0, 2, 25), /* RD_DELAY */
+ field(TEGRA_EMC_BASE + 0xb0, 31, 31, 24), /* ENABLE_ODT_DURING_WRITE */
+ field(TEGRA_EMC_BASE + 0xb0, 30, 30, 23), /* B4_WRITE */
+ field(TEGRA_EMC_BASE + 0xb0, 0, 2, 20), /* WR_DELAY */
+};
+
+static const struct pmc_scratch_field arbitration_xbar[] __initdata = {
+ field(TEGRA_AHB_GIZMO_BASE + 0xdc, 0, 31, 0),
+};
+
+static const struct pmc_scratch_field emc_zcal[] __initdata = {
+ field(TEGRA_EMC_BASE + 0x2e0, 0, 23, 0), /* ZCAL_REF_INTERVAL */
+ field(TEGRA_EMC_BASE + 0x2e4, 0, 7, 24), /* ZCAL_WAIT_CNT */
+};
+
+static const struct pmc_scratch_field emc_ctt_term[] __initdata = {
+ field(TEGRA_EMC_BASE + 0x2dc, 15, 19, 26), /* TERM_DRVDN */
+ field(TEGRA_EMC_BASE + 0x2dc, 8, 12, 21), /* TERM_OFFSET */
+ field(TEGRA_EMC_BASE + 0x2dc, 31, 31, 31), /* TERM_OVERRIDE */
+ field(TEGRA_EMC_BASE + 0x2dc, 0, 2, 18), /* TERM_SLOPE */
+ field(TEGRA_EMC_BASE + 0x2e8, 16, 23, 8), /* ZQ_MRW_MA */
+ field(TEGRA_EMC_BASE + 0x2e8, 0, 7, 0), /* ZQ_MRW_OP */
+};
+
+static const struct pmc_scratch_field xm2_cfgd[] __initdata = {
+ field(TEGRA_APB_MISC_BASE + 0x8e8, 16, 18, 9), /* CFGD0_DLYIN_TRM */
+ field(TEGRA_APB_MISC_BASE + 0x8e8, 20, 22, 6), /* CFGD1_DLYIN_TRM */
+ field(TEGRA_APB_MISC_BASE + 0x8e8, 24, 26, 3), /* CFGD2_DLYIN_TRM */
+ field(TEGRA_APB_MISC_BASE + 0x8e8, 28, 30, 0), /* CFGD3_DLYIN_TRM */
+ field(TEGRA_APB_MISC_BASE + 0x8e8, 3, 3, 12), /* XM2CFGD_CTT_HIZ_EN */
+ field(TEGRA_APB_MISC_BASE + 0x8e8, 2, 2, 13), /* XM2CFGD_PREEMP_EN */
+ field(TEGRA_APB_MISC_BASE + 0x8e8, 0, 0, 14), /* CM2CFGD_RX_FT_REC_EN */
+};
+
+struct pmc_scratch_reg {
+ const struct pmc_scratch_field *fields;
+ void __iomem *scratch_addr;
+ int num_fields;
+};
+
+#define scratch(offs, field_list) \
+ { \
+ .scratch_addr = IO_ADDRESS(TEGRA_PMC_BASE) + offs, \
+ .fields = field_list, \
+ .num_fields = ARRAY_SIZE(field_list), \
+ }
+
+static const struct pmc_scratch_reg scratch[] __initdata = {
+ scratch(PMC_SCRATCH3, pllx),
+ scratch(PMC_SCRATCH5, emc_0),
+ scratch(PMC_SCRATCH6, emc_1),
+ scratch(PMC_SCRATCH7, emc_2),
+ scratch(PMC_SCRATCH8, emc_3),
+ scratch(PMC_SCRATCH9, emc_4),
+ scratch(PMC_SCRATCH10, emc_5),
+ scratch(PMC_SCRATCH11, emc_6),
+ scratch(PMC_SCRATCH12, emc_dqsib_dly),
+ scratch(PMC_SCRATCH13, emc_quse_dly),
+ scratch(PMC_SCRATCH14, emc_clktrim),
+ scratch(PMC_SCRATCH15, emc_autocal_fbio),
+ scratch(PMC_SCRATCH16, emc_autocal_interval),
+ scratch(PMC_SCRATCH17, emc_cfgs),
+ scratch(PMC_SCRATCH18, emc_adr_cfg1),
+ scratch(PMC_SCRATCH19, emc_digital_dll),
+ scratch(PMC_SCRATCH20, emc_dqs_clktrim),
+ scratch(PMC_SCRATCH21, emc_dq_clktrim),
+ scratch(PMC_SCRATCH22, emc_dll_xform_dqs),
+ scratch(PMC_SCRATCH23, emc_odt_rw),
+ scratch(PMC_SCRATCH25, arbitration_xbar),
+ scratch(PMC_SCRATCH35, emc_zcal),
+ scratch(PMC_SCRATCH36, emc_ctt_term),
+ scratch(PMC_SCRATCH40, xm2_cfgd),
+};
+
+void __init tegra2_lp0_suspend_init(void)
+{
+ int i;
+ int j;
+ unsigned int v;
+ unsigned int r;
+
+ for (i = 0; i < ARRAY_SIZE(scratch); i++) {
+ r = 0;
+
+ for (j = 0; j < scratch[i].num_fields; j++) {
+ v = readl(IO_ADDRESS(scratch[i].fields[j].addr));
+ v >>= scratch[i].fields[j].shift_src;
+ v &= scratch[i].fields[j].mask;
+ v <<= scratch[i].fields[j].shift_dst;
+ r |= v;
+ }
+
+ __raw_writel(r, scratch[i].scratch_addr);
+ }
+ wmb();
+}
diff --git a/arch/arm/mach-tegra/pm-t3.c b/arch/arm/mach-tegra/pm-t3.c
new file mode 100644
index 000000000000..df20340ed508
--- /dev/null
+++ b/arch/arm/mach-tegra/pm-t3.c
@@ -0,0 +1,398 @@
+/*
+ * arch/arm/mach-tegra/pm-t3.c
+ *
+ * Tegra3 SOC-specific power and cluster management
+ *
+ * Copyright (c) 2009-2011, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/smp.h>
+#include <linux/interrupt.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+
+#include <mach/gpio.h>
+#include <mach/iomap.h>
+#include <mach/irqs.h>
+
+#include <asm/cpu_pm.h>
+#include <asm/hardware/gic.h>
+
+#include <trace/events/power.h>
+
+#include "clock.h"
+#include "cpuidle.h"
+#include "pm.h"
+#include "sleep.h"
+#include "tegra3_emc.h"
+
+#ifdef CONFIG_TEGRA_CLUSTER_CONTROL
+#define CAR_CCLK_BURST_POLICY \
+ (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x20)
+
+#define CAR_SUPER_CCLK_DIVIDER \
+ (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x24)
+
+#define CAR_CCLKG_BURST_POLICY \
+ (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x368)
+
+#define CAR_SUPER_CCLKG_DIVIDER \
+ (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x36C)
+
+#define CAR_CCLKLP_BURST_POLICY \
+ (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x370)
+#define PLLX_DIV2_BYPASS_LP (1<<16)
+
+#define CAR_SUPER_CCLKLP_DIVIDER \
+ (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x374)
+
+#define CAR_BOND_OUT_V \
+ (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x390)
+#define CAR_BOND_OUT_V_CPU_G (1<<0)
+#define CAR_BOND_OUT_V_CPU_LP (1<<1)
+
+#define CAR_CLK_ENB_V_SET \
+ (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x440)
+#define CAR_CLK_ENB_V_CPU_G (1<<0)
+#define CAR_CLK_ENB_V_CPU_LP (1<<1)
+
+#define CAR_RST_CPUG_CMPLX_SET \
+ (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x450)
+
+#define CAR_RST_CPUG_CMPLX_CLR \
+ (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x454)
+
+#define CAR_RST_CPULP_CMPLX_SET \
+ (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x458)
+
+#define CAR_RST_CPULP_CMPLX_CLR \
+ (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x45C)
+
+#define CAR_CLK_CPUG_CMPLX_SET \
+ (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x460)
+
+#define CAR_CLK_CPUG_CMPLX_CLR \
+ (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x464)
+
+#define CAR_CLK_CPULP_CMPLX_SET \
+ (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x468)
+
+#define CAR_CLK_CPULP_CMPLX_CLR \
+ (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x46C)
+
+#define CPU_CLOCK(cpu) (0x1<<(8+cpu))
+#define CPU_RESET(cpu) (0x1111ul<<(cpu))
+
+static int cluster_switch_prolog_clock(unsigned int flags)
+{
+ u32 reg;
+ u32 CclkBurstPolicy;
+ u32 SuperCclkDivier;
+
+ /* Read the bond out register containing the G and LP CPUs. */
+ reg = readl(CAR_BOND_OUT_V);
+
+ /* Sync G-PLLX divider bypass with LP (no effect on G, just to prevent
+ LP settings overwrite by save/restore code */
+ CclkBurstPolicy = ~PLLX_DIV2_BYPASS_LP & readl(CAR_CCLKG_BURST_POLICY);
+ CclkBurstPolicy |= PLLX_DIV2_BYPASS_LP & readl(CAR_CCLKLP_BURST_POLICY);
+ writel(CclkBurstPolicy, CAR_CCLKG_BURST_POLICY);
+
+ /* Switching to G? */
+ if (flags & TEGRA_POWER_CLUSTER_G) {
+ /* Do the G CPUs exist? */
+ if (reg & CAR_BOND_OUT_V_CPU_G)
+ return -ENXIO;
+
+ /* Keep G CPU clock policy set by upper laayer, with the
+ exception of the transition via LP1 */
+ if (flags & TEGRA_POWER_SDRAM_SELFREFRESH) {
+ /* In LP1 power mode come up on CLKM (oscillator) */
+ CclkBurstPolicy = readl(CAR_CCLKG_BURST_POLICY);
+ CclkBurstPolicy &= ~0xF;
+ SuperCclkDivier = 0;
+
+ writel(CclkBurstPolicy, CAR_CCLKG_BURST_POLICY);
+ writel(SuperCclkDivier, CAR_SUPER_CCLKG_DIVIDER);
+ }
+
+ /* Hold G CPUs 1-3 in reset after the switch */
+ reg = CPU_RESET(1) | CPU_RESET(2) | CPU_RESET(3);
+ writel(reg, CAR_RST_CPUG_CMPLX_SET);
+
+ /* Take G CPU 0 out of reset after the switch */
+ reg = CPU_RESET(0);
+ writel(reg, CAR_RST_CPUG_CMPLX_CLR);
+
+ /* Disable the clocks on G CPUs 1-3 after the switch */
+ reg = CPU_CLOCK(1) | CPU_CLOCK(2) | CPU_CLOCK(3);
+ writel(reg, CAR_CLK_CPUG_CMPLX_SET);
+
+ /* Enable the clock on G CPU 0 after the switch */
+ reg = CPU_CLOCK(0);
+ writel(reg, CAR_CLK_CPUG_CMPLX_CLR);
+
+ /* Enable the G CPU complex clock after the switch */
+ reg = CAR_CLK_ENB_V_CPU_G;
+ writel(reg, CAR_CLK_ENB_V_SET);
+ }
+ /* Switching to LP? */
+ else if (flags & TEGRA_POWER_CLUSTER_LP) {
+ /* Does the LP CPU exist? */
+ if (reg & CAR_BOND_OUT_V_CPU_LP)
+ return -ENXIO;
+
+ /* Keep LP CPU clock policy set by upper layer, with the
+ exception of the transition via LP1 */
+ if (flags & TEGRA_POWER_SDRAM_SELFREFRESH) {
+ /* In LP1 power mode come up on CLKM (oscillator) */
+ CclkBurstPolicy = readl(CAR_CCLKLP_BURST_POLICY);
+ CclkBurstPolicy &= ~0xF;
+ SuperCclkDivier = 0;
+
+ writel(CclkBurstPolicy, CAR_CCLKLP_BURST_POLICY);
+ writel(SuperCclkDivier, CAR_SUPER_CCLKLP_DIVIDER);
+ }
+
+ /* Take the LP CPU ut of reset after the switch */
+ reg = CPU_RESET(0);
+ writel(reg, CAR_RST_CPULP_CMPLX_CLR);
+
+ /* Enable the clock on the LP CPU after the switch */
+ reg = CPU_CLOCK(0);
+ writel(reg, CAR_CLK_CPULP_CMPLX_CLR);
+
+ /* Enable the LP CPU complex clock after the switch */
+ reg = CAR_CLK_ENB_V_CPU_LP;
+ writel(reg, CAR_CLK_ENB_V_SET);
+ }
+
+ return 0;
+}
+
+void tegra_cluster_switch_prolog(unsigned int flags)
+{
+ unsigned int target_cluster = flags & TEGRA_POWER_CLUSTER_MASK;
+ unsigned int current_cluster = is_lp_cluster()
+ ? TEGRA_POWER_CLUSTER_LP
+ : TEGRA_POWER_CLUSTER_G;
+ u32 reg;
+
+ /* Read the flow controler CSR register and clear the CPU switch
+ and immediate flags. If an actual CPU switch is to be performed,
+ re-write the CSR register with the desired values. */
+ reg = readl(FLOW_CTRL_CPU_CSR(0));
+ reg &= ~(FLOW_CTRL_CPU_CSR_IMMEDIATE_WAKE |
+ FLOW_CTRL_CPU_CSR_SWITCH_CLUSTER);
+
+ /* Program flow controller for immediate wake if requested */
+ if (flags & TEGRA_POWER_CLUSTER_IMMEDIATE)
+ reg |= FLOW_CTRL_CPU_CSR_IMMEDIATE_WAKE;
+
+ /* Do nothing if no switch actions requested */
+ if (!target_cluster)
+ goto done;
+
+ if ((current_cluster != target_cluster) ||
+ (flags & TEGRA_POWER_CLUSTER_FORCE)) {
+ if (current_cluster != target_cluster) {
+ // Set up the clocks for the target CPU.
+ if (cluster_switch_prolog_clock(flags)) {
+ /* The target CPU does not exist */
+ goto done;
+ }
+
+ /* Set up the flow controller to switch CPUs. */
+ reg |= FLOW_CTRL_CPU_CSR_SWITCH_CLUSTER;
+ }
+ }
+
+done:
+ writel(reg, FLOW_CTRL_CPU_CSR(0));
+}
+
+
+static void cluster_switch_epilog_actlr(void)
+{
+ u32 actlr;
+
+ /* TLB maintenance broadcast bit (FW) is stubbed out on LP CPU (reads
+ as zero, writes ignored). Hence, it is not preserved across G=>LP=>G
+ switch by CPU save/restore code, but SMP bit is restored correctly.
+ Synchronize these two bits here after LP=>G transition. Note that
+ only CPU0 core is powered on before and after the switch. See also
+ bug 807595. */
+
+ __asm__("mrc p15, 0, %0, c1, c0, 1\n" : "=r" (actlr));
+
+ if (actlr & (0x1 << 6)) {
+ actlr |= 0x1;
+ __asm__("mcr p15, 0, %0, c1, c0, 1\n" : : "r" (actlr));
+ }
+}
+
+static void cluster_switch_epilog_gic(void)
+{
+ unsigned int max_irq, i;
+ void __iomem *gic_base = IO_ADDRESS(TEGRA_ARM_INT_DIST_BASE);
+
+ /* Reprogram the interrupt affinity because the on the LP CPU,
+ the interrupt distributor affinity regsiters are stubbed out
+ by ARM (reads as zero, writes ignored). So when the LP CPU
+ context save code runs, the affinity registers will read
+ as all zero. This causes all interrupts to be effectively
+ disabled when back on the G CPU because they aren't routable
+ to any CPU. See bug 667720 for details. */
+
+ max_irq = readl(gic_base + GIC_DIST_CTR) & 0x1f;
+ max_irq = (max_irq + 1) * 32;
+
+ for (i = 32; i < max_irq; i += 4)
+ writel(0x01010101, gic_base + GIC_DIST_TARGET + i * 4 / 4);
+}
+
+void tegra_cluster_switch_epilog(unsigned int flags)
+{
+ u32 reg;
+
+ /* Make sure the switch and immediate flags are cleared in
+ the flow controller to prevent undesirable side-effects
+ for future users of the flow controller. */
+ reg = readl(FLOW_CTRL_CPU_CSR(0));
+ reg &= ~(FLOW_CTRL_CPU_CSR_IMMEDIATE_WAKE |
+ FLOW_CTRL_CPU_CSR_SWITCH_CLUSTER);
+ writel(reg, FLOW_CTRL_CPU_CSR(0));
+
+ /* Perform post-switch LP=>G clean-up */
+ if (!is_lp_cluster()) {
+ cluster_switch_epilog_actlr();
+ cluster_switch_epilog_gic();
+ }
+
+ #if DEBUG_CLUSTER_SWITCH
+ {
+ /* FIXME: clock functions below are taking mutex */
+ struct clk *c = tegra_get_clock_by_name(
+ is_lp_cluster() ? "cpu_lp" : "cpu_g");
+ DEBUG_CLUSTER(("%s: %s freq %lu\r\n", __func__,
+ is_lp_cluster() ? "LP" : "G", clk_get_rate(c)));
+ }
+ #endif
+}
+
+int tegra_cluster_control(unsigned int us, unsigned int flags)
+{
+ static ktime_t last_g2lp;
+
+ unsigned int target_cluster = flags & TEGRA_POWER_CLUSTER_MASK;
+ unsigned int current_cluster = is_lp_cluster()
+ ? TEGRA_POWER_CLUSTER_LP
+ : TEGRA_POWER_CLUSTER_G;
+ unsigned long irq_flags;
+
+ if ((target_cluster == TEGRA_POWER_CLUSTER_MASK) || !target_cluster)
+ return -EINVAL;
+
+ if (num_online_cpus() > 1)
+ return -EBUSY;
+
+ if ((current_cluster == target_cluster)
+ && !(flags & TEGRA_POWER_CLUSTER_FORCE))
+ return -EEXIST;
+
+ if (target_cluster == TEGRA_POWER_CLUSTER_G)
+ if (!is_g_cluster_present())
+ return -EPERM;
+
+ trace_power_start(POWER_PSTATE, target_cluster, 0);
+
+ if (flags & TEGRA_POWER_CLUSTER_IMMEDIATE)
+ us = 0;
+
+ if (current_cluster != target_cluster && !timekeeping_suspended) {
+ if (target_cluster == TEGRA_POWER_CLUSTER_G) {
+ s64 t = ktime_to_us(ktime_sub(ktime_get(), last_g2lp));
+ s64 t_off = tegra_cpu_power_off_time();
+ if (t_off > t)
+ udelay((unsigned int)(t_off - t));
+ }
+ else
+ last_g2lp = ktime_get();
+ }
+
+ DEBUG_CLUSTER(("%s(LP%d): %s->%s %s %s %d\r\n", __func__,
+ (flags & TEGRA_POWER_SDRAM_SELFREFRESH) ? 1 : 2,
+ is_lp_cluster() ? "LP" : "G",
+ (target_cluster == TEGRA_POWER_CLUSTER_G) ? "G" : "LP",
+ (flags & TEGRA_POWER_CLUSTER_IMMEDIATE) ? "immediate" : "",
+ (flags & TEGRA_POWER_CLUSTER_FORCE) ? "force" : "",
+ us));
+
+ local_irq_save(irq_flags);
+ if (flags & TEGRA_POWER_SDRAM_SELFREFRESH) {
+ if (us)
+ tegra_lp2_set_trigger(us);
+
+ tegra_cluster_switch_prolog(flags);
+ tegra_suspend_dram(TEGRA_SUSPEND_LP1, flags);
+ tegra_cluster_switch_epilog(flags);
+
+ if (us)
+ tegra_lp2_set_trigger(0);
+ } else {
+ tegra_set_cpu_in_lp2(0);
+ cpu_pm_enter();
+ tegra_idle_lp2_last(0, flags);
+ cpu_pm_exit();
+ tegra_clear_cpu_in_lp2(0);
+ }
+ local_irq_restore(irq_flags);
+
+ DEBUG_CLUSTER(("%s: %s\r\n", __func__, is_lp_cluster() ? "LP" : "G"));
+
+ return 0;
+}
+#endif
+
+#ifdef CONFIG_PM_SLEEP
+
+void tegra_lp0_suspend_mc(void)
+{
+ /* Since memory frequency after LP0 is restored to boot rate
+ mc timing is saved during init, not on entry to LP0. Keep
+ this hook just in case, anyway */
+}
+
+void tegra_lp0_resume_mc(void)
+{
+ tegra_mc_timing_restore();
+}
+
+void tegra_lp0_cpu_mode(bool enter)
+{
+ static bool entered_on_g = false;
+ unsigned int flags;
+
+ if (enter)
+ entered_on_g = !is_lp_cluster();
+
+ if (entered_on_g) {
+ flags = enter ? TEGRA_POWER_CLUSTER_LP : TEGRA_POWER_CLUSTER_G;
+ flags |= TEGRA_POWER_CLUSTER_IMMEDIATE;
+ tegra_cluster_control(0, flags);
+ }
+}
+#endif
diff --git a/arch/arm/mach-tegra/pm.c b/arch/arm/mach-tegra/pm.c
new file mode 100644
index 000000000000..ca3baada0aab
--- /dev/null
+++ b/arch/arm/mach-tegra/pm.c
@@ -0,0 +1,1287 @@
+/*
+ * arch/arm/mach-tegra/pm.c
+ *
+ * CPU complex suspend & resume functions for Tegra SoCs
+ *
+ * Copyright (c) 2009-2011, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/ctype.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/sched.h>
+#include <linux/smp.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/suspend.h>
+#include <linux/slab.h>
+#include <linux/serial_reg.h>
+#include <linux/seq_file.h>
+#include <linux/uaccess.h>
+#include <linux/syscore_ops.h>
+#include <linux/vmalloc.h>
+#include <linux/memblock.h>
+#include <linux/console.h>
+
+#include <asm/cacheflush.h>
+#include <asm/cpu_pm.h>
+#include <asm/hardware/cache-l2x0.h>
+#include <asm/hardware/gic.h>
+#include <asm/localtimer.h>
+#include <asm/pgalloc.h>
+#include <asm/pgtable.h>
+#include <asm/tlbflush.h>
+
+#include <mach/clk.h>
+#include <mach/iomap.h>
+#include <mach/irqs.h>
+#include <mach/powergate.h>
+
+#include "board.h"
+#include "clock.h"
+#include "cpuidle.h"
+#include "fuse.h"
+#include "gic.h"
+#include "pm.h"
+#include "pm-irq.h"
+#include "reset.h"
+#include "sleep.h"
+#include "timer.h"
+
+struct suspend_context {
+ /*
+ * The next 7 values are referenced by offset in __restart_plls
+ * in headsmp-t2.S, and should not be moved
+ */
+ u32 pllx_misc;
+ u32 pllx_base;
+ u32 pllp_misc;
+ u32 pllp_base;
+ u32 pllp_outa;
+ u32 pllp_outb;
+ u32 pll_timeout;
+
+ u32 cpu_burst;
+ u32 clk_csite_src;
+ u32 cclk_divider;
+
+ u32 mc[3];
+ u8 uart[5];
+
+ struct tegra_twd_context twd;
+};
+
+#ifdef CONFIG_PM_SLEEP
+#if USE_TEGRA_CPU_SUSPEND
+void *tegra_cpu_context; /* non-cacheable page for CPU context */
+#endif
+phys_addr_t tegra_pgd_phys; /* pgd used by hotplug & LP2 bootup */
+static pgd_t *tegra_pgd;
+static DEFINE_SPINLOCK(tegra_lp2_lock);
+static cpumask_t tegra_in_lp2;
+static cpumask_t *iram_cpu_lp2_mask;
+static unsigned long *iram_cpu_lp1_mask;
+static u8 *iram_save;
+static unsigned long iram_save_size;
+static void __iomem *iram_code = IO_ADDRESS(TEGRA_IRAM_CODE_AREA);
+static void __iomem *clk_rst = IO_ADDRESS(TEGRA_CLK_RESET_BASE);
+static void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE);
+static int tegra_last_pclk;
+#endif
+
+struct suspend_context tegra_sctx;
+
+#define TEGRA_POWER_PWRREQ_POLARITY (1 << 8) /* core power request polarity */
+#define TEGRA_POWER_PWRREQ_OE (1 << 9) /* core power request enable */
+#define TEGRA_POWER_SYSCLK_POLARITY (1 << 10) /* sys clk polarity */
+#define TEGRA_POWER_SYSCLK_OE (1 << 11) /* system clock enable */
+#define TEGRA_POWER_PWRGATE_DIS (1 << 12) /* power gate disabled */
+#define TEGRA_POWER_EFFECT_LP0 (1 << 14) /* enter LP0 when CPU pwr gated */
+#define TEGRA_POWER_CPU_PWRREQ_POLARITY (1 << 15) /* CPU power request polarity */
+#define TEGRA_POWER_CPU_PWRREQ_OE (1 << 16) /* CPU power request enable */
+
+#define PMC_CTRL 0x0
+#define PMC_CTRL_LATCH_WAKEUPS (1 << 5)
+#define PMC_WAKE_MASK 0xc
+#define PMC_WAKE_LEVEL 0x10
+#define PMC_DPAD_ORIDE 0x1C
+#define PMC_WAKE_DELAY 0xe0
+#define PMC_DPD_SAMPLE 0x20
+
+#define PMC_WAKE_STATUS 0x14
+#define PMC_SW_WAKE_STATUS 0x18
+#define PMC_COREPWRGOOD_TIMER 0x3c
+#define PMC_SCRATCH0 0x50
+#define PMC_SCRATCH1 0x54
+#define PMC_SCRATCH4 0x60
+#define PMC_CPUPWRGOOD_TIMER 0xc8
+#define PMC_CPUPWROFF_TIMER 0xcc
+#define PMC_COREPWROFF_TIMER PMC_WAKE_DELAY
+
+#ifdef CONFIG_TEGRA_CLUSTER_CONTROL
+#define PMC_SCRATCH4_WAKE_CLUSTER_MASK (1<<31)
+#endif
+
+#define CLK_RESET_CCLK_BURST 0x20
+#define CLK_RESET_CCLK_DIVIDER 0x24
+#define CLK_RESET_PLLC_BASE 0x80
+#define CLK_RESET_PLLM_BASE 0x90
+#define CLK_RESET_PLLX_BASE 0xe0
+#define CLK_RESET_PLLX_MISC 0xe4
+#define CLK_RESET_PLLP_BASE 0xa0
+#define CLK_RESET_PLLP_OUTA 0xa4
+#define CLK_RESET_PLLP_OUTB 0xa8
+#define CLK_RESET_PLLP_MISC 0xac
+
+#define CLK_RESET_SOURCE_CSITE 0x1d4
+
+#define CLK_RESET_CCLK_BURST_POLICY_SHIFT 28
+#define CLK_RESET_CCLK_BURST_POLICY_PLLM 3
+#define CLK_RESET_CCLK_BURST_POLICY_PLLX 8
+
+#define EMC_MRW_0 0x0e8
+#define EMC_MRW_DEV_SELECTN 30
+#define EMC_MRW_DEV_NONE (3 << EMC_MRW_DEV_SELECTN)
+
+#define MC_SECURITY_START 0x6c
+#define MC_SECURITY_SIZE 0x70
+#define MC_SECURITY_CFG2 0x7c
+
+static struct clk *tegra_pclk;
+static const struct tegra_suspend_platform_data *pdata;
+static enum tegra_suspend_mode current_suspend_mode = TEGRA_SUSPEND_NONE;
+
+static const char *tegra_suspend_name[TEGRA_MAX_SUSPEND_MODE] = {
+ [TEGRA_SUSPEND_NONE] = "none",
+ [TEGRA_SUSPEND_LP2] = "lp2",
+ [TEGRA_SUSPEND_LP1] = "lp1",
+ [TEGRA_SUSPEND_LP0] = "lp0",
+};
+
+#if defined(CONFIG_TEGRA_CLUSTER_CONTROL) && INSTRUMENT_CLUSTER_SWITCH
+enum tegra_cluster_switch_time_id {
+ tegra_cluster_switch_time_id_start = 0,
+ tegra_cluster_switch_time_id_prolog,
+ tegra_cluster_switch_time_id_switch,
+ tegra_cluster_switch_time_id_epilog,
+ tegra_cluster_switch_time_id_max
+};
+
+static unsigned long
+ tegra_cluster_switch_times[tegra_cluster_switch_time_id_max];
+#define tegra_cluster_switch_time(flags, id) \
+ do { \
+ barrier(); \
+ if (flags & TEGRA_POWER_CLUSTER_MASK) { \
+ void __iomem *timer_us = \
+ IO_ADDRESS(TEGRA_TMRUS_BASE); \
+ if (id < tegra_cluster_switch_time_id_max) \
+ tegra_cluster_switch_times[id] = \
+ readl(timer_us); \
+ wmb(); \
+ } \
+ barrier(); \
+ } while(0)
+#else
+#define tegra_cluster_switch_time(flags, id) do {} while(0)
+#endif
+
+#ifdef CONFIG_PM_SLEEP
+unsigned long tegra_cpu_power_good_time(void)
+{
+ if (WARN_ON_ONCE(!pdata))
+ return 5000;
+
+ return pdata->cpu_timer;
+}
+
+unsigned long tegra_cpu_power_off_time(void)
+{
+ if (WARN_ON_ONCE(!pdata))
+ return 5000;
+
+ return pdata->cpu_off_timer;
+}
+
+unsigned long tegra_cpu_lp2_min_residency(void)
+{
+ if (WARN_ON_ONCE(!pdata))
+ return 2000;
+
+ return pdata->cpu_lp2_min_residency;
+}
+
+/*
+ * create_suspend_pgtable
+ *
+ * Creates a page table with identity mappings of physical memory and IRAM
+ * for use when the MMU is off, in addition to all the regular kernel mappings.
+ */
+static __init int create_suspend_pgtable(void)
+{
+ tegra_pgd = pgd_alloc(&init_mm);
+ if (!tegra_pgd)
+ return -ENOMEM;
+
+ identity_mapping_add(tegra_pgd, PLAT_PHYS_OFFSET,
+ PLAT_PHYS_OFFSET + memblock_phys_mem_size());
+ identity_mapping_add(tegra_pgd, IO_IRAM_PHYS,
+ IO_IRAM_PHYS + SECTION_SIZE);
+
+ /* inner/outer write-back/write-allocate, sharable */
+ tegra_pgd_phys = (virt_to_phys(tegra_pgd) & PAGE_MASK) | 0x4A;
+
+ return 0;
+}
+
+/*
+ * alloc_suspend_context
+ *
+ * Allocate a non-cacheable page to hold the CPU contexts.
+ * The standard ARM CPU context save functions don't work if there's
+ * an external L2 cache controller (like a PL310) in system.
+ */
+static __init int alloc_suspend_context(void)
+{
+#if USE_TEGRA_CPU_SUSPEND
+ pgprot_t prot = __pgprot_modify(pgprot_kernel, L_PTE_MT_MASK,
+ L_PTE_MT_BUFFERABLE | L_PTE_XN);
+ struct page *ctx_page;
+ unsigned long ctx_virt = 0;
+ pgd_t *pgd;
+ pmd_t *pmd;
+ pte_t *pte;
+
+ ctx_page = alloc_pages(GFP_KERNEL, 0);
+ if (IS_ERR_OR_NULL(ctx_page))
+ goto fail;
+
+ tegra_cpu_context = vm_map_ram(&ctx_page, 1, -1, prot);
+ if (IS_ERR_OR_NULL(tegra_cpu_context))
+ goto fail;
+
+ /* Add the context page to our private pgd. */
+ ctx_virt = (unsigned long)tegra_cpu_context;
+
+ pgd = tegra_pgd + pgd_index(ctx_virt);
+ if (!pgd_present(*pgd))
+ goto fail;
+ pmd = pmd_offset(pgd, ctx_virt);
+ if (!pmd_none(*pmd))
+ goto fail;
+ pte = pte_alloc_kernel(pmd, ctx_virt);
+ if (!pte)
+ goto fail;
+
+ set_pte_ext(pte, mk_pte(ctx_page, prot), 0);
+
+ outer_clean_range(__pa(pmd), __pa(pmd + 1));
+
+ return 0;
+
+fail:
+ if (ctx_page)
+ __free_page(ctx_page);
+ if (ctx_virt)
+ vm_unmap_ram((void*)ctx_virt, 1);
+ tegra_cpu_context = NULL;
+ return -ENOMEM;
+#else
+ return 0;
+#endif
+}
+
+/* ensures that sufficient time is passed for a register write to
+ * serialize into the 32KHz domain */
+static void pmc_32kwritel(u32 val, unsigned long offs)
+{
+ writel(val, pmc + offs);
+ udelay(130);
+}
+
+static void set_power_timers(unsigned long us_on, unsigned long us_off,
+ long rate)
+{
+ static unsigned long last_us_off = 0;
+ unsigned long long ticks;
+ unsigned long long pclk;
+
+ if (WARN_ON_ONCE(rate <= 0))
+ pclk = 100000000;
+ else
+ pclk = rate;
+
+ if ((rate != tegra_last_pclk) || (us_off != last_us_off)) {
+ ticks = (us_on * pclk) + 999999ull;
+ do_div(ticks, 1000000);
+ writel((unsigned long)ticks, pmc + PMC_CPUPWRGOOD_TIMER);
+
+ ticks = (us_off * pclk) + 999999ull;
+ do_div(ticks, 1000000);
+ writel((unsigned long)ticks, pmc + PMC_CPUPWROFF_TIMER);
+ wmb();
+ }
+ tegra_last_pclk = pclk;
+ last_us_off = us_off;
+}
+
+/*
+ * restore_cpu_complex
+ *
+ * restores cpu clock setting, clears flow controller
+ *
+ * Always called on CPU 0.
+ */
+static void restore_cpu_complex(u32 mode)
+{
+ int cpu = smp_processor_id();
+ unsigned int reg;
+
+ BUG_ON(cpu != 0);
+
+ /* restore original PLL settings */
+ writel(tegra_sctx.pllx_misc, clk_rst + CLK_RESET_PLLX_MISC);
+ writel(tegra_sctx.pllx_base, clk_rst + CLK_RESET_PLLX_BASE);
+ writel(tegra_sctx.pllp_misc, clk_rst + CLK_RESET_PLLP_MISC);
+ writel(tegra_sctx.pllp_base, clk_rst + CLK_RESET_PLLP_BASE);
+ writel(tegra_sctx.pllp_outa, clk_rst + CLK_RESET_PLLP_OUTA);
+ writel(tegra_sctx.pllp_outb, clk_rst + CLK_RESET_PLLP_OUTB);
+
+ /* Is CPU complex already running on PLLX? */
+ reg = readl(clk_rst + CLK_RESET_CCLK_BURST);
+ reg &= 0xF;
+ if (reg != 0x8) {
+ /* restore original burst policy setting; PLLX state restored
+ * by CPU boot-up code - wait for PLL stabilization if PLLX
+ * was enabled */
+
+ reg = readl(clk_rst + CLK_RESET_PLLX_BASE);
+ /* mask out bit 27 - not to check PLL lock bit */
+ BUG_ON((reg & (~(1 << 27))) !=
+ (tegra_sctx.pllx_base & (~(1 << 27))));
+
+ if (tegra_sctx.pllx_base & (1<<30)) {
+#if USE_PLL_LOCK_BITS
+ /* Enable lock detector */
+ reg = readl(clk_rst + CLK_RESET_PLLX_MISC);
+ reg |= 1<<18;
+ writel(reg, clk_rst + CLK_RESET_PLLX_MISC);
+ while (!(readl(clk_rst + CLK_RESET_PLLX_BASE) &&
+ (1<<27)))
+ cpu_relax();
+#else
+ udelay(300);
+#endif
+ }
+ writel(tegra_sctx.cclk_divider, clk_rst +
+ CLK_RESET_CCLK_DIVIDER);
+ writel(tegra_sctx.cpu_burst, clk_rst +
+ CLK_RESET_CCLK_BURST);
+ }
+
+ writel(tegra_sctx.clk_csite_src, clk_rst + CLK_RESET_SOURCE_CSITE);
+
+ /* Do not power-gate CPU 0 when flow controlled */
+ reg = readl(FLOW_CTRL_CPU_CSR(cpu));
+ reg &= ~FLOW_CTRL_CSR_WFE_BITMAP; /* clear wfe bitmap */
+ reg &= ~FLOW_CTRL_CSR_WFI_BITMAP; /* clear wfi bitmap */
+ reg &= ~FLOW_CTRL_CSR_ENABLE; /* clear enable */
+ reg |= FLOW_CTRL_CSR_INTR_FLAG; /* clear intr */
+ reg |= FLOW_CTRL_CSR_EVENT_FLAG; /* clear event */
+ flowctrl_writel(reg, FLOW_CTRL_CPU_CSR(cpu));
+
+ /* If an immedidate cluster switch is being perfomed, restore the
+ local timer registers. For calls resulting from CPU LP2 in
+ idle or system suspend, the local timer was shut down and
+ timekeeping switched over to the global system timer. In this
+ case keep local timer disabled, and restore only periodic load. */
+ if (!(mode & (TEGRA_POWER_CLUSTER_MASK |
+ TEGRA_POWER_CLUSTER_IMMEDIATE)))
+ tegra_sctx.twd.twd_ctrl = 0;
+ tegra_twd_resume(&tegra_sctx.twd);
+}
+
+/*
+ * suspend_cpu_complex
+ *
+ * saves pll state for use by restart_plls, prepares flow controller for
+ * transition to suspend state
+ *
+ * Must always be called on cpu 0.
+ */
+static void suspend_cpu_complex(u32 mode)
+{
+ int cpu = smp_processor_id();
+ unsigned int reg;
+ int i;
+
+ BUG_ON(cpu != 0);
+
+ /* switch coresite to clk_m, save off original source */
+ tegra_sctx.clk_csite_src = readl(clk_rst + CLK_RESET_SOURCE_CSITE);
+ writel(3<<30, clk_rst + CLK_RESET_SOURCE_CSITE);
+
+ tegra_sctx.cpu_burst = readl(clk_rst + CLK_RESET_CCLK_BURST);
+ tegra_sctx.pllx_base = readl(clk_rst + CLK_RESET_PLLX_BASE);
+ tegra_sctx.pllx_misc = readl(clk_rst + CLK_RESET_PLLX_MISC);
+ tegra_sctx.pllp_base = readl(clk_rst + CLK_RESET_PLLP_BASE);
+ tegra_sctx.pllp_outa = readl(clk_rst + CLK_RESET_PLLP_OUTA);
+ tegra_sctx.pllp_outb = readl(clk_rst + CLK_RESET_PLLP_OUTB);
+ tegra_sctx.pllp_misc = readl(clk_rst + CLK_RESET_PLLP_MISC);
+ tegra_sctx.cclk_divider = readl(clk_rst + CLK_RESET_CCLK_DIVIDER);
+
+ tegra_twd_suspend(&tegra_sctx.twd);
+
+ reg = readl(FLOW_CTRL_CPU_CSR(cpu));
+ reg &= ~FLOW_CTRL_CSR_WFE_BITMAP; /* clear wfe bitmap */
+ reg &= ~FLOW_CTRL_CSR_WFI_BITMAP; /* clear wfi bitmap */
+ reg |= FLOW_CTRL_CSR_INTR_FLAG; /* clear intr flag */
+ reg |= FLOW_CTRL_CSR_EVENT_FLAG; /* clear event flag */
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+ reg |= FLOW_CTRL_CSR_WFE_CPU0 << cpu; /* enable power gating on wfe */
+#else
+ reg |= FLOW_CTRL_CSR_WFI_CPU0 << cpu; /* enable power gating on wfi */
+#endif
+ reg |= FLOW_CTRL_CSR_ENABLE; /* enable power gating */
+ flowctrl_writel(reg, FLOW_CTRL_CPU_CSR(cpu));
+
+ for (i = 0; i < num_possible_cpus(); i++) {
+ if (i == cpu)
+ continue;
+ reg = readl(FLOW_CTRL_CPU_CSR(i));
+ reg |= FLOW_CTRL_CSR_EVENT_FLAG;
+ reg |= FLOW_CTRL_CSR_INTR_FLAG;
+ flowctrl_writel(reg, FLOW_CTRL_CPU_CSR(i));
+ }
+
+ tegra_gic_cpu_disable();
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+ /* Tegra3 enters LPx states via WFI - do not propagate legacy IRQs
+ to CPU core to avoid fall through WFI (IRQ-to-flow controller wake
+ path is not affected). */
+ tegra_gic_pass_through_disable();
+#endif
+}
+
+void tegra_clear_cpu_in_lp2(int cpu)
+{
+ spin_lock(&tegra_lp2_lock);
+ BUG_ON(!cpumask_test_cpu(cpu, &tegra_in_lp2));
+ cpumask_clear_cpu(cpu, &tegra_in_lp2);
+
+ /* Update the IRAM copy used by the reset handler. The IRAM copy
+ can't use used directly by cpumask_clear_cpu() because it uses
+ LDREX/STREX which requires the addressed location to be inner
+ cacheable and sharable which IRAM isn't. */
+ writel(tegra_in_lp2.bits[0], iram_cpu_lp2_mask);
+ dsb();
+
+ spin_unlock(&tegra_lp2_lock);
+}
+
+bool tegra_set_cpu_in_lp2(int cpu)
+{
+ bool last_cpu = false;
+
+ spin_lock(&tegra_lp2_lock);
+ BUG_ON(cpumask_test_cpu(cpu, &tegra_in_lp2));
+ cpumask_set_cpu(cpu, &tegra_in_lp2);
+
+ /* Update the IRAM copy used by the reset handler. The IRAM copy
+ can't use used directly by cpumask_set_cpu() because it uses
+ LDREX/STREX which requires the addressed location to be inner
+ cacheable and sharable which IRAM isn't. */
+ writel(tegra_in_lp2.bits[0], iram_cpu_lp2_mask);
+ dsb();
+
+ if ((cpu == 0) && cpumask_equal(&tegra_in_lp2, cpu_online_mask))
+ last_cpu = true;
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+ else
+ tegra2_cpu_set_resettable_soon();
+#endif
+
+ spin_unlock(&tegra_lp2_lock);
+ return last_cpu;
+}
+
+static void tegra_sleep_core(enum tegra_suspend_mode mode,
+ unsigned long v2p)
+{
+#ifdef CONFIG_TRUSTED_FOUNDATIONS
+ if (mode == TEGRA_SUSPEND_LP0) {
+ tegra_generic_smc(0xFFFFFFFC, 0xFFFFFFE3,
+ virt_to_phys(tegra_resume));
+ } else {
+ tegra_generic_smc(0xFFFFFFFC, 0xFFFFFFE6,
+ (TEGRA_RESET_HANDLER_BASE +
+ tegra_cpu_reset_handler_offset));
+ }
+#endif
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+ tegra2_sleep_core(v2p);
+#else
+ tegra3_sleep_core(v2p);
+#endif
+}
+
+static inline void tegra_sleep_cpu(unsigned long v2p)
+{
+#ifdef CONFIG_TRUSTED_FOUNDATIONS
+ tegra_generic_smc(0xFFFFFFFC, 0xFFFFFFE4,
+ (TEGRA_RESET_HANDLER_BASE +
+ tegra_cpu_reset_handler_offset));
+#endif
+ tegra_sleep_cpu_save(v2p);
+}
+
+unsigned int tegra_idle_lp2_last(unsigned int sleep_time, unsigned int flags)
+{
+ u32 mode; /* hardware + software power mode flags */
+ unsigned int remain;
+
+ /* Only the last cpu down does the final suspend steps */
+ mode = readl(pmc + PMC_CTRL);
+ mode |= TEGRA_POWER_CPU_PWRREQ_OE;
+ if (pdata->combined_req)
+ mode &= ~TEGRA_POWER_PWRREQ_OE;
+ else
+ mode |= TEGRA_POWER_PWRREQ_OE;
+ mode &= ~TEGRA_POWER_EFFECT_LP0;
+ pmc_32kwritel(mode, PMC_CTRL);
+ mode |= flags;
+
+ tegra_cluster_switch_time(flags, tegra_cluster_switch_time_id_start);
+
+ /*
+ * We can use clk_get_rate_all_locked() here, because all other cpus
+ * are in LP2 state and irqs are disabled
+ */
+ if (flags & TEGRA_POWER_CLUSTER_MASK) {
+ set_power_timers(pdata->cpu_timer, 0,
+ clk_get_rate_all_locked(tegra_pclk));
+ tegra_cluster_switch_prolog(mode);
+ } else {
+ set_power_timers(pdata->cpu_timer, pdata->cpu_off_timer,
+ clk_get_rate_all_locked(tegra_pclk));
+ }
+
+ if (sleep_time)
+ tegra_lp2_set_trigger(sleep_time);
+
+ cpu_complex_pm_enter();
+ suspend_cpu_complex(mode);
+ tegra_cluster_switch_time(flags, tegra_cluster_switch_time_id_prolog);
+ flush_cache_all();
+ outer_disable();
+
+ tegra_sleep_cpu(PLAT_PHYS_OFFSET - PAGE_OFFSET);
+
+#ifdef CONFIG_CACHE_L2X0
+ tegra_init_cache(false);
+#endif
+ tegra_cluster_switch_time(flags, tegra_cluster_switch_time_id_switch);
+ restore_cpu_complex(mode);
+ cpu_complex_pm_exit();
+
+ remain = tegra_lp2_timer_remain();
+ if (sleep_time)
+ tegra_lp2_set_trigger(0);
+
+ if (flags & TEGRA_POWER_CLUSTER_MASK)
+ tegra_cluster_switch_epilog(mode);
+
+ tegra_cluster_switch_time(flags, tegra_cluster_switch_time_id_epilog);
+
+#if INSTRUMENT_CLUSTER_SWITCH
+ if (flags & TEGRA_POWER_CLUSTER_MASK) {
+ pr_err("%s: prolog %lu us, switch %lu us, epilog %lu us, total %lu us\n",
+ is_lp_cluster() ? "G=>LP" : "LP=>G",
+ tegra_cluster_switch_times[tegra_cluster_switch_time_id_prolog] -
+ tegra_cluster_switch_times[tegra_cluster_switch_time_id_start],
+ tegra_cluster_switch_times[tegra_cluster_switch_time_id_switch] -
+ tegra_cluster_switch_times[tegra_cluster_switch_time_id_prolog],
+ tegra_cluster_switch_times[tegra_cluster_switch_time_id_epilog] -
+ tegra_cluster_switch_times[tegra_cluster_switch_time_id_switch],
+ tegra_cluster_switch_times[tegra_cluster_switch_time_id_epilog] -
+ tegra_cluster_switch_times[tegra_cluster_switch_time_id_start]);
+ }
+#endif
+ return remain;
+}
+
+static int tegra_common_suspend(void)
+{
+ void __iomem *mc = IO_ADDRESS(TEGRA_MC_BASE);
+
+ tegra_sctx.mc[0] = readl(mc + MC_SECURITY_START);
+ tegra_sctx.mc[1] = readl(mc + MC_SECURITY_SIZE);
+ tegra_sctx.mc[2] = readl(mc + MC_SECURITY_CFG2);
+
+ /* copy the reset vector and SDRAM shutdown code into IRAM */
+ memcpy(iram_save, iram_code, iram_save_size);
+ memcpy(iram_code, tegra_iram_start(), iram_save_size);
+
+ return 0;
+}
+
+static void tegra_common_resume(void)
+{
+ void __iomem *mc = IO_ADDRESS(TEGRA_MC_BASE);
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+ void __iomem *emc = IO_ADDRESS(TEGRA_EMC_BASE);
+#endif
+
+ /* Clear DPD sample */
+ writel(0x0, pmc + PMC_DPD_SAMPLE);
+
+ writel(tegra_sctx.mc[0], mc + MC_SECURITY_START);
+ writel(tegra_sctx.mc[1], mc + MC_SECURITY_SIZE);
+ writel(tegra_sctx.mc[2], mc + MC_SECURITY_CFG2);
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+ /* trigger emc mode write */
+ writel(EMC_MRW_DEV_NONE, emc + EMC_MRW_0);
+#endif
+ /* clear scratch registers shared by suspend and the reset pen */
+ writel(0x0, pmc + PMC_SCRATCH39);
+ writel(0x0, pmc + PMC_SCRATCH41);
+
+ /* restore IRAM */
+ memcpy(iram_code, iram_save, iram_save_size);
+}
+
+static int tegra_suspend_prepare_late(void)
+{
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+ disable_irq(INT_SYS_STATS_MON);
+#endif
+ return 0;
+}
+
+static void tegra_suspend_wake(void)
+{
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+ enable_irq(INT_SYS_STATS_MON);
+#endif
+}
+
+static void tegra_pm_set(enum tegra_suspend_mode mode)
+{
+ u32 reg, boot_flag;
+ unsigned long rate = 32768;
+
+ reg = readl(pmc + PMC_CTRL);
+ reg |= TEGRA_POWER_CPU_PWRREQ_OE;
+ if (pdata->combined_req)
+ reg &= ~TEGRA_POWER_PWRREQ_OE;
+ else
+ reg |= TEGRA_POWER_PWRREQ_OE;
+ reg &= ~TEGRA_POWER_EFFECT_LP0;
+
+ switch (mode) {
+ case TEGRA_SUSPEND_LP0:
+ if (pdata->combined_req) {
+ reg |= TEGRA_POWER_PWRREQ_OE;
+ reg &= ~TEGRA_POWER_CPU_PWRREQ_OE;
+ }
+
+ /*
+ * LP0 boots through the AVP, which then resumes the AVP to
+ * the address in scratch 39, and the cpu to the address in
+ * scratch 41 to tegra_resume
+ */
+ writel(0x0, pmc + PMC_SCRATCH39);
+
+ /* Enable DPD sample to trigger sampling pads data and direction
+ * in which pad will be driven during lp0 mode*/
+ writel(0x1, pmc + PMC_DPD_SAMPLE);
+
+ /* Set warmboot flag */
+ boot_flag = readl(pmc + PMC_SCRATCH0);
+ pmc_32kwritel(boot_flag | 1, PMC_SCRATCH0);
+
+ pmc_32kwritel(tegra_lp0_vec_start, PMC_SCRATCH1);
+
+ reg |= TEGRA_POWER_EFFECT_LP0;
+ /* No break here. LP0 code falls through to write SCRATCH41 */
+ case TEGRA_SUSPEND_LP1:
+ __raw_writel(virt_to_phys(tegra_resume), pmc + PMC_SCRATCH41);
+ wmb();
+ break;
+ case TEGRA_SUSPEND_LP2:
+ rate = clk_get_rate(tegra_pclk);
+ break;
+ case TEGRA_SUSPEND_NONE:
+ return;
+ default:
+ BUG();
+ }
+
+ set_power_timers(pdata->cpu_timer, pdata->cpu_off_timer, rate);
+
+ pmc_32kwritel(reg, PMC_CTRL);
+}
+
+static const char *lp_state[TEGRA_MAX_SUSPEND_MODE] = {
+ [TEGRA_SUSPEND_NONE] = "none",
+ [TEGRA_SUSPEND_LP2] = "LP2",
+ [TEGRA_SUSPEND_LP1] = "LP1",
+ [TEGRA_SUSPEND_LP0] = "LP0",
+};
+
+static int tegra_suspend_enter(suspend_state_t state)
+{
+ int ret;
+
+ if (pdata && pdata->board_suspend)
+ pdata->board_suspend(current_suspend_mode, TEGRA_SUSPEND_BEFORE_PERIPHERAL);
+
+ ret = tegra_suspend_dram(current_suspend_mode, 0);
+
+ if (pdata && pdata->board_resume)
+ pdata->board_resume(current_suspend_mode, TEGRA_RESUME_AFTER_PERIPHERAL);
+
+ return ret;
+}
+
+static void tegra_suspend_check_pwr_stats(void)
+{
+ /* cpus and l2 are powered off later */
+ unsigned long pwrgate_partid_mask =
+#if !defined(CONFIG_ARCH_TEGRA_2x_SOC)
+ (1 << TEGRA_POWERGATE_HEG) |
+ (1 << TEGRA_POWERGATE_SATA) |
+ (1 << TEGRA_POWERGATE_3D1) |
+#endif
+ (1 << TEGRA_POWERGATE_3D) |
+ (1 << TEGRA_POWERGATE_VENC) |
+ (1 << TEGRA_POWERGATE_PCIE) |
+ (1 << TEGRA_POWERGATE_VDEC) |
+ (1 << TEGRA_POWERGATE_MPE);
+
+ int partid;
+
+ for (partid = 0; partid < TEGRA_NUM_POWERGATE; partid++)
+ if ((1 << partid) & pwrgate_partid_mask)
+ if (tegra_powergate_is_powered(partid))
+ pr_warning("partition %s is left on before suspend\n",
+ tegra_powergate_get_name(partid));
+
+ return;
+}
+
+int tegra_suspend_dram(enum tegra_suspend_mode mode, unsigned int flags)
+{
+ BUG_ON(mode < 0 || mode >= TEGRA_MAX_SUSPEND_MODE);
+
+ if ((mode == TEGRA_SUSPEND_LP0) && !tegra_pm_irq_lp0_allowed()) {
+ pr_info("LP0 not used due to unsupported wakeup events\n");
+ mode = TEGRA_SUSPEND_LP1;
+ }
+
+ if ((mode == TEGRA_SUSPEND_LP0) || (mode == TEGRA_SUSPEND_LP1))
+ tegra_suspend_check_pwr_stats();
+
+ tegra_common_suspend();
+
+ tegra_pm_set(mode);
+
+ if (pdata && pdata->board_suspend)
+ pdata->board_suspend(mode, TEGRA_SUSPEND_BEFORE_CPU);
+
+ local_fiq_disable();
+
+ cpu_pm_enter();
+ cpu_complex_pm_enter();
+
+ if (mode == TEGRA_SUSPEND_LP0) {
+#ifdef CONFIG_TEGRA_CLUSTER_CONTROL
+ u32 reg = readl(pmc + PMC_SCRATCH4);
+ if (is_lp_cluster())
+ reg |= PMC_SCRATCH4_WAKE_CLUSTER_MASK;
+ else
+ reg &= (~PMC_SCRATCH4_WAKE_CLUSTER_MASK);
+ pmc_32kwritel(reg, PMC_SCRATCH4);
+#endif
+ tegra_lp0_suspend_mc();
+ tegra_cpu_reset_handler_save();
+
+ }
+ else if (mode == TEGRA_SUSPEND_LP1)
+ *iram_cpu_lp1_mask = 1;
+
+ suspend_cpu_complex(flags);
+
+ flush_cache_all();
+ outer_flush_all();
+ outer_disable();
+
+ if (mode == TEGRA_SUSPEND_LP2)
+ tegra_sleep_cpu(PLAT_PHYS_OFFSET - PAGE_OFFSET);
+ else
+ tegra_sleep_core(mode, PLAT_PHYS_OFFSET - PAGE_OFFSET);
+
+ tegra_init_cache(true);
+
+ if (mode == TEGRA_SUSPEND_LP0) {
+ tegra_cpu_reset_handler_restore();
+ tegra_lp0_resume_mc();
+ } else if (mode == TEGRA_SUSPEND_LP1)
+ *iram_cpu_lp1_mask = 0;
+
+ restore_cpu_complex(flags);
+
+ /* for platforms where the core & CPU power requests are
+ * combined as a single request to the PMU, transition out
+ * of LP0 state by temporarily enabling both requests
+ */
+ if (mode == TEGRA_SUSPEND_LP0 && pdata->combined_req) {
+ u32 reg;
+ reg = readl(pmc + PMC_CTRL);
+ reg |= TEGRA_POWER_CPU_PWRREQ_OE;
+ pmc_32kwritel(reg, PMC_CTRL);
+ reg &= ~TEGRA_POWER_PWRREQ_OE;
+ pmc_32kwritel(reg, PMC_CTRL);
+ }
+
+ cpu_complex_pm_exit();
+ cpu_pm_exit();
+
+ if (pdata && pdata->board_resume)
+ pdata->board_resume(mode, TEGRA_RESUME_AFTER_CPU);
+
+ local_fiq_enable();
+
+ tegra_common_resume();
+
+ return 0;
+}
+
+/*
+ * Function pointers to optional board specific function
+ */
+void (*tegra_deep_sleep)(int);
+EXPORT_SYMBOL(tegra_deep_sleep);
+
+static int tegra_suspend_prepare(void)
+{
+ if ((current_suspend_mode == TEGRA_SUSPEND_LP0) && tegra_deep_sleep)
+ tegra_deep_sleep(1);
+ return 0;
+}
+
+static void tegra_suspend_finish(void)
+{
+ if ((current_suspend_mode == TEGRA_SUSPEND_LP0) && tegra_deep_sleep)
+ tegra_deep_sleep(0);
+}
+
+static const struct platform_suspend_ops tegra_suspend_ops = {
+ .valid = suspend_valid_only_mem,
+ .prepare = tegra_suspend_prepare,
+ .finish = tegra_suspend_finish,
+ .prepare_late = tegra_suspend_prepare_late,
+ .wake = tegra_suspend_wake,
+ .enter = tegra_suspend_enter,
+};
+
+static ssize_t suspend_mode_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ char *start = buf;
+ char *end = buf + PAGE_SIZE;
+
+ start += scnprintf(start, end - start, "%s ", \
+ tegra_suspend_name[current_suspend_mode]);
+ start += scnprintf(start, end - start, "\n");
+
+ return start - buf;
+}
+
+static ssize_t suspend_mode_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t n)
+{
+ int len;
+ const char *name_ptr;
+ enum tegra_suspend_mode new_mode;
+
+ name_ptr = buf;
+ while (*name_ptr && !isspace(*name_ptr))
+ name_ptr++;
+ len = name_ptr - buf;
+ if (!len)
+ goto bad_name;
+
+ for (new_mode = TEGRA_SUSPEND_NONE; \
+ new_mode < TEGRA_MAX_SUSPEND_MODE; ++new_mode) {
+ if (!strncmp(buf, tegra_suspend_name[new_mode], len)) {
+ current_suspend_mode = new_mode;
+ break;
+ }
+ }
+
+bad_name:
+ return n;
+}
+
+static struct kobj_attribute suspend_mode_attribute =
+ __ATTR(mode, 0644, suspend_mode_show, suspend_mode_store);
+
+static struct kobject *suspend_kobj;
+#endif
+
+#ifdef CONFIG_PM_SLEEP
+static int tegra_pm_enter_suspend(void)
+{
+ pr_info("Entering suspend state %s\n", lp_state[current_suspend_mode]);
+ if (current_suspend_mode == TEGRA_SUSPEND_LP0)
+ tegra_lp0_cpu_mode(true);
+ return 0;
+}
+
+static void tegra_pm_enter_resume(void)
+{
+ if (current_suspend_mode == TEGRA_SUSPEND_LP0)
+ tegra_lp0_cpu_mode(false);
+ pr_info("Exited suspend state %s\n", lp_state[current_suspend_mode]);
+}
+
+static struct syscore_ops tegra_pm_enter_syscore_ops = {
+ .suspend = tegra_pm_enter_suspend,
+ .resume = tegra_pm_enter_resume,
+};
+
+static __init int tegra_pm_enter_syscore_init(void)
+{
+ register_syscore_ops(&tegra_pm_enter_syscore_ops);
+ return 0;
+}
+subsys_initcall(tegra_pm_enter_syscore_init);
+#endif
+
+void __init tegra_init_suspend(struct tegra_suspend_platform_data *plat)
+{
+ u32 reg;
+ u32 mode;
+
+ tegra_pclk = clk_get_sys(NULL, "pclk");
+ BUG_ON(IS_ERR(tegra_pclk));
+ pdata = plat;
+ (void)reg;
+ (void)mode;
+
+#ifndef CONFIG_PM_SLEEP
+ if (plat->suspend_mode != TEGRA_SUSPEND_NONE) {
+ pr_warning("%s: Suspend requires CONFIG_PM_SLEEP -- "
+ "disabling suspend\n", __func__);
+ plat->suspend_mode = TEGRA_SUSPEND_NONE;
+ }
+#else
+ if (create_suspend_pgtable() < 0) {
+ pr_err("%s: PGD memory alloc failed -- LP0/LP1/LP2 unavailable\n",
+ __func__);
+ plat->suspend_mode = TEGRA_SUSPEND_NONE;
+ goto fail;
+ }
+
+ if (alloc_suspend_context() < 0) {
+ pr_err("%s: CPU context alloc failed -- LP0/LP1/LP2 unavailable\n",
+ __func__);
+ plat->suspend_mode = TEGRA_SUSPEND_NONE;
+ goto fail;
+ }
+
+ if ((tegra_get_chipid() == TEGRA_CHIPID_TEGRA3) &&
+ (tegra_get_revision() == TEGRA_REVISION_A01) &&
+ (plat->suspend_mode == TEGRA_SUSPEND_LP0)) {
+ /* Tegra 3 A01 supports only LP1 */
+ pr_warning("%s: Suspend mode LP0 is not supported on A01 "
+ "-- disabling LP0\n", __func__);
+ plat->suspend_mode = TEGRA_SUSPEND_LP1;
+ }
+ if (plat->suspend_mode == TEGRA_SUSPEND_LP0 && tegra_lp0_vec_size &&
+ tegra_lp0_vec_relocate) {
+ unsigned char *reloc_lp0;
+ unsigned long tmp;
+ void __iomem *orig;
+ reloc_lp0 = kmalloc(tegra_lp0_vec_size + L1_CACHE_BYTES - 1,
+ GFP_KERNEL);
+ WARN_ON(!reloc_lp0);
+ if (!reloc_lp0) {
+ pr_err("%s: Failed to allocate reloc_lp0\n",
+ __func__);
+ goto out;
+ }
+
+ orig = ioremap(tegra_lp0_vec_start, tegra_lp0_vec_size);
+ WARN_ON(!orig);
+ if (!orig) {
+ pr_err("%s: Failed to map tegra_lp0_vec_start %08lx\n",
+ __func__, tegra_lp0_vec_start);
+ kfree(reloc_lp0);
+ goto out;
+ }
+
+ tmp = (unsigned long) reloc_lp0;
+ tmp = (tmp + L1_CACHE_BYTES - 1) & ~(L1_CACHE_BYTES - 1);
+ reloc_lp0 = (unsigned char *)tmp;
+ memcpy(reloc_lp0, orig, tegra_lp0_vec_size);
+ iounmap(orig);
+ tegra_lp0_vec_start = virt_to_phys(reloc_lp0);
+ }
+
+out:
+ if (plat->suspend_mode == TEGRA_SUSPEND_LP0 && !tegra_lp0_vec_size) {
+ pr_warning("%s: Suspend mode LP0 requested, no lp0_vec "
+ "provided by bootlader -- disabling LP0\n",
+ __func__);
+ plat->suspend_mode = TEGRA_SUSPEND_LP1;
+ }
+
+ iram_save_size = tegra_iram_end() - tegra_iram_start();
+
+ iram_save = kmalloc(iram_save_size, GFP_KERNEL);
+ if (!iram_save && (plat->suspend_mode >= TEGRA_SUSPEND_LP1)) {
+ pr_err("%s: unable to allocate memory for SDRAM self-refresh "
+ "-- LP0/LP1 unavailable\n", __func__);
+ plat->suspend_mode = TEGRA_SUSPEND_LP2;
+ }
+
+ /* !!!FIXME!!! THIS IS TEGRA2 ONLY */
+ /* Initialize scratch registers used for CPU LP2 synchronization */
+ writel(0, pmc + PMC_SCRATCH37);
+ writel(0, pmc + PMC_SCRATCH38);
+ writel(0, pmc + PMC_SCRATCH39);
+ writel(0, pmc + PMC_SCRATCH41);
+
+ /* Always enable CPU power request; just normal polarity is supported */
+ reg = readl(pmc + PMC_CTRL);
+ BUG_ON(reg & TEGRA_POWER_CPU_PWRREQ_POLARITY);
+ reg |= TEGRA_POWER_CPU_PWRREQ_OE;
+ pmc_32kwritel(reg, PMC_CTRL);
+
+ /* Configure core power request and system clock control if LP0
+ is supported */
+ __raw_writel(pdata->core_timer, pmc + PMC_COREPWRGOOD_TIMER);
+ __raw_writel(pdata->core_off_timer, pmc + PMC_COREPWROFF_TIMER);
+
+ reg = readl(pmc + PMC_CTRL);
+
+ if (!pdata->sysclkreq_high)
+ reg |= TEGRA_POWER_SYSCLK_POLARITY;
+ else
+ reg &= ~TEGRA_POWER_SYSCLK_POLARITY;
+
+ if (!pdata->corereq_high)
+ reg |= TEGRA_POWER_PWRREQ_POLARITY;
+ else
+ reg &= ~TEGRA_POWER_PWRREQ_POLARITY;
+
+ /* configure output inverters while the request is tristated */
+ pmc_32kwritel(reg, PMC_CTRL);
+
+ /* now enable requests */
+ reg |= TEGRA_POWER_SYSCLK_OE;
+ if (!pdata->combined_req)
+ reg |= TEGRA_POWER_PWRREQ_OE;
+ pmc_32kwritel(reg, PMC_CTRL);
+
+ if (pdata->suspend_mode == TEGRA_SUSPEND_LP0)
+ tegra_lp0_suspend_init();
+
+ suspend_set_ops(&tegra_suspend_ops);
+
+ /* Create /sys/power/suspend/type */
+ suspend_kobj = kobject_create_and_add("suspend", power_kobj);
+ if (suspend_kobj) {
+ if (sysfs_create_file(suspend_kobj, \
+ &suspend_mode_attribute.attr))
+ pr_err("%s: sysfs_create_file suspend type failed!\n",
+ __func__);
+ }
+
+ iram_cpu_lp2_mask = tegra_cpu_lp2_mask;
+ iram_cpu_lp1_mask = tegra_cpu_lp1_mask;
+fail:
+#endif
+ if (plat->suspend_mode == TEGRA_SUSPEND_NONE)
+ tegra_lp2_in_idle(false);
+
+ current_suspend_mode = plat->suspend_mode;
+}
+
+unsigned long debug_uart_port_base = 0;
+EXPORT_SYMBOL(debug_uart_port_base);
+
+static int tegra_debug_uart_suspend(void)
+{
+ void __iomem *uart;
+ u32 lcr;
+
+ if (!debug_uart_port_base)
+ return 0;
+
+ uart = IO_ADDRESS(debug_uart_port_base);
+
+ lcr = readb(uart + UART_LCR * 4);
+
+ tegra_sctx.uart[0] = lcr;
+ tegra_sctx.uart[1] = readb(uart + UART_MCR * 4);
+
+ /* DLAB = 0 */
+ writeb(lcr & ~UART_LCR_DLAB, uart + UART_LCR * 4);
+
+ tegra_sctx.uart[2] = readb(uart + UART_IER * 4);
+
+ /* DLAB = 1 */
+ writeb(lcr | UART_LCR_DLAB, uart + UART_LCR * 4);
+
+ tegra_sctx.uart[3] = readb(uart + UART_DLL * 4);
+ tegra_sctx.uart[4] = readb(uart + UART_DLM * 4);
+
+ writeb(lcr, uart + UART_LCR * 4);
+
+ return 0;
+}
+
+static void tegra_debug_uart_resume(void)
+{
+ void __iomem *uart;
+ u32 lcr;
+
+ if (!debug_uart_port_base)
+ return;
+
+ uart = IO_ADDRESS(debug_uart_port_base);
+
+ lcr = tegra_sctx.uart[0];
+
+ writeb(tegra_sctx.uart[1], uart + UART_MCR * 4);
+
+ /* DLAB = 0 */
+ writeb(lcr & ~UART_LCR_DLAB, uart + UART_LCR * 4);
+
+ writeb(UART_FCR_ENABLE_FIFO | UART_FCR_T_TRIG_01 | UART_FCR_R_TRIG_01,
+ uart + UART_FCR * 4);
+
+ writeb(tegra_sctx.uart[2], uart + UART_IER * 4);
+
+ /* DLAB = 1 */
+ writeb(lcr | UART_LCR_DLAB, uart + UART_LCR * 4);
+
+ writeb(tegra_sctx.uart[3], uart + UART_DLL * 4);
+ writeb(tegra_sctx.uart[4], uart + UART_DLM * 4);
+
+ writeb(lcr, uart + UART_LCR * 4);
+}
+
+static struct syscore_ops tegra_debug_uart_syscore_ops = {
+ .suspend = tegra_debug_uart_suspend,
+ .resume = tegra_debug_uart_resume,
+};
+
+struct clk *debug_uart_clk = NULL;
+EXPORT_SYMBOL(debug_uart_clk);
+
+void tegra_console_uart_suspend(void)
+{
+ if (console_suspend_enabled && debug_uart_clk)
+ clk_disable(debug_uart_clk);
+}
+
+void tegra_console_uart_resume(void)
+{
+ if (console_suspend_enabled && debug_uart_clk)
+ clk_enable(debug_uart_clk);
+}
+
+static int tegra_debug_uart_syscore_init(void)
+{
+ register_syscore_ops(&tegra_debug_uart_syscore_ops);
+ return 0;
+}
+arch_initcall(tegra_debug_uart_syscore_init);
+
+#ifdef CONFIG_DEBUG_FS
+static int tegra_suspend_debug_show(struct seq_file *s, void *data)
+{
+ seq_printf(s, "%s\n", tegra_suspend_name[*(int *)s->private]);
+ return 0;
+}
+
+static int tegra_suspend_debug_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, tegra_suspend_debug_show, inode->i_private);
+}
+
+static int tegra_suspend_debug_write(struct file *file,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ char buf[32];
+ int buf_size;
+ int i;
+ struct seq_file *s = file->private_data;
+ enum tegra_suspend_mode *val = s->private;
+
+ memset(buf, 0x00, sizeof(buf));
+ buf_size = min(count, (sizeof(buf)-1));
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+
+ for (i = 0; i < TEGRA_MAX_SUSPEND_MODE; i++) {
+ if (!strnicmp(buf, tegra_suspend_name[i],
+ strlen(tegra_suspend_name[i]))) {
+ if (i > pdata->suspend_mode)
+ return -EINVAL;
+ *val = i;
+ return count;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static const struct file_operations tegra_suspend_debug_fops = {
+ .open = tegra_suspend_debug_open,
+ .write = tegra_suspend_debug_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int __init tegra_suspend_debug_init(void)
+{
+ struct dentry *d;
+
+ d = debugfs_create_file("suspend_mode", 0755, NULL,
+ (void *)&current_suspend_mode, &tegra_suspend_debug_fops);
+ if (!d) {
+ pr_info("Failed to create suspend_mode debug file\n");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+late_initcall(tegra_suspend_debug_init);
+#endif
diff --git a/arch/arm/mach-tegra/pm.h b/arch/arm/mach-tegra/pm.h
new file mode 100644
index 000000000000..32b504fac80a
--- /dev/null
+++ b/arch/arm/mach-tegra/pm.h
@@ -0,0 +1,208 @@
+/*
+ * arch/arm/mach-tegra/include/mach/pm.h
+ *
+ * Copyright (C) 2010 Google, Inc.
+ * Copyright (C) 2010-2011 NVIDIA Corporation
+ *
+ * Author:
+ * Colin Cross <ccross@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.
+ *
+ */
+
+
+#ifndef _MACH_TEGRA_PM_H_
+#define _MACH_TEGRA_PM_H_
+
+#include <linux/mutex.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/clkdev.h>
+
+#include <mach/iomap.h>
+
+enum tegra_suspend_mode {
+ TEGRA_SUSPEND_NONE = 0,
+ TEGRA_SUSPEND_LP2, /* CPU voltage off */
+ TEGRA_SUSPEND_LP1, /* CPU voltage off, DRAM self-refresh */
+ TEGRA_SUSPEND_LP0, /* CPU + core voltage off, DRAM self-refresh */
+ TEGRA_MAX_SUSPEND_MODE,
+};
+
+enum suspend_stage {
+ TEGRA_SUSPEND_BEFORE_PERIPHERAL,
+ TEGRA_SUSPEND_BEFORE_CPU,
+};
+
+enum resume_stage {
+ TEGRA_RESUME_AFTER_PERIPHERAL,
+ TEGRA_RESUME_AFTER_CPU,
+};
+
+struct tegra_suspend_platform_data {
+ unsigned long cpu_timer; /* CPU power good time in us, LP2/LP1 */
+ unsigned long cpu_off_timer; /* CPU power off time us, LP2/LP1 */
+ unsigned long core_timer; /* core power good time in ticks, LP0 */
+ unsigned long core_off_timer; /* core power off time ticks, LP0 */
+ bool corereq_high; /* Core power request active-high */
+ bool sysclkreq_high; /* System clock request is active-high */
+ bool combined_req; /* if core & CPU power requests are combined */
+ enum tegra_suspend_mode suspend_mode;
+ unsigned long cpu_lp2_min_residency; /* Min LP2 state residency in us */
+ void (*board_suspend)(int lp_state, enum suspend_stage stg);
+ /* lp_state = 0 for LP0 state, 1 for LP1 state, 2 for LP2 state */
+ void (*board_resume)(int lp_state, enum resume_stage stg);
+};
+
+unsigned long tegra_cpu_power_good_time(void);
+unsigned long tegra_cpu_power_off_time(void);
+unsigned long tegra_cpu_lp2_min_residency(void);
+void tegra_clear_cpu_in_lp2(int cpu);
+bool tegra_set_cpu_in_lp2(int cpu);
+
+int tegra_suspend_dram(enum tegra_suspend_mode mode, unsigned int flags);
+
+#define FLOW_CTRL_CLUSTER_CONTROL \
+ (IO_ADDRESS(TEGRA_FLOW_CTRL_BASE) + 0x2c)
+#define FLOW_CTRL_CPU_CSR_IMMEDIATE_WAKE (1<<3)
+#define FLOW_CTRL_CPU_CSR_SWITCH_CLUSTER (1<<2)
+
+#define FUSE_SKU_DIRECT_CONFIG \
+ (IO_ADDRESS(TEGRA_FUSE_BASE) + 0x1F4)
+#define FUSE_SKU_DISABLE_ALL_CPUS (1<<5)
+#define FUSE_SKU_NUM_DISABLED_CPUS(x) (((x) >> 3) & 3)
+
+void __init tegra_init_suspend(struct tegra_suspend_platform_data *plat);
+
+u64 tegra_rtc_read_ms(void);
+
+/*
+ * Callbacks for platform drivers to implement.
+ */
+extern void (*tegra_deep_sleep)(int);
+
+unsigned int tegra_idle_lp2_last(unsigned int us, unsigned int flags);
+
+#if defined(CONFIG_PM_SLEEP) && !defined(CONFIG_ARCH_TEGRA_2x_SOC)
+void tegra_lp0_suspend_mc(void);
+void tegra_lp0_resume_mc(void);
+void tegra_lp0_cpu_mode(bool enter);
+#else
+static inline void tegra_lp0_suspend_mc(void) {}
+static inline void tegra_lp0_resume_mc(void) {}
+static inline void tegra_lp0_cpu_mode(bool enter) {}
+#endif
+
+#ifdef CONFIG_TEGRA_CLUSTER_CONTROL
+#define INSTRUMENT_CLUSTER_SWITCH 1 /* Should be zero for shipping code */
+#define DEBUG_CLUSTER_SWITCH 0 /* Should be zero for shipping code */
+#define PARAMETERIZE_CLUSTER_SWITCH 1 /* Should be zero for shipping code */
+
+static inline bool is_g_cluster_present(void)
+{
+ u32 fuse_sku = readl(FUSE_SKU_DIRECT_CONFIG);
+ if (fuse_sku & FUSE_SKU_DISABLE_ALL_CPUS)
+ return false;
+ return true;
+}
+static inline unsigned int is_lp_cluster(void)
+{
+ unsigned int reg;
+ reg = readl(FLOW_CTRL_CLUSTER_CONTROL);
+ return (reg & 1); /* 0 == G, 1 == LP*/
+}
+int tegra_cluster_control(unsigned int us, unsigned int flags);
+void tegra_cluster_switch_prolog(unsigned int flags);
+void tegra_cluster_switch_epilog(unsigned int flags);
+#else
+#define INSTRUMENT_CLUSTER_SWITCH 0 /* Must be zero for ARCH_TEGRA_2x_SOC */
+#define DEBUG_CLUSTER_SWITCH 0 /* Must be zero for ARCH_TEGRA_2x_SOC */
+#define PARAMETERIZE_CLUSTER_SWITCH 0 /* Must be zero for ARCH_TEGRA_2x_SOC */
+
+static inline bool is_g_cluster_present(void) { return true; }
+static inline unsigned int is_lp_cluster(void) { return 0; }
+static inline int tegra_cluster_control(unsigned int us, unsigned int flags)
+{
+ return -EPERM;
+}
+static inline void tegra_cluster_switch_prolog(unsigned int flags) {}
+static inline void tegra_cluster_switch_epilog(unsigned int flags) {}
+#endif
+
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+void tegra2_lp0_suspend_init(void);
+void tegra2_lp2_set_trigger(unsigned long cycles);
+unsigned long tegra2_lp2_timer_remain(void);
+#endif
+#ifdef CONFIG_ARCH_TEGRA_3x_SOC
+void tegra3_lp2_set_trigger(unsigned long cycles);
+unsigned long tegra3_lp2_timer_remain(void);
+#endif
+
+static inline void tegra_lp0_suspend_init(void)
+{
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+ tegra2_lp0_suspend_init();
+#endif
+}
+
+static inline void tegra_lp2_set_trigger(unsigned long cycles)
+{
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+ tegra2_lp2_set_trigger(cycles);
+#endif
+#ifdef CONFIG_ARCH_TEGRA_3x_SOC
+ tegra3_lp2_set_trigger(cycles);
+#endif
+}
+
+static inline unsigned long tegra_lp2_timer_remain(void)
+{
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+ return tegra2_lp2_timer_remain();
+#endif
+#ifdef CONFIG_ARCH_TEGRA_3x_SOC
+ return tegra3_lp2_timer_remain();
+#endif
+}
+
+#if DEBUG_CLUSTER_SWITCH && 0 /* !!!FIXME!!! THIS IS BROKEN */
+extern unsigned int tegra_cluster_debug;
+#define DEBUG_CLUSTER(x) do { if (tegra_cluster_debug) printk x; } while (0)
+#else
+#define DEBUG_CLUSTER(x) do { } while (0)
+#endif
+#if PARAMETERIZE_CLUSTER_SWITCH
+void tegra_cluster_switch_set_parameters(unsigned int us, unsigned int flags);
+#else
+static inline void tegra_cluster_switch_set_parameters(
+ unsigned int us, unsigned int flags)
+{ }
+#endif
+
+#ifdef CONFIG_SMP
+extern bool tegra_all_cpus_booted __read_mostly;
+#else
+#define tegra_all_cpus_booted (true)
+#endif
+
+#ifdef CONFIG_TRUSTED_FOUNDATIONS
+void tegra_generic_smc(u32 type, u32 subtype, u32 arg);
+#endif
+
+/* The debug channel uart base physical address */
+extern unsigned long debug_uart_port_base;
+
+extern struct clk *debug_uart_clk;
+void tegra_console_uart_suspend(void);
+void tegra_console_uart_resume(void);
+
+#endif /* _MACH_TEGRA_PM_H_ */
diff --git a/arch/arm/mach-tegra/powerdetect.c b/arch/arm/mach-tegra/powerdetect.c
new file mode 100644
index 000000000000..b759083330ba
--- /dev/null
+++ b/arch/arm/mach-tegra/powerdetect.c
@@ -0,0 +1,351 @@
+/*
+ * arch/arm/mach-tegra/powerdetect.c
+ *
+ * Copyright (c) 2011, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/notifier.h>
+#include <linux/regulator/consumer.h>
+
+#include <mach/iomap.h>
+
+#include "board.h"
+#include "fuse.h"
+
+#define PMC_PWR_IO_DISABLE 0x44
+#define PMC_PWR_DET_ENABLE 0x48
+#define PMC_PWR_DET_LATCH 0x4C
+#define PMC_PWR_DET_VAL 0xE4
+
+struct pwr_detect_cell {
+ const char *reg_id;
+ u32 pwrdet_mask;
+ u32 pwrio_mask;
+ u32 package_mask;
+
+ struct notifier_block regulator_nb;
+};
+
+static bool pwrdet_rails_found;
+static bool pwrdet_always_on;
+static bool pwrio_always_on;
+static u32 pwrdet_val;
+static u32 pwrio_val;
+static u32 pwrio_disabled_mask;
+
+static DEFINE_SPINLOCK(pwr_lock);
+
+static void __iomem *pmc_base = IO_ADDRESS(TEGRA_PMC_BASE);
+
+static inline void pmc_writel(u32 val, unsigned long addr)
+{
+ writel(val, (u32)pmc_base + addr);
+}
+static inline u32 pmc_readl(unsigned long addr)
+{
+ return readl((u32)pmc_base + addr);
+}
+
+
+#define POWER_CELL(_reg_id, _pwrdet_mask, _pwrio_mask, _package_mask) \
+ { \
+ .reg_id = _reg_id, \
+ .pwrdet_mask = _pwrdet_mask, \
+ .pwrio_mask = _pwrio_mask, \
+ .package_mask = _package_mask, \
+ }
+
+/* Some IO pads does not have power detect cells, but still can/should be
+ * turned off when no power - set pwrdet_mask=0 for such pads */
+static struct pwr_detect_cell pwr_detect_cells[] = {
+ POWER_CELL("pwrdet_nand", (0x1 << 1), (0x1 << 1), 0xFFFFFFFF),
+ POWER_CELL("pwrdet_uart", (0x1 << 2), (0x1 << 2), 0xFFFFFFFF),
+ POWER_CELL("pwrdet_bb", (0x1 << 3), (0x1 << 3), 0xFFFFFFFF),
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+ POWER_CELL("pwrdet_vi", 0, (0x1 << 4), 0xFFFFFFFF),
+#else
+ /* Tegra3 VI is connected on MID package only (id = 1, mask = 0x2) */
+ POWER_CELL("pwrdet_vi", 0, (0x1 << 4), 0x00000002),
+#endif
+ POWER_CELL("pwrdet_audio", (0x1 << 5), (0x1 << 5), 0xFFFFFFFF),
+ POWER_CELL("pwrdet_lcd", (0x1 << 6), (0x1 << 6), 0xFFFFFFFF),
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+ POWER_CELL("pwrdet_sd", 0, (0x1 << 8), 0xFFFFFFFF),
+#endif
+ POWER_CELL("pwrdet_mipi", 0, (0x1 << 9), 0xFFFFFFFF),
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+ POWER_CELL("pwrdet_cam", (0x1 << 10), (0x1 << 10), 0xFFFFFFFF),
+ POWER_CELL("pwrdet_pex_ctl", (0x1 << 11), (0x1 << 11), 0xFFFFFFFF),
+ POWER_CELL("pwrdet_sdmmc1", (0x1 << 12), (0x1 << 12), 0xFFFFFFFF),
+ POWER_CELL("pwrdet_sdmmc3", (0x1 << 13), (0x1 << 13), 0xFFFFFFFF),
+ POWER_CELL("pwrdet_sdmmc4", 0, (0x1 << 14), 0xFFFFFFFF),
+#endif
+};
+
+static void pwr_detect_reset(u32 pwrdet_mask)
+{
+ pmc_writel(pwrdet_mask, PMC_PWR_DET_ENABLE);
+ barrier();
+ pmc_writel(pwrdet_mask, PMC_PWR_DET_VAL);
+
+ pmc_readl(PMC_PWR_DET_VAL);
+ pmc_writel(0, PMC_PWR_DET_ENABLE);
+}
+
+static void pwr_detect_start(u32 pwrdet_mask)
+{
+ pmc_writel(pwrdet_mask, PMC_PWR_DET_ENABLE);
+ udelay(4);
+
+ pmc_writel(1, PMC_PWR_DET_LATCH);
+ pmc_readl(PMC_PWR_DET_LATCH);
+}
+
+static void pwr_detect_latch(void)
+{
+ pmc_writel(0, PMC_PWR_DET_LATCH);
+
+ pmc_readl(PMC_PWR_DET_VAL);
+ pmc_writel(0, PMC_PWR_DET_ENABLE);
+}
+
+static void pwr_io_enable(u32 pwrio_mask)
+{
+ u32 val = pmc_readl(PMC_PWR_IO_DISABLE);
+ val &= ~pwrio_mask;
+ pmc_writel(val, PMC_PWR_IO_DISABLE);
+}
+
+static void pwr_io_disable(u32 pwrio_mask)
+{
+ u32 val = pmc_readl(PMC_PWR_IO_DISABLE);
+ val |= pwrio_mask;
+ pmc_writel(val, PMC_PWR_IO_DISABLE);
+}
+
+static int pwrdet_always_on_set(const char *arg, const struct kernel_param *kp)
+{
+ int ret;
+ unsigned long flags;
+
+ spin_lock_irqsave(&pwr_lock, flags);
+
+ ret = param_set_bool(arg, kp);
+ if (ret) {
+ spin_unlock_irqrestore(&pwr_lock, flags);
+ return ret;
+ }
+
+ if (pwrdet_always_on)
+ pwr_detect_start(0xFFFFFFFF);
+ else
+ pwr_detect_latch();
+
+ spin_unlock_irqrestore(&pwr_lock, flags);
+ return 0;
+}
+
+static int pwrio_always_on_set(const char *arg, const struct kernel_param *kp)
+{
+ int ret;
+ unsigned long flags;
+
+ spin_lock_irqsave(&pwr_lock, flags);
+
+ ret = param_set_bool(arg, kp);
+ if (ret) {
+ spin_unlock_irqrestore(&pwr_lock, flags);
+ return ret;
+ }
+
+ if (pwrio_always_on)
+ pwr_io_enable(0xFFFFFFFF);
+ else
+ pwr_io_disable(pwrio_disabled_mask);
+
+ spin_unlock_irqrestore(&pwr_lock, flags);
+ return 0;
+}
+
+static int pwrdet_always_on_get(char *buffer, const struct kernel_param *kp)
+{
+ return param_get_bool(buffer, kp);
+}
+
+static struct kernel_param_ops pwrdet_always_on_ops = {
+ .set = pwrdet_always_on_set,
+ .get = pwrdet_always_on_get,
+};
+static struct kernel_param_ops pwrio_always_on_ops = {
+ .set = pwrio_always_on_set,
+ .get = pwrdet_always_on_get,
+};
+module_param_cb(pwrdet_always_on, &pwrdet_always_on_ops,
+ &pwrdet_always_on, 0644);
+module_param_cb(pwrio_always_on, &pwrio_always_on_ops,
+ &pwrio_always_on, 0644);
+
+static int pwrdet_val_get(char *buffer, const struct kernel_param *kp)
+{
+ pwrdet_val = pmc_readl(PMC_PWR_DET_VAL);
+ return param_get_ulong(buffer, kp);
+}
+static struct kernel_param_ops pwrdet_val_ops = {
+ .get = pwrdet_val_get,
+};
+module_param_cb(pwrdet_val, &pwrdet_val_ops, &pwrdet_val, 0444);
+
+static int pwrio_val_get(char *buffer, const struct kernel_param *kp)
+{
+ pwrio_val = pmc_readl(PMC_PWR_IO_DISABLE);
+ return param_get_ulong(buffer, kp);
+}
+static struct kernel_param_ops pwrio_val_ops = {
+ .get = pwrio_val_get,
+};
+module_param_cb(pwrio_val, &pwrio_val_ops, &pwrio_val, 0444);
+
+
+static int pwrdet_notify_cb(
+ struct notifier_block *nb, unsigned long event, void *v)
+{
+ unsigned long flags;
+ struct pwr_detect_cell *cell;
+
+ if (!pwrdet_rails_found)
+ return NOTIFY_OK;
+
+ cell = container_of(nb, struct pwr_detect_cell, regulator_nb);
+
+ spin_lock_irqsave(&pwr_lock, flags);
+
+ switch (event) {
+ case REGULATOR_EVENT_PRE_ENABLE:
+ pwrio_disabled_mask &= ~cell->pwrio_mask;
+ if (!pwrio_always_on)
+ pwr_io_enable(cell->pwrio_mask);
+ /* fall thru */
+ case REGULATOR_EVENT_OUT_PRECHANGE:
+ if (!pwrdet_always_on && cell->pwrdet_mask)
+ pwr_detect_reset(cell->pwrdet_mask);
+ break;
+
+ case REGULATOR_EVENT_POST_ENABLE:
+ case REGULATOR_EVENT_OUT_POSTCHANGE:
+ if (!pwrdet_always_on && cell->pwrdet_mask) {
+ pwr_detect_start(cell->pwrdet_mask);
+ pwr_detect_latch();
+ }
+ break;
+
+ case REGULATOR_EVENT_DISABLE:
+ case REGULATOR_EVENT_FORCE_DISABLE:
+ pwrio_disabled_mask |= cell->pwrio_mask;
+ if (!pwrio_always_on)
+ pwr_io_disable(cell->pwrio_mask);
+ break;
+ }
+
+ pr_debug("tegra: %s: event %lu, pwrdet 0x%x, pwrio 0x%x\n",
+ cell->reg_id, event,
+ pmc_readl(PMC_PWR_DET_VAL), pmc_readl(PMC_PWR_IO_DISABLE));
+ spin_unlock_irqrestore(&pwr_lock, flags);
+
+ return NOTIFY_OK;
+}
+
+static int __init pwr_detect_cell_init_one(
+ struct pwr_detect_cell *cell, u32 *disabled_mask)
+{
+ int ret;
+ struct regulator *regulator = regulator_get(NULL, cell->reg_id);
+
+ if (IS_ERR(regulator))
+ return PTR_ERR(regulator);
+
+ cell->regulator_nb.notifier_call = pwrdet_notify_cb;
+ ret = regulator_register_notifier(regulator, &cell->regulator_nb);
+ if (ret) {
+ regulator_put(regulator);
+ return ret;
+ }
+
+ if (!regulator_is_enabled(regulator))
+ *disabled_mask |= cell->pwrio_mask;
+
+ regulator_put(regulator);
+ return 0;
+}
+
+int __init tegra_pwr_detect_cell_init(void)
+{
+ int i, ret;
+ u32 package_mask;
+ unsigned long flags;
+ bool rails_found = true;
+
+ i = tegra_package_id();
+ if ((i != -1) && (i & (~0x1F))) {
+ pr_err("tegra: not supported package id %d - io power detection"
+ " is left always on\n", i);
+ return 0;
+ }
+ package_mask = (i == -1) ? i : (0x1 << i);
+
+ for (i = 0; i < ARRAY_SIZE(pwr_detect_cells); i++) {
+ struct pwr_detect_cell *cell = &pwr_detect_cells[i];
+
+ if (!(cell->package_mask & package_mask)) {
+ pwrio_disabled_mask |= cell->pwrio_mask;
+ continue;
+ }
+
+ ret = pwr_detect_cell_init_one(cell, &pwrio_disabled_mask);
+ if (ret) {
+ pr_err("tegra: failed to map regulator to power detect"
+ " cell %s(%d)\n", cell->reg_id, ret);
+ rails_found = false;
+ }
+ }
+
+ if (!rails_found) {
+ pr_err("tegra: failed regulators mapping - io power detection"
+ " is left always on\n");
+ return 0;
+ }
+ pwrdet_rails_found = true;
+
+ /* Latch initial i/o power levels, disable all detection cells
+ and not powered interfaces */
+ spin_lock_irqsave(&pwr_lock, flags);
+ if (!pwrdet_always_on)
+ pwr_detect_latch();
+ if (!pwrio_always_on)
+ pwr_io_disable(pwrio_disabled_mask);
+ spin_unlock_irqrestore(&pwr_lock, flags);
+
+ pr_info("tegra: started io power detection dynamic control\n");
+ pr_info("tegra: NO_IO_POWER setting 0x%x\n", pwrio_disabled_mask);
+
+ return 0;
+}
+
+fs_initcall(tegra_pwr_detect_cell_init);
diff --git a/arch/arm/mach-tegra/powergate.c b/arch/arm/mach-tegra/powergate.c
index 3cee9aa1f2c8..8efdf44dd3e8 100644
--- a/arch/arm/mach-tegra/powergate.c
+++ b/arch/arm/mach-tegra/powergate.c
@@ -2,6 +2,7 @@
* drivers/powergate/tegra-powergate.c
*
* Copyright (c) 2010 Google, Inc
+ * Copyright (C) 2011 NVIDIA Corporation.
*
* Author:
* Colin Cross <ccross@google.com>
@@ -31,15 +32,135 @@
#include <mach/iomap.h>
#include <mach/powergate.h>
+#include "clock.h"
+
#define PWRGATE_TOGGLE 0x30
-#define PWRGATE_TOGGLE_START (1 << 8)
+#define PWRGATE_TOGGLE_START (1 << 8)
#define REMOVE_CLAMPING 0x34
#define PWRGATE_STATUS 0x38
+#if !defined(CONFIG_ARCH_TEGRA_2x_SOC)
+enum mc_client {
+ MC_CLIENT_AFI = 0,
+ MC_CLIENT_AVPC = 1,
+ MC_CLIENT_DC = 2,
+ MC_CLIENT_DCB = 3,
+ MC_CLIENT_EPP = 4,
+ MC_CLIENT_G2 = 5,
+ MC_CLIENT_HC = 6,
+ MC_CLIENT_HDA = 7,
+ MC_CLIENT_ISP = 8,
+ MC_CLIENT_MPCORE = 9,
+ MC_CLIENT_MPCORELP = 10,
+ MC_CLIENT_MPE = 11,
+ MC_CLIENT_NV = 12,
+ MC_CLIENT_NV2 = 13,
+ MC_CLIENT_PPCS = 14,
+ MC_CLIENT_SATA = 15,
+ MC_CLIENT_VDE = 16,
+ MC_CLIENT_VI = 17,
+ MC_CLIENT_LAST = -1,
+};
+#else
+enum mc_client {
+ MC_CLIENT_AVPC = 0,
+ MC_CLIENT_DC = 1,
+ MC_CLIENT_DCB = 2,
+ MC_CLIENT_EPP = 3,
+ MC_CLIENT_G2 = 4,
+ MC_CLIENT_HC = 5,
+ MC_CLIENT_ISP = 6,
+ MC_CLIENT_MPCORE = 7,
+ MC_CLIENT_MPEA = 8,
+ MC_CLIENT_MPEB = 9,
+ MC_CLIENT_MPEC = 10,
+ MC_CLIENT_NV = 11,
+ MC_CLIENT_PPCS = 12,
+ MC_CLIENT_VDE = 13,
+ MC_CLIENT_VI = 14,
+ MC_CLIENT_LAST = -1,
+ MC_CLIENT_AFI = MC_CLIENT_LAST,
+};
+#endif
+
+#define MAX_CLK_EN_NUM 4
+
static DEFINE_SPINLOCK(tegra_powergate_lock);
+#define MAX_HOTRESET_CLIENT_NUM 4
+
+enum clk_type {
+ CLK_AND_RST,
+ RST_ONLY,
+ CLK_ONLY,
+};
+
+struct partition_clk_info {
+ const char *clk_name;
+ enum clk_type clk_type;
+ /* true if clk is only used in assert/deassert reset and not while enable-den*/
+ struct clk *clk_ptr;
+};
+
+struct powergate_partition {
+ const char *name;
+ enum mc_client hot_reset_clients[MAX_HOTRESET_CLIENT_NUM];
+ struct partition_clk_info clk_info[MAX_CLK_EN_NUM];
+};
+
+static struct powergate_partition powergate_partition_info[TEGRA_NUM_POWERGATE] = {
+ [TEGRA_POWERGATE_CPU] = { "cpu0", {MC_CLIENT_LAST}, },
+ [TEGRA_POWERGATE_L2] = { "l2", {MC_CLIENT_LAST}, },
+ [TEGRA_POWERGATE_3D] = { "3d0",
+ {MC_CLIENT_NV, MC_CLIENT_LAST},
+ {{"3d", CLK_AND_RST} }, },
+ [TEGRA_POWERGATE_PCIE] = { "pcie",
+ {MC_CLIENT_AFI, MC_CLIENT_LAST},
+ {{"afi", CLK_AND_RST},
+ {"pcie", CLK_AND_RST},
+ {"pciex", RST_ONLY} }, },
+ [TEGRA_POWERGATE_VDEC] = { "vde",
+ {MC_CLIENT_VDE, MC_CLIENT_LAST},
+ {{"vde", CLK_AND_RST} }, },
+ [TEGRA_POWERGATE_MPE] = { "mpe",
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+ {MC_CLIENT_MPE, MC_CLIENT_LAST},
+#else
+ {MC_CLIENT_MPEA, MC_CLIENT_MPEB,
+ MC_CLIENT_MPEC, MC_CLIENT_LAST},
+#endif
+ {{"mpe", CLK_AND_RST} }, },
+ [TEGRA_POWERGATE_VENC] = { "ve",
+ {MC_CLIENT_ISP, MC_CLIENT_VI, MC_CLIENT_LAST},
+ {{"isp", CLK_AND_RST},
+ {"vi", CLK_AND_RST},
+ {"csi", CLK_AND_RST} }, },
+#if !defined(CONFIG_ARCH_TEGRA_2x_SOC)
+ [TEGRA_POWERGATE_CPU1] = { "cpu1", {MC_CLIENT_LAST}, },
+ [TEGRA_POWERGATE_CPU2] = { "cpu2", {MC_CLIENT_LAST}, },
+ [TEGRA_POWERGATE_CPU3] = { "cpu3", {MC_CLIENT_LAST}, },
+ [TEGRA_POWERGATE_CELP] = { "celp", {MC_CLIENT_LAST}, },
+ [TEGRA_POWERGATE_SATA] = { "sata", {MC_CLIENT_SATA, MC_CLIENT_LAST},
+ {{"sata", CLK_AND_RST},
+ {"sata_oob", CLK_AND_RST},
+ {"cml1", CLK_ONLY},
+ {"sata_cold", RST_ONLY} }, },
+ [TEGRA_POWERGATE_3D1] = { "3d1",
+ {MC_CLIENT_NV2, MC_CLIENT_LAST},
+ {{"3d2", CLK_AND_RST} }, },
+ [TEGRA_POWERGATE_HEG] = { "heg",
+ {MC_CLIENT_G2, MC_CLIENT_EPP,
+ MC_CLIENT_HC,
+ MC_CLIENT_LAST},
+ {{"2d", CLK_AND_RST},
+ {"epp", CLK_AND_RST},
+ {"host1x", CLK_AND_RST},
+ {"3d", RST_ONLY} }, },
+#endif
+};
+
static void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE);
static u32 pmc_read(unsigned long reg)
@@ -52,40 +173,306 @@ static void pmc_write(u32 val, unsigned long reg)
writel(val, pmc + reg);
}
+static void __iomem *mc = IO_ADDRESS(TEGRA_MC_BASE);
+
+static u32 mc_read(unsigned long reg)
+{
+ return readl(mc + reg);
+}
+
+static void mc_write(u32 val, unsigned long reg)
+{
+ writel(val, mc + reg);
+}
+
+#if !defined(CONFIG_ARCH_TEGRA_2x_SOC)
+
+#define MC_CLIENT_HOTRESET_CTRL 0x200
+#define MC_CLIENT_HOTRESET_STAT 0x204
+
+static void mc_flush(int id)
+{
+ u32 idx, rst_ctrl, rst_stat;
+ enum mc_client mcClientBit;
+ unsigned long flags;
+
+ BUG_ON(id < 0 || id >= TEGRA_NUM_POWERGATE);
+
+ for (idx = 0; idx < MAX_HOTRESET_CLIENT_NUM; idx++) {
+ mcClientBit = powergate_partition_info[id].hot_reset_clients[idx];
+ if (mcClientBit == MC_CLIENT_LAST)
+ break;
+
+ spin_lock_irqsave(&tegra_powergate_lock, flags);
+ rst_ctrl = mc_read(MC_CLIENT_HOTRESET_CTRL);
+ rst_ctrl |= (1 << mcClientBit);
+ mc_write(rst_ctrl, MC_CLIENT_HOTRESET_CTRL);
+
+ spin_unlock_irqrestore(&tegra_powergate_lock, flags);
+
+ do {
+ udelay(10);
+ rst_stat = mc_read(MC_CLIENT_HOTRESET_STAT);
+ } while (!(rst_stat & (1 << mcClientBit)));
+ }
+}
+
+static void mc_flush_done(int id)
+{
+ u32 idx, rst_ctrl;
+ enum mc_client mcClientBit;
+ unsigned long flags;
+
+ BUG_ON(id < 0 || id >= TEGRA_NUM_POWERGATE);
+
+ for (idx = 0; idx < MAX_HOTRESET_CLIENT_NUM; idx++) {
+ mcClientBit = powergate_partition_info[id].hot_reset_clients[idx];
+ if (mcClientBit == MC_CLIENT_LAST)
+ break;
+
+ spin_lock_irqsave(&tegra_powergate_lock, flags);
+
+ rst_ctrl = mc_read(MC_CLIENT_HOTRESET_CTRL);
+ rst_ctrl &= ~(1 << mcClientBit);
+ mc_write(rst_ctrl, MC_CLIENT_HOTRESET_CTRL);
+
+ spin_unlock_irqrestore(&tegra_powergate_lock, flags);
+ }
+
+ wmb();
+}
+
+int tegra_powergate_mc_flush(int id)
+{
+ if (id < 0 || id >= TEGRA_NUM_POWERGATE)
+ return -EINVAL;
+ mc_flush(id);
+ return 0;
+}
+
+int tegra_powergate_mc_flush_done(int id)
+{
+ if (id < 0 || id >= TEGRA_NUM_POWERGATE)
+ return -EINVAL;
+ mc_flush_done(id);
+ return 0;
+}
+
+int tegra_powergate_mc_disable(int id)
+{
+ return 0;
+}
+
+int tegra_powergate_mc_enable(int id)
+{
+ return 0;
+}
+
+#else
+
+#define MC_CLIENT_CTRL 0x100
+#define MC_CLIENT_HOTRESETN 0x104
+#define MC_CLIENT_ORRC_BASE 0x140
+
+int tegra_powergate_mc_disable(int id)
+{
+ u32 idx, clt_ctrl, orrc_reg;
+ enum mc_client mcClientBit;
+ unsigned long flags;
+
+ if (id < 0 || id >= TEGRA_NUM_POWERGATE) {
+ WARN_ON(1);
+ return -EINVAL;
+ }
+
+ for (idx = 0; idx < MAX_HOTRESET_CLIENT_NUM; idx++) {
+ mcClientBit =
+ powergate_partition_info[id].hot_reset_clients[idx];
+ if (mcClientBit == MC_CLIENT_LAST)
+ break;
+
+ spin_lock_irqsave(&tegra_powergate_lock, flags);
+
+ /* clear client enable bit */
+ clt_ctrl = mc_read(MC_CLIENT_CTRL);
+ clt_ctrl &= ~(1 << mcClientBit);
+ mc_write(clt_ctrl, MC_CLIENT_CTRL);
+
+ /* read back to flush write */
+ clt_ctrl = mc_read(MC_CLIENT_CTRL);
+
+ spin_unlock_irqrestore(&tegra_powergate_lock, flags);
+
+ /* wait for outstanding requests to reach 0 */
+ orrc_reg = MC_CLIENT_ORRC_BASE + (mcClientBit * 4);
+ while (mc_read(orrc_reg) != 0)
+ udelay(10);
+ }
+ return 0;
+}
+
+int tegra_powergate_mc_flush(int id)
+{
+ u32 idx, hot_rstn;
+ enum mc_client mcClientBit;
+ unsigned long flags;
+
+ if (id < 0 || id >= TEGRA_NUM_POWERGATE) {
+ WARN_ON(1);
+ return -EINVAL;
+ }
+
+ for (idx = 0; idx < MAX_HOTRESET_CLIENT_NUM; idx++) {
+ mcClientBit =
+ powergate_partition_info[id].hot_reset_clients[idx];
+ if (mcClientBit == MC_CLIENT_LAST)
+ break;
+
+ spin_lock_irqsave(&tegra_powergate_lock, flags);
+
+ /* assert hotreset (client module is currently in reset) */
+ hot_rstn = mc_read(MC_CLIENT_HOTRESETN);
+ hot_rstn &= ~(1 << mcClientBit);
+ mc_write(hot_rstn, MC_CLIENT_HOTRESETN);
+
+ /* read back to flush write */
+ hot_rstn = mc_read(MC_CLIENT_HOTRESETN);
+
+ spin_unlock_irqrestore(&tegra_powergate_lock, flags);
+ }
+ return 0;
+}
+
+int tegra_powergate_mc_flush_done(int id)
+{
+ u32 idx, hot_rstn;
+ enum mc_client mcClientBit;
+ unsigned long flags;
+
+ if (id < 0 || id >= TEGRA_NUM_POWERGATE) {
+ WARN_ON(1);
+ return -EINVAL;
+ }
+
+ for (idx = 0; idx < MAX_HOTRESET_CLIENT_NUM; idx++) {
+ mcClientBit =
+ powergate_partition_info[id].hot_reset_clients[idx];
+ if (mcClientBit == MC_CLIENT_LAST)
+ break;
+
+ spin_lock_irqsave(&tegra_powergate_lock, flags);
+
+ /* deassert hotreset */
+ hot_rstn = mc_read(MC_CLIENT_HOTRESETN);
+ hot_rstn |= (1 << mcClientBit);
+ mc_write(hot_rstn, MC_CLIENT_HOTRESETN);
+
+ /* read back to flush write */
+ hot_rstn = mc_read(MC_CLIENT_HOTRESETN);
+
+ spin_unlock_irqrestore(&tegra_powergate_lock, flags);
+ }
+ return 0;
+}
+
+int tegra_powergate_mc_enable(int id)
+{
+ u32 idx, clt_ctrl;
+ enum mc_client mcClientBit;
+ unsigned long flags;
+
+ if (id < 0 || id >= TEGRA_NUM_POWERGATE) {
+ WARN_ON(1);
+ return -EINVAL;
+ }
+
+ for (idx = 0; idx < MAX_HOTRESET_CLIENT_NUM; idx++) {
+ mcClientBit =
+ powergate_partition_info[id].hot_reset_clients[idx];
+ if (mcClientBit == MC_CLIENT_LAST)
+ break;
+
+ spin_lock_irqsave(&tegra_powergate_lock, flags);
+
+ /* enable client */
+ clt_ctrl = mc_read(MC_CLIENT_CTRL);
+ clt_ctrl |= (1 << mcClientBit);
+ mc_write(clt_ctrl, MC_CLIENT_CTRL);
+
+ /* read back to flush write */
+ clt_ctrl = mc_read(MC_CLIENT_CTRL);
+
+ spin_unlock_irqrestore(&tegra_powergate_lock, flags);
+ }
+ return 0;
+}
+
+static void mc_flush(int id) {}
+static void mc_flush_done(int id) {}
+#endif
+
static int tegra_powergate_set(int id, bool new_state)
{
bool status;
unsigned long flags;
+ /* 10us timeout for toggle operation if it takes affect*/
+ int toggle_timeout = 10;
+ /* 100 * 10 = 1000us timeout for toggle command to take affect in case
+ of contention with h/w initiated CPU power gating */
+ int contention_timeout = 100;
spin_lock_irqsave(&tegra_powergate_lock, flags);
- status = pmc_read(PWRGATE_STATUS) & (1 << id);
+ status = !!(pmc_read(PWRGATE_STATUS) & (1 << id));
if (status == new_state) {
spin_unlock_irqrestore(&tegra_powergate_lock, flags);
- return -EINVAL;
+ return 0;
+ }
+
+ if (TEGRA_IS_CPU_POWERGATE_ID(id)) {
+ /* CPU ungated in s/w only during boot/resume with outer
+ waiting loop and no contention from other CPUs */
+ pmc_write(PWRGATE_TOGGLE_START | id, PWRGATE_TOGGLE);
+ spin_unlock_irqrestore(&tegra_powergate_lock, flags);
+ return 0;
}
- pmc_write(PWRGATE_TOGGLE_START | id, PWRGATE_TOGGLE);
+ do {
+ pmc_write(PWRGATE_TOGGLE_START | id, PWRGATE_TOGGLE);
+ do {
+ udelay(1);
+ status = !!(pmc_read(PWRGATE_STATUS) & (1 << id));
+
+ toggle_timeout--;
+ } while ((status != new_state) && (toggle_timeout > 0));
+
+ contention_timeout--;
+ } while ((status != new_state) && (contention_timeout > 0));
spin_unlock_irqrestore(&tegra_powergate_lock, flags);
+ if (status != new_state) {
+ WARN(1, "Could not set powergate %d to %d", id, new_state);
+ return -EBUSY;
+ }
+
return 0;
}
-int tegra_powergate_power_on(int id)
+static int unpowergate_module(int id)
{
if (id < 0 || id >= TEGRA_NUM_POWERGATE)
return -EINVAL;
-
return tegra_powergate_set(id, true);
}
-int tegra_powergate_power_off(int id)
+static int powergate_module(int id)
{
if (id < 0 || id >= TEGRA_NUM_POWERGATE)
return -EINVAL;
+ mc_flush(id);
return tegra_powergate_set(id, false);
}
@@ -94,7 +481,7 @@ bool tegra_powergate_is_powered(int id)
u32 status;
if (id < 0 || id >= TEGRA_NUM_POWERGATE)
- return -EINVAL;
+ return false;
status = pmc_read(PWRGATE_STATUS) & (1 << id);
return !!status;
@@ -103,17 +490,16 @@ bool tegra_powergate_is_powered(int id)
int tegra_powergate_remove_clamping(int id)
{
u32 mask;
-
if (id < 0 || id >= TEGRA_NUM_POWERGATE)
return -EINVAL;
/*
- * Tegra 2 has a bug where PCIE and VDE clamping masks are
- * swapped relatively to the partition ids
+ * PCIE and VDE clamping masks are swapped with respect to their
+ * partition ids
*/
if (id == TEGRA_POWERGATE_VDEC)
mask = (1 << TEGRA_POWERGATE_PCIE);
- else if (id == TEGRA_POWERGATE_PCIE)
+ else if (id == TEGRA_POWERGATE_PCIE)
mask = (1 << TEGRA_POWERGATE_VDEC);
else
mask = (1 << id);
@@ -123,20 +509,184 @@ int tegra_powergate_remove_clamping(int id)
return 0;
}
-/* Must be called with clk disabled, and returns with clk enabled */
-int tegra_powergate_sequence_power_up(int id, struct clk *clk)
+static void get_clk_info(int id)
+{
+ int idx;
+
+ for (idx = 0; idx < MAX_CLK_EN_NUM; idx++) {
+ if (!powergate_partition_info[id].clk_info[idx].clk_name)
+ break;
+ powergate_partition_info[id].
+ clk_info[idx].clk_ptr =
+ tegra_get_clock_by_name(
+ powergate_partition_info[id].clk_info[idx].clk_name);
+ }
+}
+
+static int partition_clk_enable(int id)
+{
+ int ret;
+ u32 idx;
+ struct clk *clk;
+ struct partition_clk_info *clk_info;
+
+ BUG_ON(id < 0 || id >= TEGRA_NUM_POWERGATE);
+
+ for (idx = 0; idx < MAX_CLK_EN_NUM; idx++) {
+ clk_info = &powergate_partition_info[id].clk_info[idx];
+ clk = clk_info->clk_ptr;
+ if (!clk)
+ break;
+
+ if (clk_info->clk_type != RST_ONLY) {
+ ret = clk_enable(clk);
+ if (ret)
+ goto err_clk_en;
+ }
+ }
+
+ return 0;
+
+err_clk_en:
+ WARN(1, "Could not enable clk %s", clk->name);
+ while (idx--) {
+ clk_info = &powergate_partition_info[id].clk_info[idx];
+ if (clk_info->clk_type != RST_ONLY)
+ clk_disable(clk_info->clk_ptr);
+ }
+
+ return ret;
+}
+
+static int is_partition_clk_disabled(int id)
+{
+ u32 idx;
+ struct clk *clk;
+ struct partition_clk_info *clk_info;
+ int ret = 0;
+
+ BUG_ON(id < 0 || id >= TEGRA_NUM_POWERGATE);
+
+ for (idx = 0; idx < MAX_CLK_EN_NUM; idx++) {
+ clk_info = &powergate_partition_info[id].clk_info[idx];
+ clk = clk_info->clk_ptr;
+ if (!clk)
+ break;
+
+ if (clk_info->clk_type != RST_ONLY) {
+ if (tegra_is_clk_enabled(clk)) {
+ ret = -1;
+ break;
+ }
+ }
+ }
+
+ return ret;
+}
+
+static void partition_clk_disable(int id)
+{
+ u32 idx;
+ struct clk *clk;
+ struct partition_clk_info *clk_info;
+
+ BUG_ON(id < 0 || id >= TEGRA_NUM_POWERGATE);
+
+ for (idx = 0; idx < MAX_CLK_EN_NUM; idx++) {
+ clk_info = &powergate_partition_info[id].clk_info[idx];
+ clk = clk_info->clk_ptr;
+ if (!clk)
+ break;
+
+ if (clk_info->clk_type != RST_ONLY)
+ clk_disable(clk);
+ }
+}
+
+static void powergate_partition_assert_reset(int id)
+{
+ u32 idx;
+ struct clk *clk_ptr;
+ struct partition_clk_info *clk_info;
+
+ BUG_ON(id < 0 || id >= TEGRA_NUM_POWERGATE);
+
+ for (idx = 0; idx < MAX_CLK_EN_NUM; idx++) {
+ clk_info = &powergate_partition_info[id].clk_info[idx];
+ clk_ptr = clk_info->clk_ptr;
+ if (!clk_ptr)
+ break;
+ if (clk_info->clk_type != CLK_ONLY)
+ tegra_periph_reset_assert(clk_ptr);
+ }
+}
+
+static void powergate_partition_deassert_reset(int id)
+{
+ u32 idx;
+ struct clk *clk_ptr;
+ struct partition_clk_info *clk_info;
+
+ BUG_ON(id < 0 || id >= TEGRA_NUM_POWERGATE);
+
+ for (idx = 0; idx < MAX_CLK_EN_NUM; idx++) {
+ clk_info = &powergate_partition_info[id].clk_info[idx];
+ clk_ptr = clk_info->clk_ptr;
+ if (!clk_ptr)
+ break;
+ if (clk_info->clk_type != CLK_ONLY)
+ tegra_periph_reset_deassert(clk_ptr);
+ }
+}
+
+/* Must be called with clk disabled, and returns with clk disabled */
+static int tegra_powergate_reset_module(int id)
+{
+ int ret;
+
+ powergate_partition_assert_reset(id);
+
+ udelay(10);
+
+ ret = partition_clk_enable(id);
+ if (ret)
+ return ret;
+
+ udelay(10);
+
+ powergate_partition_deassert_reset(id);
+
+ partition_clk_disable(id);
+
+ return 0;
+}
+
+/*
+ * Must be called with clk disabled, and returns with clk disabled
+ * Drivers should enable clks for partition. Unpowergates only the
+ * partition.
+ */
+int tegra_unpowergate_partition(int id)
{
int ret;
- tegra_periph_reset_assert(clk);
+ /* If first clk_ptr is null, fill clk info for the partition */
+ if (!powergate_partition_info[id].clk_info[0].clk_ptr)
+ get_clk_info(id);
- ret = tegra_powergate_power_on(id);
+ if (tegra_powergate_is_powered(id))
+ return tegra_powergate_reset_module(id);
+
+ ret = unpowergate_module(id);
if (ret)
goto err_power;
- ret = clk_enable(clk);
+ powergate_partition_assert_reset(id);
+
+ /* Un-Powergating fails if all clks are not enabled */
+ ret = partition_clk_enable(id);
if (ret)
- goto err_clk;
+ goto err_clk_on;
udelay(10);
@@ -145,29 +695,130 @@ int tegra_powergate_sequence_power_up(int id, struct clk *clk)
goto err_clamp;
udelay(10);
- tegra_periph_reset_deassert(clk);
+ powergate_partition_deassert_reset(id);
+
+ mc_flush_done(id);
+
+ /* Disable all clks enabled earlier. Drivers should enable clks */
+ partition_clk_disable(id);
return 0;
err_clamp:
- clk_disable(clk);
-err_clk:
- tegra_powergate_power_off(id);
+ partition_clk_disable(id);
+err_clk_on:
+ powergate_module(id);
err_power:
+ WARN(1, "Could not Un-Powergate %d", id);
return ret;
}
-#ifdef CONFIG_DEBUG_FS
+/*
+ * Must be called with clk disabled, and returns with clk enabled
+ * Unpowergates the partition and enables all required clks.
+ */
+int tegra_unpowergate_partition_with_clk_on(int id)
+{
+ int ret = 0;
+
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+ /* Restrict this functions use to few partitions */
+ BUG_ON(id != TEGRA_POWERGATE_SATA && id != TEGRA_POWERGATE_PCIE);
+#else
+ /* Restrict this functions use to few partitions */
+ BUG_ON(id != TEGRA_POWERGATE_PCIE);
+#endif
-static const char * const powergate_name[] = {
- [TEGRA_POWERGATE_CPU] = "cpu",
- [TEGRA_POWERGATE_3D] = "3d",
- [TEGRA_POWERGATE_VENC] = "venc",
- [TEGRA_POWERGATE_VDEC] = "vdec",
- [TEGRA_POWERGATE_PCIE] = "pcie",
- [TEGRA_POWERGATE_L2] = "l2",
- [TEGRA_POWERGATE_MPE] = "mpe",
-};
+ ret = tegra_unpowergate_partition(id);
+ if (ret)
+ goto err_unpowergating;
+
+ /* Enable clks for the partition */
+ ret = partition_clk_enable(id);
+ if (ret)
+ goto err_unpowergate_clk;
+
+ return ret;
+
+err_unpowergate_clk:
+ tegra_powergate_partition(id);
+ WARN(1, "Could not Un-Powergate %d, err in enabling clk", id);
+err_unpowergating:
+ WARN(1, "Could not Un-Powergate %d", id);
+ return ret;
+}
+
+/*
+ * Must be called with clk disabled. Powergates the partition only
+ */
+int tegra_powergate_partition(int id)
+{
+ int ret;
+
+ /* If first clk_ptr is null, fill clk info for the partition */
+ if (powergate_partition_info[id].clk_info[0].clk_ptr)
+ get_clk_info(id);
+ powergate_partition_assert_reset(id);
+
+ /* Powergating is done only if refcnt of all clks is 0 */
+ ret = is_partition_clk_disabled(id);
+ if (ret)
+ goto err_clk_off;
+
+ ret = powergate_module(id);
+ if (ret)
+ goto err_power_off;
+
+ return 0;
+
+err_power_off:
+ WARN(1, "Could not Powergate Partition %d", id);
+err_clk_off:
+ WARN(1, "Could not Powergate Partition %d, all clks not disabled", id);
+ return ret;
+}
+
+int tegra_powergate_partition_with_clk_off(int id)
+{
+ int ret = 0;
+
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+ /* Restrict functions use to selected partitions */
+ BUG_ON(id != TEGRA_POWERGATE_PCIE && id != TEGRA_POWERGATE_SATA);
+#else
+ /* Restrict functions use to selected partitions */
+ BUG_ON(id != TEGRA_POWERGATE_PCIE);
+#endif
+ /* Disable clks for the partition */
+ partition_clk_disable(id);
+
+ ret = is_partition_clk_disabled(id);
+ if (ret)
+ goto err_powergate_clk;
+
+ ret = tegra_powergate_partition(id);
+ if (ret)
+ goto err_powergating;
+
+ return ret;
+
+err_powergate_clk:
+ WARN(1, "Could not Powergate Partition %d, all clks not disabled", id);
+err_powergating:
+ partition_clk_enable(id);
+ WARN(1, "Could not Powergate Partition %d", id);
+ return ret;
+}
+
+const char *tegra_powergate_get_name(int id)
+{
+ if (id < 0 || id >= TEGRA_NUM_POWERGATE)
+ return "invalid";
+
+ return powergate_partition_info[id].name;
+}
+
+#ifdef CONFIG_DEBUG_FS
static int powergate_show(struct seq_file *s, void *data)
{
@@ -177,7 +828,7 @@ static int powergate_show(struct seq_file *s, void *data)
seq_printf(s, "------------------\n");
for (i = 0; i < TEGRA_NUM_POWERGATE; i++)
- seq_printf(s, " %9s %7s\n", powergate_name[i],
+ seq_printf(s, " %9s %7s\n", powergate_partition_info[i].name,
tegra_powergate_is_powered(i) ? "yes" : "no");
return 0;
}
@@ -197,14 +848,13 @@ static const struct file_operations powergate_fops = {
static int __init powergate_debugfs_init(void)
{
struct dentry *d;
- int err = -ENOMEM;
d = debugfs_create_file("powergate", S_IRUGO, NULL, NULL,
&powergate_fops);
if (!d)
return -ENOMEM;
- return err;
+ return 0;
}
late_initcall(powergate_debugfs_init);
diff --git a/arch/arm/mach-tegra/pwm.c b/arch/arm/mach-tegra/pwm.c
new file mode 100644
index 000000000000..a268c391cb27
--- /dev/null
+++ b/arch/arm/mach-tegra/pwm.c
@@ -0,0 +1,296 @@
+/*
+ * arch/arm/mach-tegra/pwm.c
+ *
+ * Tegra pulse-width-modulation controller driver
+ *
+ * Copyright (c) 2010, NVIDIA Corporation.
+ * Based on arch/arm/plat-mxc/pwm.c by Sascha Hauer <s.hauer@pengutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/slab.h>
+
+#define PWM_ENABLE (1 << 31)
+#define PWM_DUTY_WIDTH 8
+#define PWM_DUTY_SHIFT 16
+#define PWM_SCALE_WIDTH 13
+#define PWM_SCALE_SHIFT 0
+
+struct pwm_device {
+ struct list_head node;
+ struct platform_device *pdev;
+
+ const char *label;
+ struct clk *clk;
+
+ int clk_enb;
+ void __iomem *mmio_base;
+
+ unsigned int in_use;
+ unsigned int id;
+};
+
+static DEFINE_MUTEX(pwm_lock);
+static LIST_HEAD(pwm_list);
+
+static inline int pwm_writel(struct pwm_device *pwm, unsigned long val)
+{
+ int rc;
+
+ rc = clk_enable(pwm->clk);
+ if (WARN_ON(rc))
+ return rc;
+ writel(val, pwm->mmio_base);
+ clk_disable(pwm->clk);
+ return 0;
+}
+
+int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
+{
+ unsigned long long c;
+ unsigned long rate, hz;
+ u32 val = 0;
+
+ /* convert from duty_ns / period_ns to a fixed number of duty
+ * ticks per (1 << PWM_DUTY_WIDTH) cycles. */
+ c = duty_ns * ((1 << PWM_DUTY_WIDTH) - 1);
+ do_div(c, period_ns);
+
+ val = (u32)c << PWM_DUTY_SHIFT;
+
+ /* compute the prescaler value for which (1 << PWM_DUTY_WIDTH)
+ * cycles at the PWM clock rate will take period_ns nanoseconds. */
+ rate = clk_get_rate(pwm->clk) >> PWM_DUTY_WIDTH;
+ hz = 1000000000ul / period_ns;
+
+ rate = (rate + (hz / 2)) / hz;
+
+ if (rate >> PWM_SCALE_WIDTH)
+ return -EINVAL;
+ /* Due to the PWM divider is zero-based, we need to minus 1 to get desired frequency*/
+ if (rate>0)
+ rate--;
+
+ val |= (rate << PWM_SCALE_SHIFT);
+
+ /* the struct clk may be shared across multiple PWM devices, so
+ * only enable the PWM if this device has been enabled */
+ if (pwm->clk_enb)
+ val |= PWM_ENABLE;
+
+ return pwm_writel(pwm, val);
+}
+EXPORT_SYMBOL(pwm_config);
+
+int pwm_enable(struct pwm_device *pwm)
+{
+ int rc = 0;
+
+ mutex_lock(&pwm_lock);
+ if (!pwm->clk_enb) {
+ rc = clk_enable(pwm->clk);
+ if (!rc) {
+ u32 val = readl(pwm->mmio_base);
+ writel(val | PWM_ENABLE, pwm->mmio_base);
+ pwm->clk_enb = 1;
+ }
+ }
+ mutex_unlock(&pwm_lock);
+
+ return rc;
+}
+EXPORT_SYMBOL(pwm_enable);
+
+void pwm_disable(struct pwm_device *pwm)
+{
+ mutex_lock(&pwm_lock);
+ if (pwm->clk_enb) {
+ u32 val = readl(pwm->mmio_base);
+ writel(val & ~PWM_ENABLE, pwm->mmio_base);
+ clk_disable(pwm->clk);
+ pwm->clk_enb = 0;
+ } else
+ dev_warn(&pwm->pdev->dev, "%s called on disabled PWM\n",
+ __func__);
+ mutex_unlock(&pwm_lock);
+}
+EXPORT_SYMBOL(pwm_disable);
+
+struct pwm_device *pwm_request(int pwm_id, const char *label)
+{
+ struct pwm_device *pwm;
+ int found = 0;
+
+ mutex_lock(&pwm_lock);
+
+ list_for_each_entry(pwm, &pwm_list, node) {
+ if (pwm->id == pwm_id) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (found) {
+ if (!pwm->in_use) {
+ pwm->in_use = 1;
+ pwm->label = label;
+ } else
+ pwm = ERR_PTR(-EBUSY);
+ } else
+ pwm = ERR_PTR(-ENOENT);
+
+ mutex_unlock(&pwm_lock);
+
+ return pwm;
+}
+EXPORT_SYMBOL(pwm_request);
+
+void pwm_free(struct pwm_device *pwm)
+{
+ mutex_lock(&pwm_lock);
+ if (pwm->in_use) {
+ pwm->in_use = 0;
+ pwm->label = NULL;
+ } else
+ dev_warn(&pwm->pdev->dev, "PWM device already freed\n");
+
+ mutex_unlock(&pwm_lock);
+}
+EXPORT_SYMBOL(pwm_free);
+
+static int tegra_pwm_probe(struct platform_device *pdev)
+{
+ struct pwm_device *pwm;
+ struct resource *r;
+ int ret;
+
+ pwm = kzalloc(sizeof(*pwm), GFP_KERNEL);
+ if (!pwm) {
+ dev_err(&pdev->dev, "failed to allocate memory\n");
+ return -ENOMEM;
+ }
+ pwm->clk = clk_get(&pdev->dev, NULL);
+
+ if (IS_ERR(pwm->clk)) {
+ ret = PTR_ERR(pwm->clk);
+ goto err_free;
+ }
+
+ pwm->clk_enb = 0;
+ pwm->in_use = 0;
+ pwm->id = pdev->id;
+ pwm->pdev = pdev;
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!r) {
+ dev_err(&pdev->dev, "no memory resources defined\n");
+ ret = -ENODEV;
+ goto err_put_clk;
+ }
+
+ r = request_mem_region(r->start, resource_size(r), pdev->name);
+ if (!r) {
+ dev_err(&pdev->dev, "failed to request memory\n");
+ ret = -EBUSY;
+ goto err_put_clk;
+ }
+
+ pwm->mmio_base = ioremap(r->start, resource_size(r));
+ if (!pwm->mmio_base) {
+ dev_err(&pdev->dev, "failed to ioremap() region\n");
+ ret = -ENODEV;
+ goto err_free_mem;
+ }
+
+ platform_set_drvdata(pdev, pwm);
+
+ mutex_lock(&pwm_lock);
+ list_add_tail(&pwm->node, &pwm_list);
+ mutex_unlock(&pwm_lock);
+
+ return 0;
+
+err_free_mem:
+ release_mem_region(r->start, resource_size(r));
+err_put_clk:
+ clk_put(pwm->clk);
+err_free:
+ kfree(pwm);
+ return ret;
+}
+
+static int __devexit tegra_pwm_remove(struct platform_device *pdev)
+{
+ struct pwm_device *pwm = platform_get_drvdata(pdev);
+ struct resource *r;
+ int rc;
+
+ if (WARN_ON(!pwm))
+ return -ENODEV;
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ mutex_lock(&pwm_lock);
+ if (pwm->in_use) {
+ mutex_unlock(&pwm_lock);
+ return -EBUSY;
+ }
+ list_del(&pwm->node);
+ mutex_unlock(&pwm_lock);
+
+ rc = pwm_writel(pwm, 0);
+
+ iounmap(pwm->mmio_base);
+ release_mem_region(r->start, resource_size(r));
+
+ if (pwm->clk_enb)
+ clk_disable(pwm->clk);
+
+ clk_put(pwm->clk);
+
+ kfree(pwm);
+ return rc;
+}
+
+static struct platform_driver tegra_pwm_driver = {
+ .driver = {
+ .name = "tegra_pwm",
+ },
+ .probe = tegra_pwm_probe,
+ .remove = __devexit_p(tegra_pwm_remove),
+};
+
+static int __init tegra_pwm_init(void)
+{
+ return platform_driver_register(&tegra_pwm_driver);
+}
+subsys_initcall(tegra_pwm_init);
+
+static void __exit tegra_pwm_exit(void)
+{
+ platform_driver_unregister(&tegra_pwm_driver);
+}
+module_exit(tegra_pwm_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("NVIDIA Corporation");
diff --git a/arch/arm/mach-tegra/reset.c b/arch/arm/mach-tegra/reset.c
new file mode 100644
index 000000000000..c44a3de07873
--- /dev/null
+++ b/arch/arm/mach-tegra/reset.c
@@ -0,0 +1,116 @@
+/*
+ * arch/arm/mach-tegra/reset.c
+ *
+ * Copyright (C) 2011 NVIDIA Corporation.
+ *
+ * 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/init.h>
+#include <linux/io.h>
+#include <linux/cpumask.h>
+#include <linux/bitops.h>
+
+#include <asm/cacheflush.h>
+#include <asm/hardware/cache-l2x0.h>
+
+#include <mach/iomap.h>
+
+#include "reset.h"
+#include "sleep.h"
+#include "pm.h"
+
+static bool is_enabled;
+
+static void tegra_cpu_reset_handler_enable(void)
+{
+ void __iomem *iram_base = IO_ADDRESS(TEGRA_IRAM_BASE);
+#ifndef CONFIG_TRUSTED_FOUNDATIONS
+ void __iomem *evp_cpu_reset =
+ IO_ADDRESS(TEGRA_EXCEPTION_VECTORS_BASE + 0x100);
+ void __iomem *sb_ctrl = IO_ADDRESS(TEGRA_SB_BASE);
+ unsigned long reg;
+#endif
+ BUG_ON(is_enabled);
+ BUG_ON(tegra_cpu_reset_handler_size > TEGRA_RESET_HANDLER_SIZE);
+
+ memcpy(iram_base, (void *)__tegra_cpu_reset_handler_start,
+ tegra_cpu_reset_handler_size);
+
+#ifdef CONFIG_TRUSTED_FOUNDATIONS
+ tegra_generic_smc(0xFFFFF200,
+ TEGRA_RESET_HANDLER_BASE + tegra_cpu_reset_handler_offset, 0);
+#else
+ /* NOTE: This must be the one and only write to the EVP CPU reset
+ vector in the entire system. */
+ writel(TEGRA_RESET_HANDLER_BASE + tegra_cpu_reset_handler_offset,
+ evp_cpu_reset);
+ wmb();
+ reg = readl(evp_cpu_reset);
+
+ /* Prevent further modifications to the physical reset vector.
+ NOTE: Has no effect on chips prior to Tegra3. */
+ reg = readl(sb_ctrl);
+ reg |= 2;
+ writel(reg, sb_ctrl);
+ wmb();
+#endif
+ is_enabled = true;
+}
+
+#ifdef CONFIG_PM_SLEEP
+void tegra_cpu_reset_handler_save(void)
+{
+ unsigned int i;
+ BUG_ON(!is_enabled);
+ for (i = 0; i < TEGRA_RESET_DATA_SIZE; i++)
+ __tegra_cpu_reset_handler_data[i] =
+ tegra_cpu_reset_handler_ptr[i];
+ is_enabled = false;
+}
+
+void tegra_cpu_reset_handler_restore(void)
+{
+ unsigned int i;
+ BUG_ON(is_enabled);
+ tegra_cpu_reset_handler_enable();
+ for (i = 0; i < TEGRA_RESET_DATA_SIZE; i++)
+ tegra_cpu_reset_handler_ptr[i] =
+ __tegra_cpu_reset_handler_data[i];
+ is_enabled = true;
+}
+#endif
+
+void __init tegra_cpu_reset_handler_init(void)
+{
+#ifdef CONFIG_SMP
+ __tegra_cpu_reset_handler_data[TEGRA_RESET_MASK_PRESENT] =
+ *((u32 *)cpu_present_mask);
+ __tegra_cpu_reset_handler_data[TEGRA_RESET_STARTUP_SECONDARY] =
+ virt_to_phys((void *)tegra_secondary_startup);
+#endif
+#ifdef CONFIG_PM_SLEEP
+ __tegra_cpu_reset_handler_data[TEGRA_RESET_STARTUP_LP1] =
+ TEGRA_IRAM_CODE_AREA;
+ __tegra_cpu_reset_handler_data[TEGRA_RESET_STARTUP_LP2] =
+ virt_to_phys((void *)tegra_resume);
+#endif
+
+ /* Push all of reset handler data out to the L3 memory system. */
+ __cpuc_coherent_kern_range(
+ (unsigned long)&__tegra_cpu_reset_handler_data[0],
+ (unsigned long)&__tegra_cpu_reset_handler_data[TEGRA_RESET_DATA_SIZE]);
+
+ outer_clean_range(__pa(&__tegra_cpu_reset_handler_data[0]),
+ __pa(&__tegra_cpu_reset_handler_data[TEGRA_RESET_DATA_SIZE]));
+
+ tegra_cpu_reset_handler_enable();
+}
diff --git a/arch/arm/mach-tegra/reset.h b/arch/arm/mach-tegra/reset.h
new file mode 100644
index 000000000000..2af17c3fee8f
--- /dev/null
+++ b/arch/arm/mach-tegra/reset.h
@@ -0,0 +1,70 @@
+/*
+ * arch/arm/mach-tegra/reset.h
+ *
+ * CPU reset dispatcher.
+ *
+ * Copyright (c) 2011, NVIDIA Corporation.
+ *
+ * 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 __MACH_TEGRA_RESET_H
+#define __MACH_TEGRA_RESET_H
+
+#define TEGRA_RESET_MASK_PRESENT 0
+#define TEGRA_RESET_MASK_LP1 1
+#define TEGRA_RESET_MASK_LP2 2
+#define TEGRA_RESET_STARTUP_SECONDARY 3
+#define TEGRA_RESET_STARTUP_LP2 4
+#define TEGRA_RESET_STARTUP_LP1 5
+#define TEGRA_RESET_DATA_SIZE 6
+
+#ifndef __ASSEMBLY__
+
+#include <linux/cpumask.h>
+
+extern unsigned long __tegra_cpu_reset_handler_data[TEGRA_RESET_DATA_SIZE];
+
+void __tegra_cpu_reset_handler_start(void);
+void __tegra_cpu_reset_handler(void);
+void __tegra_cpu_reset_handler_end(void);
+void tegra_secondary_startup(void);
+
+#ifdef CONFIG_PM_SLEEP
+#define tegra_cpu_lp1_mask ((unsigned long *)(IO_ADDRESS(TEGRA_RESET_HANDLER_BASE + \
+ ((u32)&__tegra_cpu_reset_handler_data[TEGRA_RESET_MASK_LP1] - \
+ (u32)__tegra_cpu_reset_handler_start))))
+
+#define tegra_cpu_reset_handler_ptr ((u32 *)(IO_ADDRESS(TEGRA_RESET_HANDLER_BASE + \
+ ((u32)__tegra_cpu_reset_handler_data - \
+ (u32)__tegra_cpu_reset_handler_start))))
+
+#define tegra_cpu_reset_handler_offset \
+ ((u32)__tegra_cpu_reset_handler - \
+ (u32)__tegra_cpu_reset_handler_start)
+
+#define tegra_cpu_reset_handler_size \
+ (__tegra_cpu_reset_handler_end - \
+ __tegra_cpu_reset_handler_start)
+
+#define tegra_cpu_lp2_mask ((cpumask_t *)(IO_ADDRESS(TEGRA_RESET_HANDLER_BASE + \
+ ((u32)&__tegra_cpu_reset_handler_data[TEGRA_RESET_MASK_LP2] - \
+ (u32)__tegra_cpu_reset_handler_start))))
+#endif
+
+void __init tegra_cpu_reset_handler_init(void);
+
+#ifdef CONFIG_PM_SLEEP
+void tegra_cpu_reset_handler_save(void);
+void tegra_cpu_reset_handler_restore(void);
+#endif
+#endif
+#endif
diff --git a/arch/arm/mach-tegra/sleep-t2.S b/arch/arm/mach-tegra/sleep-t2.S
new file mode 100644
index 000000000000..d350a17f475f
--- /dev/null
+++ b/arch/arm/mach-tegra/sleep-t2.S
@@ -0,0 +1,570 @@
+/*
+ * arch/arm/mach-tegra/include/mach/sleep-t2.S
+ *
+ * Copyright (c) 2010-2011, NVIDIA Corporation.
+ * Copyright (c) 2011, Google, Inc.
+ *
+ * Author: Colin Cross <ccross@android.com>
+ * Gary King <gking@nvidia.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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/const.h>
+#include <linux/init.h>
+#include <linux/linkage.h>
+
+#include <asm/assembler.h>
+#include <asm/cache.h>
+#include <asm/domain.h>
+#include <asm/memory.h>
+#include <asm/page.h>
+#include <asm/ptrace.h>
+#include <asm/asm-offsets.h>
+#include <asm/glue-cache.h>
+#include <asm/glue-proc.h>
+#include <asm/system.h>
+
+#include <mach/iomap.h>
+#include <mach/io.h>
+
+#include "asm_macros.h"
+#include "sleep.h"
+
+#define EMC_CFG 0xc
+#define EMC_ADR_CFG 0x10
+#define EMC_REFRESH 0x70
+#define EMC_NOP 0xdc
+#define EMC_SELF_REF 0xe0
+#define EMC_REQ_CTRL 0x2b0
+#define EMC_EMC_STATUS 0x2b4
+
+#define CLK_RESET_CCLK_BURST 0x20
+#define CLK_RESET_CCLK_DIVIDER 0x24
+#define CLK_RESET_SCLK_BURST 0x28
+#define CLK_RESET_SCLK_DIVIDER 0x2c
+
+#define CLK_RESET_PLLC_BASE 0x80
+#define CLK_RESET_PLLM_BASE 0x90
+#define CLK_RESET_PLLP_BASE 0xa0
+#define CLK_RESET_PLLP_OUTA 0xa4
+#define CLK_RESET_PLLP_OUTB 0xa8
+#define CLK_RESET_PLLP_MISC 0xac
+#define CLK_RESET_PLLX_BASE 0xe0
+#define CLK_RESET_PLLX_MISC 0xe4
+
+#define CLK_RESET_RST_CPU_CMPLX_SET 0x340
+
+#define TEGRA_PMC_VIRT (TEGRA_PMC_BASE - IO_APB_PHYS + IO_APB_VIRT)
+#define TEGRA_ARM_PERIF_VIRT (TEGRA_ARM_PERIF_BASE - IO_CPU_PHYS + IO_CPU_VIRT)
+#define TEGRA_CLK_RESET_VIRT (TEGRA_CLK_RESET_BASE - IO_PPSB_PHYS + IO_PPSB_VIRT)
+
+#ifdef CONFIG_HOTPLUG_CPU
+/*
+ * tegra2_hotplug_shutdown(void)
+ *
+ * puts the current cpu in reset
+ * should never return
+ */
+ENTRY(tegra2_hotplug_shutdown)
+ mov r6, lr
+ bl tegra_cpu_exit_coherency
+
+ /* Put this CPU into reset. */
+ cpu_id r0
+ bl tegra2_cpu_reset
+ mov pc, r6
+ENDPROC(tegra2_hotplug_shutdown)
+#endif
+
+#if defined(CONFIG_HOTPLUG_CPU) || defined(CONFIG_PM_SLEEP)
+/*
+ * tegra2_cpu_reset(int cpu)
+ *
+ * r0 is cpu to reset
+ *
+ * puts the specified CPU in wait-for-event mode on the flow controller
+ * and puts the CPU in reset
+ * can be called on the current cpu or another cpu
+ * if called on the current cpu, does not return
+ * MUST NOT BE CALLED FOR CPU 0.
+ *
+ * corrupts r0-r3, r12
+ */
+ENTRY(tegra2_cpu_reset)
+ cmp r0, #0
+ moveq pc, lr @ must not be called for CPU 0
+
+ mov32 r1, TEGRA_PMC_VIRT + PMC_SCRATCH41
+ mov r12, #CPU_RESETTABLE
+ str r12, [r1]
+
+ cpu_to_halt_reg r1, r0
+ mov32 r3, TEGRA_FLOW_CTRL_VIRT
+ mov r2, #FLOW_CTRL_WAITEVENT | FLOW_CTRL_JTAG_RESUME
+ str r2, [r3, r1] @ put flow controller in wait event mode
+ ldr r2, [r3, r1]
+ isb
+ dsb
+ movw r1, 0x1011
+ mov r1, r1, lsl r0
+ mov32 r3, TEGRA_CLK_RESET_VIRT
+ str r1, [r3, #CLK_RESET_RST_CPU_CMPLX_SET] @ put slave CPU in reset
+ isb
+ dsb
+ cpu_id r3
+ cmp r3, r0
+ beq .
+ mov pc, lr
+ENDPROC(tegra2_cpu_reset)
+#endif
+
+#ifdef CONFIG_PM_SLEEP
+/*
+ * tegra2_cpu_clear_resettable(void)
+ *
+ * Called to clear the "resettable soon" flag in PMC_SCRATCH41 when
+ * it is expected that the secondary CPU will be idle soon.
+ */
+ENTRY(tegra2_cpu_clear_resettable)
+ mov32 r1, TEGRA_PMC_VIRT + PMC_SCRATCH41
+ mov r12, #CPU_NOT_RESETTABLE
+ str r12, [r1]
+ mov pc, lr
+ENDPROC(tegra2_cpu_clear_resettable)
+
+/*
+ * tegra2_cpu_set_resettable_soon(void)
+ *
+ * Called to set the "resettable soon" flag in PMC_SCRATCH41 when
+ * it is expected that the secondary CPU will be idle soon.
+ */
+ENTRY(tegra2_cpu_set_resettable_soon)
+ mov32 r1, TEGRA_PMC_VIRT + PMC_SCRATCH41
+ mov r12, #CPU_RESETTABLE_SOON
+ str r12, [r1]
+ mov pc, lr
+ENDPROC(tegra2_cpu_set_resettable_soon)
+
+/*
+ * tegra2_cpu_is_resettable_soon(void)
+ *
+ * Returns true if the "resettable soon" flag in PMC_SCRATCH41 has been
+ * set because it is expected that the secondary CPU will be idle soon.
+ */
+ENTRY(tegra2_cpu_is_resettable_soon)
+ mov32 r1, TEGRA_PMC_VIRT + PMC_SCRATCH41
+ ldr r12, [r1]
+ cmp r12, #CPU_RESETTABLE_SOON
+ moveq r0, #1
+ movne r0, #0
+ mov pc, lr
+ENDPROC(tegra2_cpu_is_resettable_soon)
+
+/*
+ * tegra2_sleep_core(unsigned long v2p)
+ *
+ * enters suspend in LP0 or LP1 by turning off the mmu and jumping to
+ * tegra2_tear_down_core in IRAM
+ */
+ENTRY(tegra2_sleep_core)
+ mov r12, pc @ return here is via r12
+ b tegra_cpu_save
+ mov32 r1, tegra2_tear_down_core
+ mov32 r2, tegra2_iram_start
+ sub r1, r1, r2
+ mov32 r2, TEGRA_IRAM_CODE_AREA
+ add r1, r1, r2
+ b tegra_turn_off_mmu
+ENDPROC(tegra2_sleep_core)
+
+/*
+ * tegra2_sleep_wfi(unsigned long v2p)
+ */
+ENTRY(tegra2_sleep_wfi)
+ mrc p15, 0, r2, c1, c0, 1 @ save actlr before exiting coherency
+ mov r12, pc @ return here is via r12
+ b tegra_cpu_save
+ mov r11, r2
+
+ mov32 r0, TEGRA_PMC_VIRT + PMC_SCRATCH41
+ mov r3, #CPU_RESETTABLE
+ str r3, [r0]
+
+ bl tegra_cpu_wfi
+
+ mov32 r0, TEGRA_PMC_VIRT + PMC_SCRATCH41
+ mov r3, #CPU_NOT_RESETTABLE
+ str r3, [r0]
+
+ /*
+ * cpu may be reset while in wfi, which will return through
+ * tegra_resume to tegra_cpu_resume_phys to tegra_cpu_resume
+ * or interrupt may wake wfi, which will return here
+ * cpu state is unchanged - MMU is on, cache is on, coherency
+ * is off, and the data cache is off
+ *
+ * r11 contains the original actlr
+ */
+
+ mov sp, r7 @ restore SP for aborted suspend
+ bl tegra_pen_lock
+
+ mov32 r3, TEGRA_PMC_VIRT
+ add r0, r3, #PMC_SCRATCH41
+ mov r3, #CPU_NOT_RESETTABLE
+ str r3, [r0]
+
+ bl tegra_pen_unlock
+
+#if USE_TEGRA_CPU_SUSPEND
+ /* Enable the data cache and SMP coherency */
+ mrc p15, 0, r10, c1, c0, 0
+ orr r10, r10, #CR_C
+ dsb
+ mcr p15, 0, r10, c1, c0, 0
+ isb
+ mcr p15, 0, r11, c1, c0, 1 @ reenable coherency
+
+#else
+ mcr p15, 0, r11, c1, c0, 1 @ reenable coherency
+
+ /* Invalidate the TLBs & BTAC */
+ mov r1, #0
+ mcr p15, 0, r1, c8, c3, 0 @ invalidate shared TLBs
+ mcr p15, 0, r1, c7, c1, 6 @ invalidate shared BTAC
+ dsb
+ isb
+
+ @ the cpu was running with coherency disabled, caches may be out of date
+#ifdef MULTI_CACHE
+ mov32 r10, cpu_cache
+ mov lr, pc
+ ldr pc, [r10, #CACHE_FLUSH_KERN_ALL]
+#else
+ bl __cpuc_flush_kern_all
+#endif
+#endif
+
+#ifdef CONFIG_CACHE_L2X0
+ /* Issue a PL310 cache sync operation */
+ dsb
+ mov32 r2, TEGRA_PL310_VIRT
+ movw r1, 0x730 @ cache sync
+ add r2, r2, r1
+ mov r1, #0
+ str r1, [r2]
+#endif
+
+ pop_ctx_regs r0, r1 @ restore context registers
+ mov pc, lr
+ENDPROC(tegra2_sleep_wfi)
+
+/*
+ * tegra2_tear_down_cpu
+ *
+ * Switches the CPU cluster to PLL-P and enters sleep.
+ */
+ENTRY(tegra2_tear_down_cpu)
+ bl tegra_cpu_pllp
+ b tegra2_enter_sleep
+ENDPROC(tegra2_tear_down_cpu)
+
+/* START OF ROUTINES COPIED TO IRAM */
+ .align L1_CACHE_SHIFT
+ .globl tegra2_iram_start
+tegra2_iram_start:
+
+/*
+ * tegra2_lp1_reset
+ *
+ * reset vector for LP1 restore; copied into IRAM during suspend.
+ * brings the system back up to a safe starting point (SDRAM out of
+ * self-refresh, PLLC, PLLM and PLLP reenabled, CPU running on PLLP,
+ * system clock running on the same PLL that it suspended at), and
+ * jumps to tegra_lp2_startup to restore PLLX and virtual addressing.
+ * physical address of tegra_lp2_startup expected to be stored in
+ * PMC_SCRATCH41
+ *
+ * NOTE: THIS *MUST* BE RELOCATED TO TEGRA_IRAM_CODE_AREA AND MUST BE FIRST.
+ */
+ENTRY(tegra2_lp1_reset)
+ /*
+ * the CPU and system bus are running at 32KHz and executing from
+ * IRAM when this code is executed; immediately switch to CLKM and
+ * enable PLLP.
+ */
+ mov32 r0, TEGRA_CLK_RESET_BASE
+#ifndef CONFIG_TRUSTED_FOUNDATIONS
+ /* secure code handles 32KHz to CLKM/OSC clock switch */
+ mov r1, #(1 << 28)
+ str r1, [r0, #CLK_RESET_SCLK_BURST]
+ str r1, [r0, #CLK_RESET_CCLK_BURST]
+ mov r1, #0
+ str r1, [r0, #CLK_RESET_SCLK_DIVIDER]
+ str r1, [r0, #CLK_RESET_CCLK_DIVIDER]
+#endif
+ ldr r1, [r0, #CLK_RESET_PLLM_BASE]
+ tst r1, #(1 << 30)
+ orreq r1, r1, #(1 << 30)
+ streq r1, [r0, #CLK_RESET_PLLM_BASE]
+ ldr r1, [r0, #CLK_RESET_PLLP_BASE]
+ tst r1, #(1 << 30)
+ orreq r1, r1, #(1 << 30)
+ streq r1, [r0, #CLK_RESET_PLLP_BASE]
+ ldr r1, [r0, #CLK_RESET_PLLC_BASE]
+ tst r1, #(1 << 30)
+ orreq r1, r1, #(1 << 30)
+ streq r1, [r0, #CLK_RESET_PLLC_BASE]
+
+ adr r2, tegra2_sdram_pad_address
+ adr r4, tegra2_sdram_pad_save
+ mov r5, #0
+
+padload:
+ ldr r0, [r2, r5] @ r0 is emc register address
+
+ ldr r1, [r4, r5]
+ str r1, [r0] @ set emc register to safe vals
+
+ add r5, r5, #4
+ ldr r0, tegra2_sdram_pad_size
+ cmp r0, r5
+ bne padload
+
+padload_done:
+ mov32 r7, TEGRA_TMRUS_BASE
+ ldr r1, [r7]
+ add r1, r1, #0xff @ 255uS delay for PLL stabilization
+
+1: ldr r0, [r7]
+ cmp r0, r1
+ dmb
+ bmi 1b
+
+ adr r4, tegra2_sclk_save
+ ldr r4, [r4]
+ mov32 r0, TEGRA_CLK_RESET_BASE
+ str r4, [r0, #CLK_RESET_SCLK_BURST]
+ ldr r4, =((1 << 28) | (4)) @ burst policy is PLLP
+ str r4, [r0, #CLK_RESET_CCLK_BURST]
+
+ mov32 r0, TEGRA_EMC_BASE
+ ldr r1, [r0, #EMC_CFG]
+ bic r1, r1, #(1 << 31) @ disable DRAM_CLK_STOP
+ str r1, [r0, #EMC_CFG]
+
+ mov r1, #0
+ str r1, [r0, #EMC_SELF_REF] @ take DRAM out of self refresh
+ mov r1, #1
+ str r1, [r0, #EMC_NOP]
+ str r1, [r0, #EMC_NOP]
+ str r1, [r0, #EMC_REFRESH]
+
+ ldr r1, [r0, #EMC_ADR_CFG]
+ tst r1, #(0x3 << 24)
+ moveq r1, #(0x1 << 8) @ just 1 device
+ movne r1, #(0x3 << 8) @ 2 devices
+
+exit_selfrefresh_loop:
+ ldr r2, [r0, #EMC_EMC_STATUS]
+ ands r2, r2, r1
+ bne exit_selfrefresh_loop
+
+ mov r1, #0
+ str r1, [r0, #EMC_REQ_CTRL]
+
+ mov32 r0, TEGRA_PMC_BASE
+ ldr r0, [r0, #PMC_SCRATCH41]
+ mov pc, r0
+ENDPROC(tegra2_lp1_reset)
+
+/*
+ * tegra2_tear_down_core
+ *
+ * copied into and executed from IRAM
+ * puts memory in self-refresh for LP0 and LP1
+ */
+tegra2_tear_down_core:
+ bl tegra2_sdram_self_refresh
+ bl tegra2_cpu_clk32k
+ b tegra2_enter_sleep
+
+/*
+ * tegra2_cpu_clk32k
+ *
+ * In LP0 and LP1 all plls will be turned off. Switch the CPU and system clock
+ * to the 32khz clock (clks)
+ */
+tegra2_cpu_clk32k:
+ /* start by jumping to clkm to safely disable PLLs, then jump
+ * to clks */
+ mov r0, #(1 << 28)
+ str r0, [r5, #CLK_RESET_SCLK_BURST]
+ str r0, [r5, #CLK_RESET_CCLK_BURST]
+ mov r0, #0
+ str r0, [r5, #CLK_RESET_CCLK_DIVIDER]
+ str r0, [r5, #CLK_RESET_SCLK_DIVIDER]
+
+ /* 2 us delay between changing sclk and disabling PLLs */
+ mov32 r7, TEGRA_TMRUS_BASE
+ ldr r1, [r7]
+ add r1, r1, #3
+
+1: ldr r0, [r7]
+ cmp r0, r1
+ dmb
+ bmi 1b
+
+ /* switch to CLKS */
+ mov r0, #0 /* burst policy = 32KHz */
+ str r0, [r5, #CLK_RESET_SCLK_BURST]
+
+ /* disable PLLP, PLLM, PLLC in LP0 and LP1 states */
+ ldr r0, [r5, #CLK_RESET_PLLM_BASE]
+ bic r0, r0, #(1 << 30)
+ str r0, [r5, #CLK_RESET_PLLM_BASE]
+ ldr r0, [r5, #CLK_RESET_PLLP_BASE]
+ bic r0, r0, #(1 << 30)
+ str r0, [r5, #CLK_RESET_PLLP_BASE]
+ ldr r0, [r5, #CLK_RESET_PLLC_BASE]
+ bic r0, r0, #(1 << 30)
+ str r0, [r5, #CLK_RESET_PLLC_BASE]
+ mov pc, lr
+
+/*
+ * tegra2_enter_sleep
+ *
+ * uses flow controller to enter sleep state
+ * executes from IRAM with SDRAM in selfrefresh when target state is LP0 and LP1
+ * executes from SDRAM with target state is LP2
+ */
+tegra2_enter_sleep:
+ mov32 r7, TEGRA_TMRUS_BASE
+ ldr r1, [r7]
+ mov32 r4, TEGRA_PMC_BASE
+ str r1, [r4, #PMC_SCRATCH38]
+ dsb
+ mov32 r6, TEGRA_FLOW_CTRL_BASE
+
+ mov r0, #FLOW_CTRL_WAIT_FOR_INTERRUPT
+ orr r0, r0, #FLOW_CTRL_HALT_CPU_IRQ | FLOW_CTRL_HALT_CPU_FIQ
+ cpu_id r1
+ cpu_to_halt_reg r1, r1
+ str r0, [r6, r1]
+ dsb
+ ldr r0, [r6, r1] /* memory barrier */
+
+halted: dsb
+ wfe /* CPU should be power gated here */
+ isb
+ b halted
+
+/*
+ * tegra2_sdram_self_refresh
+ *
+ * called with MMU off and caches disabled
+ * puts sdram in self refresh
+ * must execute from IRAM
+ */
+tegra2_sdram_self_refresh:
+ mov32 r1, TEGRA_EMC_BASE
+ mov r2, #3
+ str r2, [r1, #EMC_REQ_CTRL] @ stall incoming DRAM requests
+
+emcidle:ldr r2, [r1, #EMC_EMC_STATUS]
+ tst r2, #4
+ beq emcidle
+
+ mov r2, #1
+ str r2, [r1, #EMC_SELF_REF]
+
+ ldr r2, [r1, #EMC_ADR_CFG]
+ tst r2, #(0x3 << 24)
+ moveq r2, #(0x1 << 8) @ just 1 device
+ movne r2, #(0x3 << 8) @ 2 devices
+
+emcself:ldr r3, [r1, #EMC_EMC_STATUS]
+ and r3, r3, r2
+ cmp r3, r2
+ bne emcself @ loop until DDR in self-refresh
+
+ adr r2, tegra2_sdram_pad_address
+ adr r3, tegra2_sdram_pad_safe
+ adr r4, tegra2_sdram_pad_save
+ mov r5, #0
+
+padsave:
+ ldr r0, [r2, r5] @ r0 is emc register address
+
+ ldr r1, [r0]
+ str r1, [r4, r5] @ save emc register
+
+ ldr r1, [r3, r5]
+ str r1, [r0] @ set emc register to safe vals
+
+ add r5, r5, #4
+ ldr r0, tegra2_sdram_pad_size
+ cmp r0, r5
+ bne padsave
+padsave_done:
+
+ mov32 r5, TEGRA_CLK_RESET_BASE
+ ldr r0, [r5, #CLK_RESET_SCLK_BURST]
+ adr r2, tegra2_sclk_save
+ str r0, [r2]
+ dsb
+ mov pc, lr
+
+tegra2_sdram_pad_address:
+ .word TEGRA_APB_MISC_BASE + 0x8c8 /* XM2CFGCPADCTRL */
+ .word TEGRA_APB_MISC_BASE + 0x8cc /* XM2CFGDPADCTRL */
+ .word TEGRA_APB_MISC_BASE + 0x8d0 /* XM2CLKCFGPADCTRL */
+ .word TEGRA_APB_MISC_BASE + 0x8d4 /* XM2COMPPADCTRL */
+ .word TEGRA_APB_MISC_BASE + 0x8d8 /* XM2VTTGENPADCTRL */
+ .word TEGRA_APB_MISC_BASE + 0x8e4 /* XM2CFGCPADCTRL2 */
+ .word TEGRA_APB_MISC_BASE + 0x8e8 /* XM2CFGDPADCTRL2 */
+
+tegra2_sdram_pad_size:
+ .word tegra2_sdram_pad_size - tegra2_sdram_pad_address
+
+tegra2_sdram_pad_safe:
+ .word 0x8
+ .word 0x8
+ .word 0x0
+ .word 0x8
+ .word 0x5500
+ .word 0x08080040
+ .word 0x0
+
+tegra2_sclk_save:
+ .word 0x0
+
+tegra2_sdram_pad_save:
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+
+ .ltorg
+/* dummy symbol for end of IRAM */
+ .align L1_CACHE_SHIFT
+ .globl tegra2_iram_end
+tegra2_iram_end:
+ b .
+#endif
diff --git a/arch/arm/mach-tegra/sleep-t3.S b/arch/arm/mach-tegra/sleep-t3.S
new file mode 100644
index 000000000000..b0960bed1550
--- /dev/null
+++ b/arch/arm/mach-tegra/sleep-t3.S
@@ -0,0 +1,694 @@
+/*
+ * arch/arm/mach-tegra/include/mach/sleep-t3.S
+ *
+ * Copyright (c) 2010-2011, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/const.h>
+#include <linux/init.h>
+#include <linux/linkage.h>
+
+#include <asm/assembler.h>
+#include <asm/cache.h>
+#include <asm/domain.h>
+#include <asm/memory.h>
+#include <asm/page.h>
+#include <asm/ptrace.h>
+#include <asm/asm-offsets.h>
+#include <asm/glue-cache.h>
+#include <asm/glue-proc.h>
+#include <asm/system.h>
+
+#include <mach/iomap.h>
+#include <mach/io.h>
+
+#include "asm_macros.h"
+#include "sleep.h"
+
+#define EMC_CFG 0xc
+#define EMC_ADR_CFG 0x10
+#define EMC_TIMING_CONTROL 0x28
+#define EMC_REFRESH 0x70
+#define EMC_NOP 0xdc
+#define EMC_SELF_REF 0xe0
+#define EMC_MRW 0xe8
+#define EMC_REQ_CTRL 0x2b0
+#define EMC_EMC_STATUS 0x2b4
+#define EMC_FBIO_CFG5 0x104
+#define EMC_AUTO_CAL_CONFIG 0x2a4
+#define EMC_AUTO_CAL_INTERVAL 0x2a8
+#define EMC_AUTO_CAL_STATUS 0x2ac
+#define EMC_CFG_DIG_DLL 0x2bc
+#define EMC_ZCAL_INTERVAL 0x2e0
+#define EMC_ZQ_CAL 0x2ec
+#define EMC_XM2VTTGENPADCTRL 0x310
+#define EMC_XM2VTTGENPADCTRL2 0x314
+
+#define PMC_CTRL 0x0
+#define PMC_CTRL_SIDE_EFFECT_LP0 (1 << 14) /* enter LP0 when CPU pwr gated */
+
+#define PMC_PWRGATE_TOGGLE 0x30
+#define PMC_REMOVE_CLAMPING_CMD 0x34
+#define PMC_PWRGATE_STATUS 0x38
+
+#define PMC_PWRGATE_PARTID_L2C (0x5)
+
+#define PMC_IO_DPD_REQ 0x1b8
+#define PMC_IO_DPD_STATUS 0x1bc
+
+#define CLK_RESET_CCLK_BURST 0x20
+#define CLK_RESET_CCLK_DIVIDER 0x24
+#define CLK_RESET_SCLK_BURST 0x28
+#define CLK_RESET_SCLK_DIVIDER 0x2c
+
+#define CLK_RESET_PLLC_BASE 0x80
+#define CLK_RESET_PLLM_BASE 0x90
+#define CLK_RESET_PLLP_BASE 0xa0
+#define CLK_RESET_PLLA_BASE 0xb0
+#define CLK_RESET_PLLX_BASE 0xe0
+
+#define CLK_RESET_PLLC_MISC 0x8c
+#define CLK_RESET_PLLM_MISC 0x9c
+#define CLK_RESET_PLLP_MISC 0xac
+#define CLK_RESET_PLLA_MISC 0xbc
+#define CLK_RESET_PLLX_MISC 0xe4
+
+#define CLK_RESET_PLLP_OUTA 0xa4
+#define CLK_RESET_PLLP_OUTB 0xa8
+
+#define PMC_PLLP_WB0_OVERRIDE 0xf8
+
+#define CLK_RESET_CLK_SOURCE_MSELECT 0x3b4
+
+#define MSELECT_CLKM (0x3 << 30)
+
+#define USE_PLL_LOCK_BITS 0
+#define USE_PLLP_ON_SLEEP_ENTRY 0
+
+.macro emc_device_mask, rd, base
+ ldr \rd, [\base, #EMC_ADR_CFG]
+ tst \rd, #0x1
+ moveq \rd, #(0x1<<8) @ just 1 device
+ movne \rd, #(0x3<<8) @ 2 devices
+.endm
+
+.macro emc_timing_update, rd, base
+ mov \rd, #1
+ str \rd, [\base, #EMC_TIMING_CONTROL]
+1001:
+ ldr \rd, [\base, #EMC_EMC_STATUS]
+ tst \rd, #(0x1<<23) @ wait until EMC_STATUS_TIMING_UPDATE_STALLED is clear
+ bne 1001b
+.endm
+
+#ifdef CONFIG_HOTPLUG_CPU
+/*
+ * tegra3_hotplug_shutdown(void)
+ *
+ * Powergates the current CPU.
+ * Should never return.
+ */
+ENTRY(tegra3_hotplug_shutdown)
+ mov r6, lr
+ bl tegra_cpu_exit_coherency
+
+ /* Powergate this CPU. */
+ mov r0, #TEGRA_POWER_HOTPLUG_SHUTDOWN
+ bl tegra3_cpu_reset
+ mov pc, r6 @ should never get here
+ENDPROC(tegra3_hotplug_shutdown)
+#endif
+
+#if defined(CONFIG_HOTPLUG_CPU) || defined(CONFIG_PM_SLEEP)
+/*
+ * tegra3_cpu_reset(unsigned long flags)
+ *
+ * Puts the current CPU in wait-for-event mode on the flow controller
+ * and powergates it -- flags (in R0) indicate the request type.
+ * Must never be called for CPU 0.
+ *
+ * corrupts r0-r4, r12
+ */
+ENTRY(tegra3_cpu_reset)
+ cpu_id r3
+ cmp r3, #0
+ moveq pc, lr @ Must never be called for CPU 0
+
+ mov32 r12, TEGRA_FLOW_CTRL_VIRT
+ cpu_to_csr_reg r1, r3
+ add r1, r1, r12 @ virtual CSR address for this CPU
+ cpu_to_halt_reg r2, r3
+ add r2, r2, r12 @ virtual HALT_EVENTS address for this CPU
+
+ /* Clear this CPU's "event" and "interrupt" flags and power gate
+ it when halting but not before it is in the "WFE" state. */
+ movw r12, FLOW_CTRL_CSR_INTR_FLAG | FLOW_CTRL_CSR_EVENT_FLAG | FLOW_CTRL_CSR_ENABLE
+ mov r4, #(1 << 4)
+ orr r12, r12, r4, lsl r3
+ str r12, [r1]
+
+ /* Halt this CPU. */
+ mov r3, #0x400
+delay_1:
+ subs r3, r3, #1 @ delay as a part of wfe war.
+ bge delay_1;
+ cpsid a @ disable imprecise aborts.
+ ldr r3, [r1] @ read CSR
+ str r3, [r1] @ clear CSR
+ tst r0, #TEGRA_POWER_HOTPLUG_SHUTDOWN
+ moveq r3, #FLOW_CTRL_WAIT_FOR_INTERRUPT @ For LP2
+ movne r3, #FLOW_CTRL_WAITEVENT @ For hotplug
+ str r3, [r2]
+ ldr r0, [r2]
+ b wfe_war
+
+__cpu_reset_again:
+ dsb
+ .align 5
+ wfe @ CPU should be power gated here
+wfe_war:
+ b __cpu_reset_again
+
+ /* 38 nop's, which fills reset of wfe cache line and 4 more cachelines with nop*/
+ .rept 38
+ nop
+ .endr
+ b . @ should never get here
+
+ENDPROC(tegra3_cpu_reset)
+#endif
+
+#ifdef CONFIG_PM_SLEEP
+
+/*
+ * tegra3_sleep_core(unsigned long v2p)
+ *
+ * enters suspend in LP0 or LP1 by turning off the mmu and jumping to
+ * tegra3_tear_down_core in IRAM
+ */
+ENTRY(tegra3_sleep_core)
+ mov r12, pc @ return here is via r12
+ b tegra_cpu_save
+
+ /* preload all the address literals that are needed for the
+ * CPU power-gating process, to avoid loads from SDRAM (which are
+ * not supported once SDRAM is put into self-refresh.
+ * LP0 / LP1 use physical address, since the MMU needs to be
+ * disabled before putting SDRAM into self-refresh to avoid
+ * memory access due to page table walks */
+ mov32 r4, TEGRA_PMC_BASE
+ mov32 r5, TEGRA_CLK_RESET_BASE
+ mov32 r6, TEGRA_FLOW_CTRL_BASE
+ mov32 r7, TEGRA_TMRUS_BASE
+
+ mov32 r1, tegra3_tear_down_core
+ mov32 r2, tegra3_iram_start
+ sub r1, r1, r2
+ mov32 r2, TEGRA_IRAM_CODE_AREA
+ add r1, r1, r2
+ b tegra_turn_off_mmu
+ENDPROC(tegra3_sleep_core)
+
+/*
+ * tegra3_sleep_cpu_secondary(unsigned long v2p)
+ *
+ * Enters LP2 on secondary CPU by exiting coherency and powergating the CPU.
+ */
+ENTRY(tegra3_sleep_cpu_secondary)
+ mov r12, pc @ return here is via r12
+ b tegra_cpu_save
+
+ /* Powergate this CPU. */
+ mov r0, #0 @ power mode flags (!hotplug)
+ bl tegra3_cpu_reset
+ b . @ should never get here
+ENDPROC(tegra3_sleep_cpu_secondary)
+
+/*
+ * tegra3_tear_down_cpu
+ *
+ * Switches the CPU cluster to PLL-P and enters sleep.
+ */
+ENTRY(tegra3_tear_down_cpu)
+ mov32 r4, TEGRA_PMC_BASE
+ mov32 r5, TEGRA_CLK_RESET_BASE
+ mov32 r6, TEGRA_FLOW_CTRL_BASE
+ mov32 r7, TEGRA_TMRUS_BASE
+#if USE_PLLP_ON_SLEEP_ENTRY
+ bl tegra_cpu_pllp
+#endif
+ b tegra3_enter_sleep
+ENDPROC(tegra3_tear_down_cpu)
+
+/* START OF ROUTINES COPIED TO IRAM */
+ .align L1_CACHE_SHIFT
+ .globl tegra3_iram_start
+tegra3_iram_start:
+
+/*
+ * tegra3_lp1_reset
+ *
+ * reset vector for LP1 restore; copied into IRAM during suspend.
+ * brings the system back up to a safe starting point (SDRAM out of
+ * self-refresh, PLLC, PLLM and PLLP reenabled, CPU running on PLLP,
+ * system clock running on the same PLL that it suspended at), and
+ * jumps to tegra_lp2_startup to restore PLLX and virtual addressing.
+ * physical address of tegra_lp2_startup expected to be stored in
+ * PMC_SCRATCH41
+ *
+ * NOTE: THIS *MUST* BE RELOCATED TO TEGRA_IRAM_CODE_AREA AND MUST BE FIRST.
+ */
+.macro pll_enable, rd, car, base, misc
+ ldr \rd, [\car, #\base]
+ tst \rd, #(1<<30)
+ orreq \rd, \rd, #(1<<30)
+ streq \rd, [\car, #\base]
+#if USE_PLL_LOCK_BITS
+ ldr \rd, [\car, #\misc]
+ orr \rd, \rd, #(1<<18)
+ str \rd, [\car, #\misc]
+#endif
+.endm
+
+ENTRY(tegra3_lp1_reset)
+ /* the CPU and system bus are running at 32KHz and executing from
+ * IRAM when this code is executed; immediately switch to CLKM and
+ * enable PLLP, PLLM, PLLC, PLLA and PLLX. */
+ mov32 r0, TEGRA_CLK_RESET_BASE
+#ifndef CONFIG_TRUSTED_FOUNDATIONS
+ /* secure code handles 32KHz to CLKM/OSC clock switch */
+ mov r1, #(1<<28)
+ str r1, [r0, #CLK_RESET_SCLK_BURST]
+ str r1, [r0, #CLK_RESET_CCLK_BURST]
+ mov r1, #0
+ str r1, [r0, #CLK_RESET_SCLK_DIVIDER]
+ str r1, [r0, #CLK_RESET_CCLK_DIVIDER]
+#endif
+ /* enable PLLM via PMC */
+ mov32 r2, TEGRA_PMC_BASE
+ ldr r1, [r2, #PMC_PLLP_WB0_OVERRIDE]
+ orr r1, r1, #(1<<12)
+ str r1, [r2, #PMC_PLLP_WB0_OVERRIDE]
+
+ pll_enable r1, r0, CLK_RESET_PLLP_BASE, CLK_RESET_PLLP_MISC
+ pll_enable r1, r0, CLK_RESET_PLLA_BASE, CLK_RESET_PLLA_MISC
+ pll_enable r1, r0, CLK_RESET_PLLC_BASE, CLK_RESET_PLLC_MISC
+ pll_enable r1, r0, CLK_RESET_PLLX_BASE, CLK_RESET_PLLX_MISC
+ mov32 r7, TEGRA_TMRUS_BASE
+ ldr r1, [r7]
+
+#if USE_PLL_LOCK_BITS
+ pll_locked r1, r0, CLK_RESET_PLLM_BASE
+ pll_locked r1, r0, CLK_RESET_PLLP_BASE
+ pll_locked r1, r0, CLK_RESET_PLLA_BASE
+ pll_locked r1, r0, CLK_RESET_PLLC_BASE
+ pll_locked r1, r0, CLK_RESET_PLLX_BASE
+#else
+ add r1, r1, #0xff @ 255uS delay for PLL stabilization
+ wait_until r1, r7, r3
+#endif
+ add r5, pc, #tegra3_sdram_pad_save-(.+8) @ r5 reserved for pad base
+
+ ldr r4, [r5, #0x18]
+ str r4, [r0, #CLK_RESET_CLK_SOURCE_MSELECT]
+
+ ldr r4, [r5, #0x1C]
+ str r4, [r0, #CLK_RESET_SCLK_BURST]
+
+ mov32 r4, ((1<<28) | (8)) @ burst policy is PLLX
+ str r4, [r0, #CLK_RESET_CCLK_BURST]
+
+#if defined (CONFIG_CACHE_L2X0)
+ /* power up L2 */
+ ldr r0, [r2, #PMC_PWRGATE_STATUS]
+ tst r0, #(1<<PMC_PWRGATE_PARTID_L2C)
+ bne powerup_l2_done
+ movw r0, #(1<<8) | PMC_PWRGATE_PARTID_L2C
+ str r0, [r2, #PMC_PWRGATE_TOGGLE]
+powerup_l2_wait:
+ ldr r0, [r2, #PMC_PWRGATE_STATUS]
+ tst r0, #(1<<PMC_PWRGATE_PARTID_L2C)
+ beq powerup_l2_wait
+powerup_l2_done:
+ mov r0, #PMC_PWRGATE_PARTID_L2C
+ str r0, [r2, #PMC_REMOVE_CLAMPING_CMD]
+#endif
+
+ mov32 r0, TEGRA_EMC_BASE @ r0 reserved for emc base
+
+ ldr r1, [r5, #0x14] @ PMC_IO_DPD_STATUS
+ mvn r1, r1
+ bic r1, r1, #(0x1<<31)
+ orr r1, r1, #(0x1<<30)
+ str r1, [r2, #PMC_IO_DPD_REQ]
+ ldr r1, [r5, #0xC]
+ str r1, [r0, #EMC_XM2VTTGENPADCTRL]
+ ldr r1, [r5, #0x10]
+ str r1, [r0, #EMC_XM2VTTGENPADCTRL2]
+ ldr r1, [r5, #0x8]
+ str r1, [r0, #EMC_AUTO_CAL_INTERVAL]
+
+ ldr r1, [r0, #EMC_CFG_DIG_DLL]
+ orr r1, r1, #(0x1<<30) @ set DLL_RESET
+ str r1, [r0, #EMC_CFG_DIG_DLL]
+
+ emc_timing_update r1, r0
+
+ ldr r1, [r0, #EMC_AUTO_CAL_CONFIG]
+ orr r1, r1, #(0x1<<31) @ set AUTO_CAL_ACTIVE
+ str r1, [r0, #EMC_AUTO_CAL_CONFIG]
+
+emc_wait_audo_cal_onetime:
+ ldr r1, [r0, #EMC_AUTO_CAL_STATUS]
+ tst r1, #(0x1<<31) @ wait until AUTO_CAL_ACTIVE is clear
+ bne emc_wait_audo_cal_onetime
+
+ ldr r1, [r0, #EMC_CFG]
+ bic r1, r1, #(1<<31) @ disable DRAM_CLK_STOP
+ str r1, [r0, #EMC_CFG]
+
+ mov r1, #0
+ str r1, [r0, #EMC_SELF_REF] @ take DRAM out of self refresh
+ mov r1, #1
+ str r1, [r0, #EMC_NOP]
+ str r1, [r0, #EMC_NOP]
+ str r1, [r0, #EMC_REFRESH]
+
+ emc_device_mask r1, r0
+
+exit_selfrefresh_loop:
+ ldr r2, [r0, #EMC_EMC_STATUS]
+ ands r2, r2, r1
+ bne exit_selfrefresh_loop
+
+ lsr r1, r1, #8 @ devSel, bit0:dev0 bit1:dev1
+
+ mov32 r7, TEGRA_TMRUS_BASE
+ ldr r2, [r0, #EMC_FBIO_CFG5]
+
+ and r2, r2, #3
+ cmp r2, #2
+ beq emc_lpddr2
+
+ mov32 r2, 0x80000011
+ str r2, [r0, #EMC_ZQ_CAL]
+ ldr r2, [r7]
+ add r2, r2, #10
+ wait_until r2, r7, r3
+
+ tst r1, #2
+ beq zcal_done
+
+ mov32 r2, 0x40000011
+ str r2, [r0, #EMC_ZQ_CAL]
+ ldr r2, [r7]
+ add r2, r2, #10
+ wait_until r2, r7, r3
+ b zcal_done
+
+emc_lpddr2:
+
+ mov32 r2, 0x800A00AB
+ str r2, [r0, #EMC_MRW]
+ ldr r2, [r7]
+ add r2, r2, #1
+ wait_until r2, r7, r3
+
+ tst r1, #2
+ beq zcal_done
+
+ mov32 r2, 0x400A00AB
+ str r2, [r0, #EMC_MRW]
+ ldr r2, [r7]
+ add r2, r2, #1
+ wait_until r2, r7, r3
+
+zcal_done:
+
+ mov r1, #0
+ str r1, [r0, #EMC_REQ_CTRL]
+ ldr r1, [r5, #0x4]
+ str r1, [r0, #EMC_ZCAL_INTERVAL]
+ ldr r1, [r5, #0x0]
+ str r1, [r0, #EMC_CFG]
+
+ mov32 r0, TEGRA_PMC_BASE
+ ldr r0, [r0, #PMC_SCRATCH41]
+ mov pc, r0
+ENDPROC(tegra3_lp1_reset)
+
+ .align L1_CACHE_SHIFT
+ .type tegra3_sdram_pad_save, %object
+tegra3_sdram_pad_save:
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+
+tegra3_sdram_pad_address:
+ .word TEGRA_EMC_BASE + EMC_CFG @0x0
+ .word TEGRA_EMC_BASE + EMC_ZCAL_INTERVAL @0x4
+ .word TEGRA_EMC_BASE + EMC_AUTO_CAL_INTERVAL @0x8
+ .word TEGRA_EMC_BASE + EMC_XM2VTTGENPADCTRL @0xc
+ .word TEGRA_EMC_BASE + EMC_XM2VTTGENPADCTRL2 @0x10
+ .word TEGRA_PMC_BASE + PMC_IO_DPD_STATUS @0x14
+ .word TEGRA_CLK_RESET_BASE + CLK_RESET_CLK_SOURCE_MSELECT @0x18
+ .word TEGRA_CLK_RESET_BASE + CLK_RESET_SCLK_BURST @0x1c
+
+tegra3_sdram_pad_size:
+ .word tegra3_sdram_pad_address - tegra3_sdram_pad_save
+
+/*
+ * tegra3_tear_down_core
+ *
+ * copied into and executed from IRAM
+ * puts memory in self-refresh for LP0 and LP1
+ */
+tegra3_tear_down_core:
+ bl tegra3_sdram_self_refresh
+ bl tegra3_cpu_clk32k
+ b tegra3_enter_sleep
+
+/*
+ * tegra3_cpu_clk32k
+ *
+ * In LP0 and LP1 all plls will be turned off. Switch the CPU and system clock
+ * to the 32khz clock (clks)
+ * r4 = TEGRA_PMC_BASE
+ * r5 = TEGRA_CLK_RESET_BASE
+ * r6 = TEGRA_FLOW_CTRL_BASE
+ * r7 = TEGRA_TMRUS_BASE
+ */
+tegra3_cpu_clk32k:
+ /* start by jumping to clkm to safely disable PLLs, then jump
+ * to clks */
+ mov r0, #(1 << 28)
+ str r0, [r5, #CLK_RESET_SCLK_BURST]
+ str r0, [r5, #CLK_RESET_CCLK_BURST]
+ mov r0, #0
+ str r0, [r5, #CLK_RESET_CCLK_DIVIDER]
+ str r0, [r5, #CLK_RESET_SCLK_DIVIDER]
+
+ /* switch the clock source for mselect to be CLK_M */
+ ldr r0, [r5, #CLK_RESET_CLK_SOURCE_MSELECT]
+ orr r0, r0, #MSELECT_CLKM
+ str r0, [r5, #CLK_RESET_CLK_SOURCE_MSELECT]
+
+ /* 2 us delay between changing sclk and disabling PLLs */
+ wait_for_us r1, r7, r9
+ add r1, r1, #2
+ wait_until r1, r7, r9
+
+#if 1
+ /* switch to CLKS */
+ mov r0, #0 /* burst policy = 32KHz */
+ str r0, [r5, #CLK_RESET_SCLK_BURST]
+#endif
+
+ /* disable PLLM via PMC in LP1 */
+ ldr r0, [r4, #PMC_CTRL]
+ tst r0, #PMC_CTRL_SIDE_EFFECT_LP0
+ bne enable_pllm_lp0
+ ldr r0, [r4, #PMC_PLLP_WB0_OVERRIDE]
+ bic r0, r0, #(1<<12)
+ str r0, [r4, #PMC_PLLP_WB0_OVERRIDE]
+ b powerdown_pll_pcx
+
+enable_pllm_lp0:
+ /* enable PLLM via PMC in LP0 */
+ ldr r0, [r4, #PMC_PLLP_WB0_OVERRIDE]
+ orr r0, r0, #((1<<12) | (1 << 11))
+ str r0, [r4, #PMC_PLLP_WB0_OVERRIDE]
+
+powerdown_pll_pcx:
+ /* disable PLLP, PLLA, PLLC, and PLLX in LP0 and LP1 states */
+ ldr r0, [r5, #CLK_RESET_PLLP_BASE]
+ bic r0, r0, #(1<<30)
+ str r0, [r5, #CLK_RESET_PLLP_BASE]
+ ldr r0, [r5, #CLK_RESET_PLLA_BASE]
+ bic r0, r0, #(1<<30)
+ str r0, [r5, #CLK_RESET_PLLA_BASE]
+ ldr r0, [r5, #CLK_RESET_PLLC_BASE]
+ bic r0, r0, #(1<<30)
+ str r0, [r5, #CLK_RESET_PLLC_BASE]
+ ldr r0, [r5, #CLK_RESET_PLLX_BASE]
+ bic r0, r0, #(1<<30)
+ str r0, [r5, #CLK_RESET_PLLX_BASE]
+
+ mov pc, lr
+
+/*
+ * tegra3_enter_sleep
+ *
+ * uses flow controller to enter sleep state
+ * executes from IRAM with SDRAM in selfrefresh when target state is LP0 or LP1
+ * executes from SDRAM with target state is LP2
+ * r4 = TEGRA_PMC_BASE
+ * r5 = TEGRA_CLK_RESET_BASE
+ * r6 = TEGRA_FLOW_CTRL_BASE
+ * r7 = TEGRA_TMRUS_BASE
+ */
+tegra3_enter_sleep:
+ ldr r1, [r7]
+ str r1, [r4, #PMC_SCRATCH38]
+ dsb
+ cpu_id r1
+
+ cpu_to_csr_reg r2, r1
+ ldr r0, [r6, r2]
+ orr r0, r0, #FLOW_CTRL_CSR_INTR_FLAG | FLOW_CTRL_CSR_EVENT_FLAG
+ orr r0, r0, #FLOW_CTRL_CSR_ENABLE
+ str r0, [r6, r2]
+
+ mov r0, #FLOW_CTRL_WAIT_FOR_INTERRUPT
+ orr r0, r0, #FLOW_CTRL_HALT_CPU_IRQ | FLOW_CTRL_HALT_CPU_FIQ
+ cpu_to_halt_reg r2, r1
+ str r0, [r6, r2]
+ dsb
+ ldr r0, [r6, r2] /* memory barrier */
+
+halted:
+ isb
+ dsb
+ wfi /* CPU should be power gated here */
+
+ /* !!!FIXME!!! Implement halt failure handler */
+ b halted
+
+/*
+ * tegra3_sdram_self_refresh
+ *
+ * called with MMU off and caches disabled
+ /* puts sdram in self refresh
+ * must execute from IRAM
+ * r4 = TEGRA_PMC_BASE
+ * r5 = TEGRA_CLK_RESET_BASE
+ * r6 = TEGRA_FLOW_CTRL_BASE
+ * r7 = TEGRA_TMRUS_BASE
+ */
+
+tegra3_sdram_self_refresh:
+
+ adr r2, tegra3_sdram_pad_address
+ adr r8, tegra3_sdram_pad_save
+ mov r9, #0
+
+padsave:
+ ldr r0, [r2, r9] @ r0 is emc register address
+
+ ldr r1, [r0]
+ str r1, [r8, r9] @ save emc register
+
+ add r9, r9, #4
+ ldr r0, tegra3_sdram_pad_size
+ cmp r0, r9
+ bne padsave
+padsave_done:
+
+ dsb
+
+ mov32 r0, TEGRA_EMC_BASE @ r0 reserved for emc base
+
+ mov r1, #0
+ str r1, [r0, #EMC_ZCAL_INTERVAL]
+ str r1, [r0, #EMC_AUTO_CAL_INTERVAL]
+ ldr r1, [r0, #EMC_CFG]
+ bic r1, r1, #(1<<28)
+ str r1, [r0, #EMC_CFG] @ disable DYN_SELF_REF
+
+ emc_timing_update r1, r0
+
+ ldr r1, [r7]
+ add r1, r1, #5
+ wait_until r1, r7, r2
+
+emc_wait_audo_cal:
+ ldr r1, [r0, #EMC_AUTO_CAL_STATUS]
+ tst r1, #(0x1<<31) @ wait until AUTO_CAL_ACTIVE is clear
+ bne emc_wait_audo_cal
+
+ mov r1, #3
+ str r1, [r0, #EMC_REQ_CTRL] @ stall incoming DRAM requests
+
+emcidle:
+ ldr r1, [r0, #EMC_EMC_STATUS]
+ tst r1, #4
+ beq emcidle
+
+ mov r1, #1
+ str r1, [r0, #EMC_SELF_REF]
+
+ emc_device_mask r1, r0
+
+emcself:
+ ldr r2, [r0, #EMC_EMC_STATUS]
+ and r2, r2, r1
+ cmp r2, r1
+ bne emcself @ loop until DDR in self-refresh
+
+ ldr r1, [r0, #EMC_XM2VTTGENPADCTRL]
+ mov32 r2, 0xF8F8FFFF @ clear XM2VTTGEN_DRVUP and XM2VTTGEN_DRVDN
+ and r1, r1, r2
+ str r1, [r0, #EMC_XM2VTTGENPADCTRL]
+ ldr r1, [r0, #EMC_XM2VTTGENPADCTRL2]
+ orr r1, r1, #7 @ set E_NO_VTTGEN
+ str r1, [r0, #EMC_XM2VTTGENPADCTRL2]
+
+ emc_timing_update r1, r0
+
+ ldr r1, [r4, #PMC_CTRL]
+ tst r1, #PMC_CTRL_SIDE_EFFECT_LP0
+ bne pmc_io_dpd_skip
+ mov32 r1, 0x8EC00000
+ str r1, [r4, #PMC_IO_DPD_REQ]
+pmc_io_dpd_skip:
+
+ dsb
+
+ mov pc, lr
+
+ .ltorg
+/* dummy symbol for end of IRAM */
+ .align L1_CACHE_SHIFT
+ .globl tegra3_iram_end
+tegra3_iram_end:
+ b .
+#endif
diff --git a/arch/arm/mach-tegra/sleep.S b/arch/arm/mach-tegra/sleep.S
new file mode 100644
index 000000000000..38e5f69a3437
--- /dev/null
+++ b/arch/arm/mach-tegra/sleep.S
@@ -0,0 +1,491 @@
+/*
+ * arch/arm/mach-tegra/sleep.S
+ *
+ * Copyright (c) 2010-2011, NVIDIA Corporation.
+ * Copyright (c) 2011, Google, Inc.
+ *
+ * Author: Colin Cross <ccross@android.com>
+ * Gary King <gking@nvidia.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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/const.h>
+#include <linux/init.h>
+#include <linux/linkage.h>
+
+#include <asm/assembler.h>
+#include <asm/cache.h>
+#include <asm/domain.h>
+#include <asm/memory.h>
+#include <asm/page.h>
+#include <asm/ptrace.h>
+#include <asm/asm-offsets.h>
+#include <asm/glue-cache.h>
+#include <asm/glue-proc.h>
+#include <asm/system.h>
+
+#include <mach/iomap.h>
+#include <mach/io.h>
+
+#include "asm_macros.h"
+#include "sleep.h"
+
+#define CLK_RESET_CCLK_BURST 0x20
+#define CLK_RESET_CCLK_DIVIDER 0x24
+
+#define TEGRA_PMC_VIRT (TEGRA_PMC_BASE - IO_APB_PHYS + IO_APB_VIRT)
+#define TEGRA_CLK_RESET_VIRT (TEGRA_CLK_RESET_BASE - IO_PPSB_PHYS + IO_PPSB_VIRT)
+
+/*
+ * tegra_pen_lock
+ *
+ * spinlock implementation with no atomic test-and-set and no coherence
+ * using Peterson's algorithm on strongly-ordered registers
+ * used to synchronize a cpu waking up from wfi with entering lp2 on idle
+ *
+ * SCRATCH37 = r1 = !turn (inverted from Peterson's algorithm)
+ * on cpu 0:
+ * SCRATCH38 = r2 = flag[0]
+ * SCRATCH39 = r3 = flag[1]
+ * on cpu1:
+ * SCRATCH39 = r2 = flag[1]
+ * SCRATCH38 = r3 = flag[0]
+ *
+ * must be called with MMU on
+ * corrupts r0-r3, r12
+ */
+ENTRY(tegra_pen_lock)
+ mov32 r3, TEGRA_PMC_VIRT
+ cpu_id r0
+ add r1, r3, #PMC_SCRATCH37
+ cmp r0, #0
+ addeq r2, r3, #PMC_SCRATCH38
+ addeq r3, r3, #PMC_SCRATCH39
+ addne r2, r3, #PMC_SCRATCH39
+ addne r3, r3, #PMC_SCRATCH38
+
+ mov r12, #1
+ str r12, [r2] @ flag[cpu] = 1
+ dsb
+ str r12, [r1] @ !turn = cpu
+1: dsb
+ ldr r12, [r3]
+ cmp r12, #1 @ flag[!cpu] == 1?
+ ldreq r12, [r1]
+ cmpeq r12, r0 @ !turn == cpu?
+ beq 1b @ while !turn == cpu && flag[!cpu] == 1
+
+ mov pc, lr @ locked
+ENDPROC(tegra_pen_lock)
+
+ENTRY(tegra_pen_unlock)
+ dsb
+ mov32 r3, TEGRA_PMC_VIRT
+ cpu_id r0
+ cmp r0, #0
+ addeq r2, r3, #PMC_SCRATCH38
+ addne r2, r3, #PMC_SCRATCH39
+ mov r12, #0
+ str r12, [r2]
+ mov pc, lr
+ENDPROC(tegra_pen_unlock)
+
+/*
+ * tegra_cpu_wfi
+ *
+ * puts current CPU in clock-gated wfi using the flow controller
+ *
+ * corrupts r0-r3
+ * must be called with MMU on
+ */
+ENTRY(tegra_cpu_wfi)
+ cpu_id r0
+ cpu_to_halt_reg r1, r0
+ cpu_to_csr_reg r2, r0
+ mov32 r0, TEGRA_FLOW_CTRL_VIRT
+ mov r3, #FLOW_CTRL_CSR_INTR_FLAG | FLOW_CTRL_CSR_EVENT_FLAG
+ str r3, [r0, r2] @ clear event & interrupt status
+ mov r3, #FLOW_CTRL_WAIT_FOR_INTERRUPT | FLOW_CTRL_JTAG_RESUME
+ str r3, [r0, r1] @ put flow controller in wait irq mode
+ dsb
+ wfi
+ mov r3, #0
+ str r3, [r0, r1] @ clear flow controller halt status
+ mov r3, #FLOW_CTRL_CSR_INTR_FLAG | FLOW_CTRL_CSR_EVENT_FLAG
+ str r3, [r0, r2] @ clear event & interrupt status
+ dsb
+ mov pc, lr
+ENDPROC(tegra_cpu_wfi)
+
+/*
+ * tegra_cpu_exit_coherency
+ *
+ * Exits SMP coherency.
+ * corrupts r4-r5
+ */
+ENTRY(tegra_cpu_exit_coherency)
+ exit_smp r4, r5
+ mov pc, lr
+ENDPROC(tegra_cpu_exit_coherency)
+
+#ifdef CONFIG_PM_SLEEP
+/*
+ * Restore CPU state for a suspend
+ *
+ * NOTE: This is a copy of cpu_resume in arch/arm/sleep.S that has been
+ * modified to work with an L2 cache.
+ */
+ .align L1_CACHE_SHIFT
+ENTRY(tegra_cpu_resume_phys)
+#if USE_TEGRA_CPU_SUSPEND
+#ifdef CONFIG_SMP
+ adr r0, tegra_phys_sleep_sp
+ ALT_SMP(mrc p15, 0, r1, c0, c0, 5)
+ ALT_UP(mov r1, #0)
+ and r1, r1, #15
+ ldr r0, [r0, r1, lsl #2] @ stack phys addr
+#else
+ ldr r0, tegra_phys_sleep_sp @ stack phys addr
+#endif
+ setmode PSR_I_BIT | PSR_F_BIT | SVC_MODE, r1 @ set SVC, irqs off
+#ifdef MULTI_CPU
+ @ load v:p, stack, resume fn
+ ARM( ldmia r0!, {r1, sp, pc} )
+THUMB( ldmia r0!, {r1, r2, r3} )
+THUMB( mov sp, r2 )
+THUMB( bx r3 )
+#else
+ @ load v:p, stack, return fn
+ ARM( ldmia r0!, {r1, sp, lr} )
+THUMB( ldmia r0!, {r1, r2, lr} )
+THUMB( mov sp, r2 )
+ b cpu_do_resume
+#endif
+#else
+ /* Use the standard cpu_resume. */
+ b cpu_resume
+#endif
+ENDPROC(tegra_cpu_resume_phys)
+
+#if USE_TEGRA_CPU_SUSPEND
+ .align L1_CACHE_SHIFT
+ .globl tegra_phys_sleep_sp
+tegra_phys_sleep_sp:
+ .rept 4
+ .long 0 @ preserve stack phys ptr here
+ .endr
+ .align L1_CACHE_SHIFT @ nothing else must be in this cache line
+#endif
+
+/*
+ * tegra_cpu_suspend
+ *
+ * Save CPU suspend state
+ * NOTE: This is a copy of cpu_suspend in arch/arm/sleep.S that has been
+ * modified to work with an L2 cache.
+ *
+ * Input:
+ * r1 = v:p offset
+ * r3 = virtual return function
+ * Output:
+ * sp is decremented to allocate space for CPU state on stack
+ * r0-r3,ip,lr corrupted
+ */
+ .align L1_CACHE_SHIFT
+ENTRY(tegra_cpu_suspend)
+#if USE_TEGRA_CPU_SUSPEND
+ stmfd sp!, {r3}
+ stmfd sp!, {r4 - r11}
+ mov r9, lr
+#ifdef MULTI_CPU
+ mov32 r10, processor
+ mov r2, sp @ current virtual SP
+ ldr r0, [r10, #CPU_SLEEP_SIZE] @ size of CPU sleep state
+ ldr ip, [r10, #CPU_DO_RESUME] @ virtual resume function
+ sub sp, sp, r0 @ allocate CPU state on stack
+ mov r0, sp @ save pointer
+ add ip, ip, r1 @ convert resume fn to phys
+ stmfd sp!, {r1, r2, ip} @ save v:p, virt SP, phys resume fn
+ mov lr, pc
+ ldr pc, [r10, #CPU_DO_SUSPEND] @ save CPU state
+#else
+ mov r2, sp @ current virtual SP
+ mov32 r0, cpu_suspend_size
+ sub sp, sp, r0 @ allocate CPU state on stack
+ mov r0, sp @ save pointer
+ stmfd sp!, {r1, r2, ip} @ save v:p, virt SP, phys resume fn
+ bl cpu_do_suspend
+#endif
+ dsb
+
+ /* Disable the data cache */
+ mrc p15, 0, r10, c1, c0, 0
+ bic r10, r10, #CR_C
+ dsb
+ mcr p15, 0, r10, c1, c0, 0
+ isb
+
+ /* Flush data cache */
+#ifdef MULTI_CACHE
+ mov32 r10, cpu_cache
+ mov lr, pc
+ ldr pc, [r10, #CACHE_FLUSH_KERN_ALL]
+#else
+ bl __cpuc_flush_kern_all
+#endif
+#ifdef CONFIG_CACHE_L2X0
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+ cpu_id r2
+ cmp r2, #0
+ bne no_l2_sync
+#endif
+ /* Issue a PL310 cache sync operation */
+ dsb
+ mov32 r2, TEGRA_PL310_VIRT
+ movw r1, 0x730 @ cache sync
+ add r2, r2, r1
+ mov r1, #0
+ str r1, [r2]
+#endif
+
+no_l2_sync:
+ /* Invalidate the TLBs & BTAC */
+ mov r1, #0
+ mcr p15, 0, r1, c8, c3, 0 @ invalidate shared TLBs
+ mcr p15, 0, r1, c7, c1, 6 @ invalidate shared BTAC
+ dsb
+ isb
+
+ /* Turn off SMP coherency */
+ exit_smp r1, r2
+
+ /* Convert SP from virtual to physical address. */
+ movw r1, #0xFFF
+ bic r2, sp, r1 @ VA & 0xFFFFF000
+ mcr p15, 0, r2, c7, c8, 0 @ V2PPRPC
+ mrc p15, 0, r2, c7, c4, 0 @ PAR
+ bic r2, r2, r1 @ PA & 0xFFFFF000
+ and r0, sp, r1 @ VA & 0x00000FFF
+ orr r2, r0, r2 @ (PA & 0xFFFFF000) | (VA & 0x00000FFF)
+
+ mov32 r3, tegra_phys_sleep_sp @ per-CPU phys SP save area
+
+#ifdef CONFIG_SMP
+ ALT_SMP(mrc p15, 0, lr, c0, c0, 5)
+ ALT_UP(mov lr, #0)
+ and lr, lr, #15
+#else
+ mov lr, #0
+#endif
+
+ /* Save the normal PRRR value */
+ mrc p15, 0, r0, c10, c2, 0 @ PRRR
+
+ /* Override all remappings to strongly ordered */
+ mov r1, #0
+ mcr p15, 0, r1, c10, c2, 0 @ PRRR
+ mcr p15, 0, r1, c8, c7, 0 @ invalidate local TLBs
+ dsb
+ isb
+
+ /* Save the physical stack pointer */
+ str r2, [r3, lr, lsl #2] @ save phys SP
+
+ /* Restore the regular remappings */
+ mcr p15, 0, r0, c10, c2, 0 @ PRRR
+ mcr p15, 0, r1, c8, c7, 0 @ invalidate local TLBs
+ dsb
+ isb
+
+ mov pc, r9
+#else
+ /* Use the standard cpu_suspend. */
+ mov r8, lr
+ bl cpu_suspend
+ exit_smp r0, r2
+ mov pc, r8
+#endif
+ENDPROC(tegra_cpu_suspend)
+
+/*
+ * tegra_cpu_save
+ *
+ * Input:
+ * r0 = v:p offset
+ * r12 = return to the caller of this function
+ * lr = resume address
+ * Output:
+ * r0 = v:p offset
+ * r7 = SP after saving the registers but before cpu_suspend, suitable
+ * for restoring an aborted suspend
+ * sp = SP after tegra_cpu_suspend (the 'real' SP)
+ * Saves r4-r11 on the stack
+ * Corrupts r1, r3-r10
+ */
+
+ENTRY(tegra_cpu_save)
+ push_ctx_regs r1 @ save context registers
+
+ adr r3, tegra_cpu_resume
+
+ mov r7, sp @ SP after reg save, before suspend
+
+#if USE_TEGRA_CPU_SUSPEND
+ cpu_id r4
+ mov32 r5, tegra_cpu_context @ address of non-cacheable context page
+ ldr r5, [r5] @ non-cacheable context save area
+ mov r6, #0x400 @ size of one CPU context stack area
+ add r4, r4, #1
+ smlabb sp, r6, r4, r5 @ context area for this CPU
+ push_stack_token r4 @ debug check word
+ stmfd sp!, {r7} @ save the real stack pointer
+ push_stack_token r4 @ debug check word
+#endif
+
+ mov r4, r12
+ mov r5, r0
+ mov r6, r2
+ mov r1, r0
+ bl tegra_cpu_suspend
+ mov r0, r5
+ mov r2, r6
+ mov pc, r4
+ENDPROC(tegra_cpu_save)
+
+/*
+ * tegra_sleep_cpu_save(unsigned long v2p)
+ *
+ * enters suspend in LP2 by turning off the mmu and jumping to
+ * tegra?_tear_down_cpu
+ */
+ENTRY(tegra_sleep_cpu_save)
+ mov r12, pc @ return here is via r12
+ b tegra_cpu_save
+
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+ mov32 r1, tegra2_tear_down_cpu
+#else
+ mov32 r1, tegra3_tear_down_cpu
+#endif
+ add r1, r1, r0
+ b tegra_turn_off_mmu
+ENDPROC(tegra_sleep_cpu_save)
+
+/*
+ * tegra_cpu_resume
+ *
+ * reloads the volatile CPU state from the context area
+ * initializes the processor mode stacks
+ * the mmu should be on and the CPU should be coherent before this is called
+ */
+ .align L1_CACHE_SHIFT
+tegra_cpu_resume:
+ mov r0, #0
+ mcr p15, 0, r0, c8, c3, 0 @ invalidate TLB
+ mcr p15, 0, r0, c7, c5, 6 @ flush BTAC
+ mcr p15, 0, r0, c7, c5, 0 @ flush instruction cache
+ dsb
+ isb
+
+#if USE_TEGRA_CPU_SUSPEND
+ pop_stack_token r4, r5 @ check stack debug token
+ ldmfd sp!, {r0} @ get the real stack pointer
+ pop_stack_token r4, r5 @ check stack debug token
+ mov sp, r0 @ switch to the real stack pointer
+#endif
+
+ bl cpu_init
+
+ pop_ctx_regs r1, r2 @ restore context registers
+ mov pc, lr
+
+/*
+ * tegra_turn_off_mmu
+ *
+ * r0 = v2p
+ * r1 = physical address to jump to with mmu off
+ */
+ENTRY(tegra_turn_off_mmu)
+ mov32 r3, tegra_shut_off_mmu
+ add r3, r3, r0
+ mov r0, r1
+ mov pc, r3
+ENDPROC(tegra_turn_off_mmu)
+
+tegra_pgd_phys_address:
+ .word tegra_pgd_phys
+
+/*
+ * tegra_shut_off_mmu
+ *
+ * r0 = physical address to jump to with mmu off
+ *
+ * called with VA=PA mapping
+ * turns off MMU, icache, dcache and branch prediction
+ */
+ .align L1_CACHE_SHIFT
+tegra_shut_off_mmu:
+ mrc p15, 0, r3, c1, c0, 0
+ movw r2, #CR_I | CR_Z | CR_C | CR_M
+ bic r3, r3, r2
+ dsb
+ mcr p15, 0, r3, c1, c0, 0
+ isb
+ mov pc, r0
+
+/*
+ * tegra_cpu_clk32k
+ *
+ * In LP2 the normal cpu clock pllx will be turned off. Switch the CPU to pllp
+ */
+ENTRY(tegra_cpu_pllp)
+ /* in LP2 idle (SDRAM active), set the CPU burst policy to PLLP */
+ mov32 r5, TEGRA_CLK_RESET_BASE
+ mov r0, #(2 << 28) @ burst policy = run mode
+ orr r0, r0, #(4 << 4) @ use PLLP in run mode burst
+ str r0, [r5, #CLK_RESET_CCLK_BURST]
+ mov r0, #0
+ str r0, [r5, #CLK_RESET_CCLK_DIVIDER]
+ mov pc, lr
+ENDPROC(tegra_cpu_pllp)
+#endif
+
+#ifdef CONFIG_TRUSTED_FOUNDATIONS
+/*
+ * tegra_generic_smc
+ *
+ * r0 = smc type
+ * r1 = smc subtype
+ * r2 = argument passed to smc
+ *
+ * issues SMC (secure monitor call) instruction with
+ * the specified parameters.
+ */
+ENTRY(tegra_generic_smc)
+ adr r3, __tegra_smc_stack
+ stmia r3, {r4-r12, lr}
+ mov r3, #0
+ mov r4, #0
+ dsb
+ smc #0
+ adr r3, __tegra_smc_stack
+ ldmia r3, {r4-r12, pc}
+ENDPROC(tegra_generic_smc)
+ .type __tegra_smc_stack, %object
+__tegra_smc_stack:
+ .long 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ .size __tegra_smc_stack, . - __tegra_smc_stack
+#endif
diff --git a/arch/arm/mach-tegra/sleep.h b/arch/arm/mach-tegra/sleep.h
new file mode 100644
index 000000000000..7b8f84d61699
--- /dev/null
+++ b/arch/arm/mach-tegra/sleep.h
@@ -0,0 +1,246 @@
+/*
+ * arch/arm/mach-tegra/sleep.h
+ *
+ * Declarations for power state transition code
+ *
+ * Copyright (c) 2010-2011, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __MACH_TEGRA_SLEEP_H
+#define __MACH_TEGRA_SLEEP_H
+
+#include <mach/iomap.h>
+
+#ifdef CONFIG_CACHE_L2X0
+#define USE_TEGRA_CPU_SUSPEND 1
+#else
+#define USE_TEGRA_CPU_SUSPEND 0
+#endif
+#ifndef CONFIG_TRUSTED_FOUNDATIONS
+/* FIXME: The code associated with this should be removed if our change to
+ save the diagnostic regsiter in the CPU context is accepted. */
+#define USE_TEGRA_DIAG_REG_SAVE 1
+#else
+#define USE_TEGRA_DIAG_REG_SAVE 0
+#endif
+
+#define TEGRA_POWER_SDRAM_SELFREFRESH (1 << 26) /* SDRAM is in self-refresh */
+#define TEGRA_POWER_HOTPLUG_SHUTDOWN (1 << 27) /* Hotplug shutdown */
+#define TEGRA_POWER_CLUSTER_G (1 << 28) /* G CPU */
+#define TEGRA_POWER_CLUSTER_LP (1 << 29) /* LP CPU */
+#define TEGRA_POWER_CLUSTER_MASK (TEGRA_POWER_CLUSTER_G | \
+ TEGRA_POWER_CLUSTER_LP)
+#define TEGRA_POWER_CLUSTER_IMMEDIATE (1 << 30) /* Immediate wake */
+#define TEGRA_POWER_CLUSTER_FORCE (1 << 31) /* Force switch */
+
+#define TEGRA_IRAM_CODE_AREA (TEGRA_IRAM_BASE + SZ_4K)
+
+/* PMC_SCRATCH37-39 and 41 are used for tegra_pen_lock in Tegra2 idle */
+#define PMC_SCRATCH37 0x130
+#define PMC_SCRATCH38 0x134
+/* PMC_SCRATCH39 stores the reset vector of the AVP (always 0) after LP0 */
+#define PMC_SCRATCH39 0x138
+/* PMC_SCRATCH41 stores the reset vector of the CPU after LP0 and LP1 */
+#define PMC_SCRATCH41 0x140
+
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+#define CPU_RESETTABLE 2
+#define CPU_RESETTABLE_SOON 1
+#define CPU_NOT_RESETTABLE 0
+#endif
+
+#define FLOW_CTRL_HALT_CPU0_EVENTS 0x0
+#define FLOW_CTRL_WAITEVENT (2 << 29)
+#define FLOW_CTRL_WAIT_FOR_INTERRUPT (4 << 29)
+#define FLOW_CTRL_JTAG_RESUME (1 << 28)
+#define FLOW_CTRL_HALT_CPU_IRQ (1 << 10)
+#define FLOW_CTRL_HALT_CPU_FIQ (1 << 8)
+#define FLOW_CTRL_CPU0_CSR 0x8
+#define FLOW_CTRL_CSR_INTR_FLAG (1 << 15)
+#define FLOW_CTRL_CSR_EVENT_FLAG (1 << 14)
+#define FLOW_CTRL_CSR_ENABLE (1 << 0)
+#define FLOW_CTRL_HALT_CPU1_EVENTS 0x14
+#define FLOW_CTRL_CPU1_CSR 0x18
+
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+#define FLOW_CTRL_CSR_WFE_CPU0 (1 << 4)
+#define FLOW_CTRL_CSR_WFE_BITMAP (3 << 4)
+#define FLOW_CTRL_CSR_WFI_BITMAP 0
+#else
+#define FLOW_CTRL_CSR_WFE_BITMAP (0xF << 4)
+#define FLOW_CTRL_CSR_WFI_CPU0 (1 << 8)
+#define FLOW_CTRL_CSR_WFI_BITMAP (0xF << 8)
+#endif
+
+#define TEGRA_PL310_VIRT (TEGRA_ARM_PL310_BASE - IO_CPU_PHYS + IO_CPU_VIRT)
+#define TEGRA_FLOW_CTRL_VIRT (TEGRA_FLOW_CTRL_BASE - IO_PPSB_PHYS + IO_PPSB_VIRT)
+#define TEGRA_ARM_PERIF_VIRT (TEGRA_ARM_PERIF_BASE - IO_CPU_PHYS + IO_CPU_VIRT)
+
+#ifdef __ASSEMBLY__
+
+/* Macro to exit SMP coherency. */
+.macro exit_smp, tmp1, tmp2
+ mrc p15, 0, \tmp1, c1, c0, 1 @ ACTLR
+ bic \tmp1, \tmp1, #(1<<6) | (1<<0) @ clear ACTLR.SMP | ACTLR.FW
+ mcr p15, 0, \tmp1, c1, c0, 1 @ ACTLR
+ isb
+ cpu_id \tmp1
+ mov \tmp1, \tmp1, lsl #2
+ mov \tmp2, #0xf
+ mov \tmp2, \tmp2, lsl \tmp1
+ mov32 \tmp1, TEGRA_ARM_PERIF_VIRT + 0xC
+ str \tmp2, [\tmp1] @ invalidate SCU tags for CPU
+ dsb
+.endm
+
+#define DEBUG_CONTEXT_STACK 0
+
+/* pops a debug check token from the stack */
+.macro pop_stack_token tmp1, tmp2
+#if DEBUG_CONTEXT_STACK
+ mov32 \tmp1, 0xBAB1F00D
+ ldmfd sp!, {\tmp2}
+ cmp \tmp1, \tmp2
+ movne pc, #0
+#endif
+.endm
+
+/* pushes a debug check token onto the stack */
+.macro push_stack_token tmp1
+#if DEBUG_CONTEXT_STACK
+ mov32 \tmp1, 0xBAB1F00D
+ stmfd sp!, {\tmp1}
+#endif
+.endm
+
+.macro push_ctx_regs, tmp1
+ push_stack_token \tmp1 @ debug check word
+ stmfd sp!, {r4 - r11, lr}
+ /* Save the current TTB0 and CONTEXTID registers. */
+ mrc p15, 0, r5, c2, c0, 0 @ TTB 0
+ mrc p15, 0, r6, c13, c0, 1 @ CONTEXTID
+#if USE_TEGRA_DIAG_REG_SAVE
+ mrc p15, 0, r4, c15, c0, 1 @ read diagnostic register
+ stmfd sp!, {r4-r6}
+#else
+ stmfd sp!, {r5-r6}
+#endif
+ /* Switch to the tegra_pgd so that IRAM and the MMU shut-off code
+ will be flat mapped (VA==PA). We also do this because the common
+ ARM CPU state save/restore code doesn't support an external L2
+ cache controller. If the current PGD is left active, the common
+ ARM MMU restore may (and eventually will) damage the currently
+ running page tables by adding a temporary flat section mapping
+ that could be picked up by other CPUs from the L2 cache
+ resulting in a kernel panic. */
+ ldr r6, tegra_pgd_phys_address
+ ldr r6, [r6]
+ mov r7, #0
+ dsb
+ mcr p15, 0, r7, c13, c0, 1 @ CONTEXTID = reserved context
+ isb
+ mcr p15, 0, r6, c2, c0, 0 @ TTB 0
+ isb
+ mcr p15, 0, r7, c8, c3, 0 @ invalidate TLB
+ mcr p15, 0, r7, c7, c5, 6 @ flush BTAC
+ mcr p15, 0, r7, c7, c5, 0 @ flush instruction cache
+ dsb
+.endm
+
+.macro pop_ctx_regs, tmp1, tmp2
+#if USE_TEGRA_DIAG_REG_SAVE
+ ldmfd sp!, {r4-r6}
+ mcr p15, 0, r4, c15, c0, 1 @ write diagnostic register
+#else
+ ldmfd sp!, {r5-r6}
+#endif
+ dsb
+ mcr p15, 0, r5, c2, c0, 0 @ TTB 0
+ isb
+ mcr p15, 0, r6, c13, c0, 1 @ CONTEXTID = reserved context
+ isb
+ mov r7, #0
+ mcr p15, 0, r7, c8, c3, 0 @ invalidate TLB
+ mcr p15, 0, r7, c7, c5, 6 @ flush BTAC
+ mcr p15, 0, r7, c7, c5, 0 @ flush instruction cache
+ dsb
+ ldmfd sp!, {r4 - r11, lr}
+ pop_stack_token \tmp1, \tmp2 @ debug stack debug token
+.endm
+
+#else /* !defined(__ASSEMBLY__) */
+
+#define FLOW_CTRL_HALT_CPU(cpu) (IO_ADDRESS(TEGRA_FLOW_CTRL_BASE) + \
+ ((cpu) ? (FLOW_CTRL_HALT_CPU1_EVENTS + 8 * ((cpu) - 1)) : \
+ FLOW_CTRL_HALT_CPU0_EVENTS))
+
+#define FLOW_CTRL_CPU_CSR(cpu) (IO_ADDRESS(TEGRA_FLOW_CTRL_BASE) + \
+ ((cpu) ? (FLOW_CTRL_CPU1_CSR + 8 * ((cpu) - 1)) : \
+ FLOW_CTRL_CPU0_CSR))
+
+static inline void flowctrl_writel(unsigned long val, void __iomem *addr)
+{
+ writel(val, addr);
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+ wmb();
+#endif
+ (void)__raw_readl(addr);
+}
+
+void tegra_pen_lock(void);
+void tegra_pen_unlock(void);
+void tegra_cpu_wfi(void);
+void tegra_sleep_cpu_save(unsigned long v2p);
+void tegra_resume(void);
+
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+extern void tegra2_iram_start;
+extern void tegra2_iram_end;
+int tegra2_cpu_is_resettable_soon(void);
+void tegra2_cpu_reset(int cpu);
+void tegra2_cpu_set_resettable_soon(void);
+void tegra2_cpu_clear_resettable(void);
+void tegra2_sleep_core(unsigned long v2p);
+void tegra2_hotplug_shutdown(void);
+void tegra2_sleep_wfi(unsigned long v2p);
+#else
+extern void tegra3_iram_start;
+extern void tegra3_iram_end;
+void tegra3_sleep_core(unsigned long v2p);
+void tegra3_sleep_cpu_secondary(unsigned long v2p);
+void tegra3_hotplug_shutdown(void);
+#endif
+
+static inline void *tegra_iram_start(void)
+{
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+ return &tegra2_iram_start;
+#else
+ return &tegra3_iram_start;
+#endif
+}
+
+static inline void *tegra_iram_end(void)
+{
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+ return &tegra2_iram_end;
+#else
+ return &tegra3_iram_end;
+#endif
+}
+#endif
+#endif
diff --git a/arch/arm/mach-tegra/syncpt.c b/arch/arm/mach-tegra/syncpt.c
new file mode 100644
index 000000000000..8ebab3801a8a
--- /dev/null
+++ b/arch/arm/mach-tegra/syncpt.c
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * Author:
+ * Erik Gilling <konkers@google.com>
+ *
+ * Copyright (C) 2010, NVIDIA Corporation
+ *
+ * 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/init.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+
+#include <asm/mach/irq.h>
+
+#include <mach/iomap.h>
+#include <mach/irqs.h>
+
+#define HOST1X_SYNC_OFFSET 0x3000
+#define HOST1X_SYNC_SIZE 0x800
+enum {
+ HOST1X_SYNC_SYNCPT_THRESH_CPU0_INT_STATUS = 0x40,
+ HOST1X_SYNC_SYNCPT_THRESH_INT_DISABLE = 0x60
+};
+
+static void syncpt_thresh_mask(struct irq_data *data)
+{
+ (void)data;
+}
+
+static void syncpt_thresh_unmask(struct irq_data *data)
+{
+ (void)data;
+}
+
+static void syncpt_thresh_cascade(unsigned int irq, struct irq_desc *desc)
+{
+ void __iomem *sync_regs = irq_desc_get_handler_data(desc);
+ unsigned long reg;
+ int id;
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+
+ chained_irq_enter(chip, desc);
+
+ reg = readl(sync_regs + HOST1X_SYNC_SYNCPT_THRESH_CPU0_INT_STATUS);
+
+ for_each_set_bit(id, &reg, 32)
+ generic_handle_irq(id + INT_SYNCPT_THRESH_BASE);
+
+ chained_irq_exit(chip, desc);
+}
+
+static struct irq_chip syncpt_thresh_irq = {
+ .name = "syncpt",
+ .irq_mask = syncpt_thresh_mask,
+ .irq_unmask = syncpt_thresh_unmask
+};
+
+static int __init syncpt_init_irq(void)
+{
+ void __iomem *sync_regs;
+ unsigned int i;
+ int irq;
+
+ sync_regs = ioremap(TEGRA_HOST1X_BASE + HOST1X_SYNC_OFFSET,
+ HOST1X_SYNC_SIZE);
+ BUG_ON(!sync_regs);
+
+ writel(0xffffffffUL,
+ sync_regs + HOST1X_SYNC_SYNCPT_THRESH_INT_DISABLE);
+ writel(0xffffffffUL,
+ sync_regs + HOST1X_SYNC_SYNCPT_THRESH_CPU0_INT_STATUS);
+
+ for (i = 0; i < INT_SYNCPT_THRESH_NR; i++) {
+ irq = INT_SYNCPT_THRESH_BASE + i;
+ irq_set_chip_and_handler(irq, &syncpt_thresh_irq,
+ handle_simple_irq);
+ irq_set_chip_data(irq, sync_regs);
+ set_irq_flags(irq, IRQF_VALID);
+ }
+ irq_set_chained_handler(INT_HOST1X_MPCORE_SYNCPT,
+ syncpt_thresh_cascade);
+ irq_set_handler_data(INT_HOST1X_MPCORE_SYNCPT, sync_regs);
+
+ return 0;
+}
+
+core_initcall(syncpt_init_irq);
diff --git a/arch/arm/mach-tegra/sysfs-cluster.c b/arch/arm/mach-tegra/sysfs-cluster.c
new file mode 100644
index 000000000000..49c3abcf32b9
--- /dev/null
+++ b/arch/arm/mach-tegra/sysfs-cluster.c
@@ -0,0 +1,461 @@
+/*
+ * Copyright (c) 2010-2011 NVIDIA Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of the NVIDIA Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+/*
+ * This driver creates the /sys/kernel/cluster node and attributes for CPU
+ * switch testing. Node attributes:
+ *
+ * active: currently active CPU (G or LP)
+ * write: 'g' = switch to G CPU
+ * 'lp' = switch to LP CPU
+ * 'toggle' = switch to the other CPU
+ * read: returns the currently active CPU (g or lp)
+ *
+ * force: force switch even if already on target CPU
+ * write: '0' = do not perform switch if
+ * active CPU == target CPU (default)
+ * '1' = force switch regardless of
+ * currently active CPU
+ * read: returns the current status of the force flag
+ *
+ * immediate: request immediate wake-up from switch request
+ * write: '0' = non-immediate wake-up on next interrupt (default)
+ * '1' = immediate wake-up
+ * read: returns the current status of the immediate flag
+ *
+ * power_mode: power mode to use for switch (LP1 or LP2)
+ * write: '1' = use LP1 power mode
+ * '2' = use LP2 power mode (default)
+ * read: returns the current status of the immediate flag
+ *
+ * wake_ms: wake time (in milliseconds) -- ignored if immediate==1
+ * write: '0' = wake up at the next non-timer interrupt
+ * 'n' = (n > 0) wake-up after 'n' milliseconds or the
+ * next non-timer interrupt (whichever comes first)
+ * read: returns the current wake_ms value
+ *
+ * Writing the force, immediate and wake_ms attributes simply updates the
+ * state of internal variables that will be used for the next switch request.
+ * Writing to the active attribute initates a switch request using the
+ * current values of the force, immediate, and wake_ms attributes.
+ *
+ * The OS tick timer is not a valid interrupt source for waking up following
+ * a switch request. This is because the kernel uses local timers that are
+ * part of the CPU complex. These get shut down when the CPU complex is
+ * placed into reset by the switch request. If you want a timed wake up
+ * from a switch, you must specify a positive wake_ms value. This will
+ * ensure that a non-local timer is programmed to fire an interrupt
+ * after the desired interval.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sysfs.h>
+#include <linux/kobject.h>
+#include <linux/smp.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+
+#include <mach/iomap.h>
+#include "clock.h"
+#include "sleep.h"
+#include "pm.h"
+
+#define SYSFS_CLUSTER_PRINTS 1 /* Nonzero: enable status prints */
+#define SYSFS_CLUSTER_TRACE_PRINTS 0 /* Nonzero: enable trace prints */
+#define SYSFS_CLUSTER_POWER_MODE 1 /* Nonzero: use power modes other than LP2*/
+
+#if SYSFS_CLUSTER_TRACE_PRINTS
+#define TRACE_CLUSTER(x) printk x
+#else
+#define TRACE_CLUSTER(x)
+#endif
+
+#if SYSFS_CLUSTER_PRINTS
+#define PRINT_CLUSTER(x) printk x
+#else
+#define PRINT_CLUSTER(x)
+#endif
+
+static struct kobject *cluster_kobj;
+static spinlock_t cluster_lock;
+static unsigned int flags = 0;
+static unsigned int wake_ms = 0;
+
+static ssize_t sysfscluster_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf);
+
+static ssize_t sysfscluster_store(struct kobject *kobj,
+ struct kobj_attribute *attr, const char *buf, size_t count);
+
+/* Active CPU: "G", "LP", "toggle" */
+static struct kobj_attribute cluster_active_attr =
+ __ATTR(active, 0640, sysfscluster_show, sysfscluster_store);
+
+/* Immediate wake-up when performing switch: 0, 1 */
+static struct kobj_attribute cluster_immediate_attr =
+ __ATTR(immediate, 0640, sysfscluster_show, sysfscluster_store);
+
+/* Force power transition even if already on the desired CPU: 0, 1 */
+static struct kobj_attribute cluster_force_attr =
+ __ATTR(force, 0640, sysfscluster_show, sysfscluster_store);
+
+/* Wake time (in milliseconds) */
+static struct kobj_attribute cluster_wake_ms_attr =
+ __ATTR(wake_ms, 0640, sysfscluster_show, sysfscluster_store);
+
+#if defined(CONFIG_PM_SLEEP) && SYSFS_CLUSTER_POWER_MODE
+/* LPx power mode to use when switching CPUs: 1=LP1, 2=LP2 */
+static unsigned int power_mode = 2;
+static struct kobj_attribute cluster_powermode_attr =
+ __ATTR(power_mode, 0640, sysfscluster_show, sysfscluster_store);
+#endif
+
+#if DEBUG_CLUSTER_SWITCH
+unsigned int tegra_cluster_debug = 0;
+static struct kobj_attribute cluster_debug_attr =
+ __ATTR(debug, 0640, sysfscluster_show, sysfscluster_store);
+#endif
+
+typedef enum
+{
+ ClusterAttr_Invalid = 0,
+ ClusterAttr_Active,
+ ClusterAttr_Immediate,
+ ClusterAttr_Force,
+ ClusterAttr_WakeMs,
+#if defined(CONFIG_PM_SLEEP) && SYSFS_CLUSTER_POWER_MODE
+ ClusterAttr_PowerMode,
+#endif
+#if DEBUG_CLUSTER_SWITCH
+ ClusterAttr_Debug
+#endif
+} ClusterAttr;
+
+static ClusterAttr GetClusterAttr(const char *name)
+{
+ if (!strcmp(name, "active"))
+ return ClusterAttr_Active;
+ if (!strcmp(name, "immediate"))
+ return ClusterAttr_Immediate;
+ if (!strcmp(name, "force"))
+ return ClusterAttr_Force;
+ if (!strcmp(name, "wake_ms"))
+ return ClusterAttr_WakeMs;
+#if defined(CONFIG_PM_SLEEP) && SYSFS_CLUSTER_POWER_MODE
+ if (!strcmp(name, "power_mode"))
+ return ClusterAttr_PowerMode;
+#endif
+#if DEBUG_CLUSTER_SWITCH
+ if (!strcmp(name, "debug"))
+ return ClusterAttr_Debug;
+#endif
+ TRACE_CLUSTER(("GetClusterAttr(%s): invalid\n", name));
+ return ClusterAttr_Invalid;
+}
+
+static ssize_t sysfscluster_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ ClusterAttr type;
+ ssize_t len;
+
+ TRACE_CLUSTER(("+sysfscluster_show\n"));
+
+ type = GetClusterAttr(attr->attr.name);
+ switch (type) {
+ case ClusterAttr_Active:
+ len = sprintf(buf, "%s\n", is_lp_cluster() ? "LP" : "G");
+ break;
+
+ case ClusterAttr_Immediate:
+ len = sprintf(buf, "%d\n",
+ ((flags & TEGRA_POWER_CLUSTER_IMMEDIATE) != 0));
+ break;
+
+ case ClusterAttr_Force:
+ len = sprintf(buf, "%d\n",
+ ((flags & TEGRA_POWER_CLUSTER_FORCE) != 0));
+ break;
+
+ case ClusterAttr_WakeMs:
+ len = sprintf(buf, "%d\n", wake_ms);
+ break;
+
+#if defined(CONFIG_PM_SLEEP) && SYSFS_CLUSTER_POWER_MODE
+ case ClusterAttr_PowerMode:
+ len = sprintf(buf, "%d\n", power_mode);
+ break;
+#endif
+
+#if DEBUG_CLUSTER_SWITCH
+ case ClusterAttr_Debug:
+ len = sprintf(buf, "%d\n", tegra_cluster_debug);
+ break;
+#endif
+
+ default:
+ len = sprintf(buf, "invalid\n");
+ break;
+ }
+
+ TRACE_CLUSTER(("-sysfscluster_show\n"));
+ return len;
+}
+
+static ssize_t sysfscluster_store(struct kobject *kobj,
+ struct kobj_attribute *attr, const char *buf, size_t count)
+{
+ ClusterAttr type;
+ ssize_t ret = count--;
+ unsigned request;
+ int e;
+ int tmp;
+ int cnt;
+ struct clk *cpu_clk = tegra_get_clock_by_name("cpu");
+ struct clk *cpu_g_clk = tegra_get_clock_by_name("cpu_g");
+ struct clk *cpu_lp_clk = tegra_get_clock_by_name("cpu_lp");
+ struct clk *new_parent = NULL;
+
+ if (!cpu_clk || !cpu_g_clk || !cpu_lp_clk) {
+ ret = -ENOSYS;
+ goto fail;
+ }
+
+ TRACE_CLUSTER(("+sysfscluster_store: %p, %d\n", buf, count));
+
+ /* The count includes data bytes follow by a line feed character. */
+ if (!buf || (count < 1)) {
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ type = GetClusterAttr(attr->attr.name);
+
+ spin_lock(&cluster_lock);
+
+ switch (type) {
+ case ClusterAttr_Active:
+ if (!strncasecmp(buf, "g", count)) {
+ flags &= ~TEGRA_POWER_CLUSTER_MASK;
+ flags |= TEGRA_POWER_CLUSTER_G;
+ } else if (!strncasecmp(buf, "lp", count)) {
+ flags &= ~TEGRA_POWER_CLUSTER_MASK;
+ flags |= TEGRA_POWER_CLUSTER_LP;
+ } else if (!strncasecmp(buf, "toggle", count)) {
+ flags &= ~TEGRA_POWER_CLUSTER_MASK;
+ if (is_lp_cluster())
+ flags |= TEGRA_POWER_CLUSTER_G;
+ else
+ flags |= TEGRA_POWER_CLUSTER_LP;
+ } else {
+ PRINT_CLUSTER(("cluster/active: '%*.*s' invalid, "
+ " must be g, lp, or toggle\n",
+ count, count, buf));
+ ret = -EINVAL;
+ break;
+ }
+ PRINT_CLUSTER(("cluster/active -> %s\n",
+ (flags & TEGRA_POWER_CLUSTER_G) ? "G" : "LP"));
+
+ request = flags;
+#if defined(CONFIG_PM_SLEEP) && SYSFS_CLUSTER_POWER_MODE
+ if (power_mode == 1) {
+ request |= TEGRA_POWER_SDRAM_SELFREFRESH;
+ }
+#endif
+ tegra_cluster_switch_set_parameters(wake_ms * 1000, request);
+ new_parent = (flags & TEGRA_POWER_CLUSTER_LP) ?
+ cpu_lp_clk : cpu_g_clk;
+ break;
+
+ case ClusterAttr_Immediate:
+ if ((count == 1) && (*buf == '0'))
+ flags &= ~TEGRA_POWER_CLUSTER_IMMEDIATE;
+ else if ((count == 1) && *buf == '1')
+ flags |= TEGRA_POWER_CLUSTER_IMMEDIATE;
+ else {
+ PRINT_CLUSTER(("cluster/immediate: '%*.*s' invalid, "
+ "must be 0 or 1\n", count, count, buf));
+ ret = -EINVAL;
+ break;
+ }
+ PRINT_CLUSTER(("cluster/immediate -> %c\n",
+ (flags & TEGRA_POWER_CLUSTER_IMMEDIATE) ? '1' : '0'));
+ break;
+
+ case ClusterAttr_Force:
+ if ((count == 1) && (*buf == '0'))
+ flags &= ~TEGRA_POWER_CLUSTER_FORCE;
+ else if ((count == 1) && (*buf == '1'))
+ flags |= TEGRA_POWER_CLUSTER_FORCE;
+ else {
+ PRINT_CLUSTER(("cluster/force: '%*.*s' invalid, "
+ "must be 0 or 1\n", count, count, buf));
+ ret = -EINVAL;
+ break;
+ }
+ PRINT_CLUSTER(("cluster/force -> %c\n",
+ (flags & TEGRA_POWER_CLUSTER_FORCE) ? '1' : '0'));
+ break;
+
+ case ClusterAttr_WakeMs:
+ tmp = 0;
+ cnt = sscanf(buf, "%d\n", &tmp);
+ if ((cnt != 1) || (tmp < 0)) {
+ PRINT_CLUSTER(("cluster/wake_ms: '%*.*s' is invalid\n",
+ count, count, buf));
+ ret = -EINVAL;
+ break;
+ }
+ wake_ms = tmp;
+ PRINT_CLUSTER(("cluster/wake_ms -> %d\n", wake_ms));
+ break;
+
+#if defined(CONFIG_PM_SLEEP) && SYSFS_CLUSTER_POWER_MODE
+ case ClusterAttr_PowerMode:
+ if ((count == 1) && (*buf == '2'))
+ power_mode = 2;
+ else if ((count == 1) && *buf == '1')
+ power_mode = 1;
+ else {
+ PRINT_CLUSTER(("cluster/power_mode: '%*.*s' invalid, "
+ "must be 2 or 1\n", count, count, buf));
+ ret = -EINVAL;
+ break;
+ }
+ PRINT_CLUSTER(("cluster/power_mode -> %d\n", power_mode));
+ break;
+#endif
+
+#if DEBUG_CLUSTER_SWITCH
+ case ClusterAttr_Debug:
+ if ((count == 1) && (*buf == '0'))
+ tegra_cluster_debug = 0;
+ else if ((count == 1) && (*buf == '1'))
+ tegra_cluster_debug = 1;
+ else {
+ PRINT_CLUSTER(("cluster/debug: '%*.*s' invalid, "
+ "must be 0 or 1\n", count, count, buf));
+ ret = -EINVAL;
+ break;
+ }
+ PRINT_CLUSTER(("cluster/debug -> %d\n",tegra_cluster_debug));
+ break;
+#endif
+
+ default:
+ ret = -ENOENT;
+ break;
+ }
+
+ spin_unlock(&cluster_lock);
+
+ if (new_parent) {
+ e = clk_set_parent(cpu_clk, new_parent);
+ if (e) {
+ PRINT_CLUSTER(("cluster/active: request failed (%d)\n",
+ e));
+ ret = e;
+ }
+ }
+fail:
+ TRACE_CLUSTER(("-sysfscluster_store: %d\n", count));
+ return ret;
+}
+
+#define CREATE_FILE(x) \
+ do { \
+ e = sysfs_create_file(cluster_kobj, &cluster_##x##_attr.attr); \
+ if (e) { \
+ TRACE_CLUSTER(("cluster/" __stringify(x) \
+ ": sysfs_create_file failed!\n")); \
+ goto fail; \
+ } \
+ } while (0)
+
+static int __init sysfscluster_init(void)
+{
+ int e;
+
+ TRACE_CLUSTER(("+sysfscluster_init\n"));
+
+ spin_lock_init(&cluster_lock);
+ cluster_kobj = kobject_create_and_add("cluster", kernel_kobj);
+
+ CREATE_FILE(active);
+ CREATE_FILE(immediate);
+ CREATE_FILE(force);
+ CREATE_FILE(wake_ms);
+#if defined(CONFIG_PM_SLEEP) && SYSFS_CLUSTER_POWER_MODE
+ CREATE_FILE(powermode);
+#endif
+#if DEBUG_CLUSTER_SWITCH
+ CREATE_FILE(debug);
+#endif
+
+ spin_lock(&cluster_lock);
+ if (is_lp_cluster())
+ flags |= TEGRA_POWER_CLUSTER_LP;
+ else
+ flags |= TEGRA_POWER_CLUSTER_G;
+ spin_unlock(&cluster_lock);
+
+fail:
+ TRACE_CLUSTER(("-sysfscluster_init\n"));
+ return e;
+}
+
+#define REMOVE_FILE(x) \
+ sysfs_remove_file(cluster_kobj, &cluster_##x##_attr.attr)
+
+static void __exit sysfscluster_exit(void)
+{
+ TRACE_CLUSTER(("+sysfscluster_exit\n"));
+#if DEBUG_CLUSTER_SWITCH
+ REMOVE_FILE(debug);
+#endif
+#if defined(CONFIG_PM_SLEEP) && SYSFS_CLUSTER_POWER_MODE
+ REMOVE_FILE(powermode);
+#endif
+ REMOVE_FILE(wake_ms);
+ REMOVE_FILE(force);
+ REMOVE_FILE(immediate);
+ REMOVE_FILE(active);
+ kobject_del(cluster_kobj);
+ TRACE_CLUSTER(("-sysfscluster_exit\n"));
+}
+
+module_init(sysfscluster_init);
+module_exit(sysfscluster_exit);
+MODULE_LICENSE("GPL");
diff --git a/arch/arm/mach-tegra/sysfs-dcc.c b/arch/arm/mach-tegra/sysfs-dcc.c
new file mode 100644
index 000000000000..a4dc9a721354
--- /dev/null
+++ b/arch/arm/mach-tegra/sysfs-dcc.c
@@ -0,0 +1,249 @@
+/*
+ * Copyright (c) 2010-2011 NVIDIA Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of the NVIDIA Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/sysfs.h>
+#include <linux/workqueue.h>
+#include <linux/kobject.h>
+#include <linux/hrtimer.h>
+#include <linux/slab.h>
+
+#define DCC_TIMEOUT_US 100000 /* Delay time for DCC timeout (in uS) */
+#define CP14_DSCR_WDTRFULL 0x20000000 /* Write Data Transfer Register Full */
+#define SYSFS_DCC_DEBUG_PRINTS 0 /* Set non-zero to enable debug prints */
+
+#if SYSFS_DCC_DEBUG_PRINTS
+#define DEBUG_DCC(x) printk x
+#else
+#define DEBUG_DCC(x)
+#endif
+
+static int DebuggerConnected = 0; /* -1=not connected, 0=unknown, 1=connected */
+static struct kobject *nvdcc_kobj;
+static spinlock_t dcc_lock;
+static struct list_head dcc_list;
+
+static ssize_t sysfsdcc_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf);
+
+static ssize_t sysfsdcc_store(struct kobject *kobj,
+ struct kobj_attribute *attr, const char *buf, size_t count);
+
+
+static struct kobj_attribute nvdcc_attr =
+ __ATTR(dcc0, 0222, sysfsdcc_show, sysfsdcc_store);
+
+static int write_to_dcc(u32 c)
+{
+ volatile u32 dscr;
+
+ /* Have we already determined that there is no debugger connected? */
+ if (DebuggerConnected < 0)
+ {
+ return -ENXIO;
+ }
+
+ /* Read the DSCR. */
+ asm volatile ("mrc p14, 0, %0, c0, c1, 0" : "=r" (dscr) : : "cc");
+
+ /* If DSCR Bit 29 (wDTRFull) is set there is data in the write
+ * register. If it stays there for than the DCC_TIMEOUT_US
+ * period, ignore this write and disable further DCC accesses. */
+ if (dscr & CP14_DSCR_WDTRFULL)
+ {
+ ktime_t end = ktime_add_ns(ktime_get(), DCC_TIMEOUT_US * 1000);
+ ktime_t now;
+
+ for (;;)
+ {
+ /* Re-read the DSCR. */
+ asm volatile ("mrc p14, 0, %0, c0, c1, 0" : "=r" (dscr) : : "cc");
+
+ /* Previous data still there? */
+ if (dscr & CP14_DSCR_WDTRFULL)
+ {
+ now = ktime_get();
+
+ if (ktime_to_ns(now) >= ktime_to_ns(end))
+ {
+ goto fail;
+ }
+ }
+ else
+ {
+ if (DebuggerConnected == 0) {
+ /* Debugger connected */
+ spin_lock(&dcc_lock);
+ DebuggerConnected = 1;
+ spin_unlock(&dcc_lock);
+ }
+ break;
+ }
+ }
+ }
+
+ // Write the data into the DCC output register
+ asm volatile ("mcr p14, 0, %0, c0, c5, 0" : : "r" (c) : "cc");
+ return 0;
+
+fail:
+ /* No debugged connected -- disable DCC */
+ spin_lock(&dcc_lock);
+ DebuggerConnected = -1;
+ spin_unlock(&dcc_lock);
+ return -ENXIO;
+}
+
+
+struct tegra_dcc_req {
+ struct list_head node;
+
+ const char *pBuf;
+ unsigned int size;
+};
+
+struct dcc_action {
+ struct tegra_dcc_req req;
+ struct work_struct work;
+ struct list_head node;
+};
+
+
+static void dcc_writer(struct work_struct *work)
+{
+ struct dcc_action *action = container_of(work, struct dcc_action, work);
+ const char *p;
+
+ DEBUG_DCC(("+dcc_writer\n"));
+
+ spin_lock(&dcc_lock);
+ list_del(&action->req.node);
+ spin_unlock(&dcc_lock);
+
+ p = action->req.pBuf;
+ if (p)
+ while ((p < &(action->req.pBuf[action->req.size])) && (*p))
+ if (write_to_dcc(*p++))
+ break;
+
+ kfree(action->req.pBuf);
+ kfree(action);
+
+ DEBUG_DCC(("-dcc_writer\n"));
+}
+
+static ssize_t sysfsdcc_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ DEBUG_DCC(("!sysfsdcc_show\n"));
+ return -EACCES;
+}
+
+static ssize_t sysfsdcc_store(struct kobject *kobj,
+ struct kobj_attribute *attr, const char *buf, size_t count)
+{
+ struct dcc_action *action;
+ char *pBuf;
+ ssize_t ret = count;
+
+ DEBUG_DCC(("+sysfsdcc_store: %p, %d\n", buf, count));
+
+ if (!buf || !count) {
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ pBuf = kmalloc(count+1, GFP_KERNEL);
+ if (!pBuf) {
+ pr_debug("%s: insufficient memory\n", __func__);
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ action = kzalloc(sizeof(*action), GFP_KERNEL);
+ if (!action) {
+ kfree(pBuf);
+ pr_debug("%s: insufficient memory\n", __func__);
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ strncpy(pBuf, buf, count);
+ pBuf[count] = '\0';
+ action->req.pBuf = pBuf;
+ action->req.size = count;
+
+ INIT_WORK(&action->work, dcc_writer);
+
+ spin_lock(&dcc_lock);
+ list_add_tail(&action->req.node, &dcc_list);
+ spin_unlock(&dcc_lock);
+
+ /* DCC writes can only be performed from CPU0 */
+ schedule_work_on(0, &action->work);
+
+fail:
+ DEBUG_DCC(("-sysfsdcc_store: %d\n", count));
+ return ret;
+}
+
+static int __init sysfsdcc_init(void)
+{
+ spin_lock_init(&dcc_lock);
+ INIT_LIST_HEAD(&dcc_list);
+
+ DEBUG_DCC(("+sysfsdcc_init\n"));
+ nvdcc_kobj = kobject_create_and_add("dcc", kernel_kobj);
+
+ if (sysfs_create_file(nvdcc_kobj, &nvdcc_attr.attr))
+ {
+ DEBUG_DCC(("DCC: sysfs_create_file failed!\n"));
+ return -ENXIO;
+ }
+
+ DEBUG_DCC(("-sysfsdcc_init\n"));
+ return 0;
+}
+
+static void __exit sysfsdcc_exit(void)
+{
+ DEBUG_DCC(("+sysfsdcc_exit\n"));
+ sysfs_remove_file(nvdcc_kobj, &nvdcc_attr.attr);
+ kobject_del(nvdcc_kobj);
+ DEBUG_DCC(("-sysfsdcc_exit\n"));
+}
+
+module_init(sysfsdcc_init);
+module_exit(sysfsdcc_exit);
+MODULE_LICENSE("GPL");
diff --git a/arch/arm/mach-tegra/tegra2_clocks.c b/arch/arm/mach-tegra/tegra2_clocks.c
index 0fe9b3ee2947..4fd892cc5dc6 100644
--- a/arch/arm/mach-tegra/tegra2_clocks.c
+++ b/arch/arm/mach-tegra/tegra2_clocks.c
@@ -6,6 +6,8 @@
* Author:
* Colin Cross <ccross@google.com>
*
+ * Copyright (C) 2010-2011 NVIDIA Corporation
+ *
* 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.
@@ -25,13 +27,16 @@
#include <linux/io.h>
#include <linux/clkdev.h>
#include <linux/clk.h>
+#include <linux/syscore_ops.h>
+#include <linux/cpufreq.h>
#include <mach/iomap.h>
-#include <mach/suspend.h>
+#include <mach/pinmux.h>
#include "clock.h"
#include "fuse.h"
#include "tegra2_emc.h"
+#include "tegra2_statmon.h"
#define RST_DEVICES 0x004
#define RST_DEVICES_SET 0x300
@@ -149,8 +154,17 @@
#define PMC_BLINK_TIMER_DATA_OFF_SHIFT 16
#define PMC_BLINK_TIMER_DATA_OFF_MASK 0xffff
+#define AP25_EMC_BRIDGE_RATE 380000000
+#define AP25_EMC_INTERMEDIATE_RATE 760000000
+#define AP25_EMC_SCALING_STEP 600000000
+
static void __iomem *reg_clk_base = IO_ADDRESS(TEGRA_CLK_RESET_BASE);
static void __iomem *reg_pmc_base = IO_ADDRESS(TEGRA_PMC_BASE);
+static void __iomem *misc_gp_hidrev_base = IO_ADDRESS(TEGRA_APB_MISC_BASE);
+
+#define MISC_GP_HIDREV 0x804
+
+static int tegra2_clk_shared_bus_update(struct clk *bus);
/*
* Some clocks share a register with other clocks. Any clock op that
@@ -173,6 +187,8 @@ static int tegra_periph_clk_enable_refcount[3 * 32];
__raw_writel(value, (u32)reg_pmc_base + (reg))
#define pmc_readl(reg) \
__raw_readl((u32)reg_pmc_base + (reg))
+#define chipid_readl() \
+ __raw_readl((u32)misc_gp_hidrev_base + MISC_GP_HIDREV)
unsigned long clk_measure_input_freq(void)
{
@@ -221,12 +237,17 @@ static int clk_div16_get_divider(unsigned long parent_rate, unsigned long rate)
if (divider_u16 - 1 < 0)
return 0;
- if (divider_u16 - 1 > 255)
+ if (divider_u16 - 1 > 0xFFFF)
return -EINVAL;
return divider_u16 - 1;
}
+static inline int clk_set_div(struct clk *c, int n)
+{
+ return clk_set_rate(c, (clk_get_rate(c->parent) + n-1) / n);
+}
+
/* clk_m functions */
static unsigned long tegra2_clk_m_autodetect_rate(struct clk *c)
{
@@ -278,18 +299,6 @@ static struct clk_ops tegra_clk_m_ops = {
.disable = tegra2_clk_m_disable,
};
-void tegra2_periph_reset_assert(struct clk *c)
-{
- BUG_ON(!c->ops->reset);
- c->ops->reset(c, true);
-}
-
-void tegra2_periph_reset_deassert(struct clk *c)
-{
- BUG_ON(!c->ops->reset);
- c->ops->reset(c, false);
-}
-
/* super clock functions */
/* "super clocks" on tegra have two-stage muxes and a clock skipping
* super divider. We will ignore the clock skipping divider, since we
@@ -382,6 +391,29 @@ static struct clk_ops tegra_super_ops = {
.set_rate = tegra2_super_clk_set_rate,
};
+static int tegra2_twd_clk_set_rate(struct clk *c, unsigned long rate)
+{
+ /* The input value 'rate' is the clock rate of the CPU complex. */
+ c->rate = (rate * c->mul) / c->div;
+ return 0;
+}
+
+static struct clk_ops tegra2_twd_ops = {
+ .set_rate = tegra2_twd_clk_set_rate,
+};
+
+static struct clk tegra2_clk_twd = {
+ /* NOTE: The twd clock must have *NO* parent. It's rate is directly
+ updated by tegra3_cpu_cmplx_clk_set_rate() because the
+ frequency change notifer for the twd is called in an
+ atomic context which cannot take a mutex. */
+ .name = "twd",
+ .ops = &tegra2_twd_ops,
+ .max_rate = 1000000000, /* Same as tegra_clk_virtual_cpu.max_rate */
+ .mul = 1,
+ .div = 4,
+};
+
/* virtual cpu clock functions */
/* some clocks can not be stopped (cpu, memory bus) while the SoC is running.
To change the frequency of these clocks, the parent pll may need to be
@@ -436,6 +468,12 @@ static int tegra2_cpu_clk_set_rate(struct clk *c, unsigned long rate)
goto out;
}
+ /* We can't parent the twd to directly to the CPU complex because
+ the TWD frequency update notifier is called in an atomic context
+ and the CPU frequency update requires a mutex. Update the twd
+ clock rate with the new CPU complex rate. */
+ clk_set_rate(&tegra2_clk_twd, clk_get_rate_locked(c));
+
out:
clk_disable(c->u.cpu.main);
return ret;
@@ -448,6 +486,55 @@ static struct clk_ops tegra_cpu_ops = {
.set_rate = tegra2_cpu_clk_set_rate,
};
+static void tegra2_virtual_sclk_init(struct clk *c)
+{
+ c->max_rate = c->parent->max_rate;
+ c->min_rate = c->parent->min_rate;
+}
+
+static long tegra2_virtual_sclk_round_rate(struct clk *c, unsigned long rate)
+{
+ long new_rate = rate;
+ return new_rate;
+}
+
+static int tegra2_virtual_sclk_set_rate(struct clk *c, unsigned long rate)
+{
+ int ret;
+
+ if (rate >= c->u.system.pclk->min_rate * 2) {
+ ret = clk_set_div(c->u.system.pclk, 2);
+ if (ret) {
+ pr_err("Failed to set 1 : 2 pclk divider\n");
+ return ret;
+ }
+ }
+
+ ret = clk_set_rate(c->parent, rate);
+ if (ret) {
+ pr_err("Failed to set sclk source %s to %lu\n",
+ c->parent->name, rate);
+ return ret;
+ }
+
+ if (rate < c->u.system.pclk->min_rate * 2) {
+ ret = clk_set_div(c->u.system.pclk, 1);
+ if (ret) {
+ pr_err("Failed to set 1 : 1 pclk divider\n");
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static struct clk_ops tegra_virtual_sclk_ops = {
+ .init = tegra2_virtual_sclk_init,
+ .set_rate = tegra2_virtual_sclk_set_rate,
+ .round_rate = tegra2_virtual_sclk_round_rate,
+ .shared_bus_update = tegra2_clk_shared_bus_update,
+};
+
/* virtual cop clock functions. Used to acquire the fake 'cop' clock to
* reset the COP block (i.e. AVP) */
static void tegra2_cop_clk_reset(struct clk *c, bool assert)
@@ -513,7 +600,7 @@ static int tegra2_bus_clk_set_rate(struct clk *c, unsigned long rate)
val = clk_readl(c->reg);
for (i = 1; i <= 4; i++) {
- if (rate == parent_rate / i) {
+ if (rate >= parent_rate / i) {
val &= ~(BUS_CLK_DIV_MASK << c->reg_shift);
val |= (i - 1) << c->reg_shift;
clk_writel(val, c->reg);
@@ -659,6 +746,12 @@ static int tegra2_pll_clk_enable(struct clk *c)
val |= PLL_BASE_ENABLE;
clk_writel(val, c->reg + PLL_BASE);
+ if (c->flags & PLLD) {
+ val = clk_readl(c->reg + PLL_MISC(c) + PLL_BASE);
+ val |= PLLD_MISC_CLKENABLE;
+ clk_writel(val, c->reg + PLL_MISC(c) + PLL_BASE);
+ }
+
tegra2_pll_clk_wait_for_lock(c);
return 0;
@@ -672,6 +765,12 @@ static void tegra2_pll_clk_disable(struct clk *c)
val = clk_readl(c->reg);
val &= ~(PLL_BASE_BYPASS | PLL_BASE_ENABLE);
clk_writel(val, c->reg);
+
+ if (c->flags & PLLD) {
+ val = clk_readl(c->reg + PLL_MISC(c) + PLL_BASE);
+ val &= ~PLLD_MISC_CLKENABLE;
+ clk_writel(val, c->reg + PLL_MISC(c) + PLL_BASE);
+ }
}
static int tegra2_pll_clk_set_rate(struct clk *c, unsigned long rate)
@@ -695,13 +794,25 @@ static int tegra2_pll_clk_set_rate(struct clk *c, unsigned long rate)
PLL_BASE_DIVM_MASK);
val |= (sel->m << PLL_BASE_DIVM_SHIFT) |
(sel->n << PLL_BASE_DIVN_SHIFT);
- BUG_ON(sel->p < 1 || sel->p > 2);
+ BUG_ON(sel->p < 1 || sel->p > 128);
if (c->flags & PLLU) {
if (sel->p == 1)
val |= PLLU_BASE_POST_DIV;
} else {
if (sel->p == 2)
val |= 1 << PLL_BASE_DIVP_SHIFT;
+ else if (sel->p == 4)
+ val |= 2 << PLL_BASE_DIVP_SHIFT;
+ else if (sel->p == 8)
+ val |= 3 << PLL_BASE_DIVP_SHIFT;
+ else if (sel->p == 16)
+ val |= 4 << PLL_BASE_DIVP_SHIFT;
+ else if (sel->p == 32)
+ val |= 5 << PLL_BASE_DIVP_SHIFT;
+ else if (sel->p == 64)
+ val |= 6 << PLL_BASE_DIVP_SHIFT;
+ else if (sel->p == 128)
+ val |= 7 << PLL_BASE_DIVP_SHIFT;
}
clk_writel(val, c->reg + PLL_BASE);
@@ -728,21 +839,6 @@ static struct clk_ops tegra_pll_ops = {
.set_rate = tegra2_pll_clk_set_rate,
};
-static void tegra2_pllx_clk_init(struct clk *c)
-{
- tegra2_pll_clk_init(c);
-
- if (tegra_sku_id() == 7)
- c->max_rate = 750000000;
-}
-
-static struct clk_ops tegra_pllx_ops = {
- .init = tegra2_pllx_clk_init,
- .enable = tegra2_pll_clk_enable,
- .disable = tegra2_pll_clk_disable,
- .set_rate = tegra2_pll_clk_set_rate,
-};
-
static int tegra2_plle_clk_enable(struct clk *c)
{
u32 val;
@@ -999,6 +1095,7 @@ out:
static void tegra2_periph_clk_disable(struct clk *c)
{
unsigned long flags;
+ unsigned long val;
pr_debug("%s on clock %s\n", __func__, c->name);
@@ -1010,9 +1107,16 @@ static void tegra2_periph_clk_disable(struct clk *c)
if (c->refcnt)
tegra_periph_clk_enable_refcount[c->u.periph.clk_num]--;
- if (tegra_periph_clk_enable_refcount[c->u.periph.clk_num] == 0)
+ if (tegra_periph_clk_enable_refcount[c->u.periph.clk_num] == 0) {
+ /* If peripheral is in the APB bus then read the APB bus to
+ * flush the write operation in apb bus. This will avoid the
+ * peripheral access after disabling clock*/
+ if (c->flags & PERIPH_ON_APB)
+ val = chipid_readl();
+
clk_writel(PERIPH_CLK_TO_ENB_BIT(c),
CLK_OUT_ENB_CLR + PERIPH_CLK_TO_ENB_SET_REG(c));
+ }
spin_unlock_irqrestore(&clock_register_lock, flags);
}
@@ -1020,15 +1124,23 @@ static void tegra2_periph_clk_disable(struct clk *c)
static void tegra2_periph_clk_reset(struct clk *c, bool assert)
{
unsigned long base = assert ? RST_DEVICES_SET : RST_DEVICES_CLR;
+ unsigned long val;
pr_debug("%s %s on clock %s\n", __func__,
assert ? "assert" : "deassert", c->name);
BUG_ON(!c->u.periph.clk_num);
- if (!(c->flags & PERIPH_NO_RESET))
+ if (!(c->flags & PERIPH_NO_RESET)) {
+ /* If peripheral is in the APB bus then read the APB bus to
+ * flush the write operation in apb bus. This will avoid the
+ * peripheral access after disabling clock*/
+ if (c->flags & PERIPH_ON_APB)
+ val = chipid_readl();
+
clk_writel(PERIPH_CLK_TO_ENB_BIT(c),
base + PERIPH_CLK_TO_ENB_SET_REG(c));
+ }
}
static int tegra2_periph_clk_set_parent(struct clk *c, struct clk *p)
@@ -1039,8 +1151,8 @@ static int tegra2_periph_clk_set_parent(struct clk *c, struct clk *p)
for (sel = c->inputs; sel->input != NULL; sel++) {
if (sel->input == p) {
val = clk_readl(c->reg);
- val &= ~PERIPH_CLK_SOURCE_MASK;
- val |= (sel->value) << PERIPH_CLK_SOURCE_SHIFT;
+ val &= ~((c->reg_shift >> 8) << (c->reg_shift & 0xFF));
+ val |= (sel->value) << (c->reg_shift & 0xFF);
if (c->refcnt)
clk_enable(p);
@@ -1156,14 +1268,35 @@ static long tegra2_emc_clk_round_rate(struct clk *c, unsigned long rate)
if (new_rate < 0)
return c->max_rate;
- BUG_ON(new_rate != tegra2_periph_clk_round_rate(c, new_rate));
-
return new_rate;
}
static int tegra2_emc_clk_set_rate(struct clk *c, unsigned long rate)
{
int ret;
+ int divider;
+ struct clk *p = NULL;
+ unsigned long inp_rate;
+ unsigned long new_rate;
+ const struct clk_mux_sel *sel;
+
+ for (sel = c->inputs; sel->input != NULL; sel++) {
+ inp_rate = clk_get_rate(sel->input);
+
+ divider = clk_div71_get_divider(inp_rate, rate);
+ if (divider < 0)
+ return divider;
+
+ new_rate = DIV_ROUND_UP(inp_rate * 2, divider + 2);
+ if ((abs(rate - new_rate)) < 2000) {
+ p = sel->input;
+ break;
+ }
+ }
+
+ BUG_ON(!p);
+ BUG_ON(divider & 0x1);
+
/*
* The Tegra2 memory controller has an interlock with the clock
* block that allows memory shadowed registers to be updated,
@@ -1174,6 +1307,13 @@ static int tegra2_emc_clk_set_rate(struct clk *c, unsigned long rate)
if (ret < 0)
return ret;
+ if (c->parent != p) {
+ BUG_ON(divider != 0);
+ ret = clk_set_parent_locked(c, p);
+ udelay(1);
+ return ret;
+ }
+
ret = tegra2_periph_clk_set_rate(c, rate);
udelay(1);
@@ -1188,6 +1328,7 @@ static struct clk_ops tegra_emc_clk_ops = {
.set_rate = &tegra2_emc_clk_set_rate,
.round_rate = &tegra2_emc_clk_round_rate,
.reset = &tegra2_periph_clk_reset,
+ .shared_bus_update = &tegra2_clk_shared_bus_update,
};
/* Clock doubler ops */
@@ -1280,10 +1421,38 @@ static struct clk_ops tegra_audio_sync_clk_ops = {
.set_parent = tegra2_audio_sync_clk_set_parent,
};
-/* cdev1 and cdev2 (dap_mclk1 and dap_mclk2) ops */
+/* call this function after pinmux configuration */
+static void tegra2_cdev_clk_set_parent(struct clk *c)
+{
+ const struct clk_mux_sel *mux = 0;
+ const struct clk_mux_sel *sel;
+ enum tegra_pingroup pg = TEGRA_PINGROUP_CDEV1;
+ int val;
+
+ /* Get pinmux setting for cdev1 and cdev2 from APB_MISC register */
+ if (!strcmp(c->name, "clk_dev2"))
+ pg = TEGRA_PINGROUP_CDEV2;
+
+ val = tegra_pinmux_get_func(pg);
+ for (sel = c->inputs; sel->input != NULL; sel++) {
+ if (val == sel->value)
+ mux = sel;
+ }
+ BUG_ON(!mux);
+
+ c->parent = mux->input;
+}
+/* cdev1 and cdev2 (dap_mclk1 and dap_mclk2) ops */
static void tegra2_cdev_clk_init(struct clk *c)
{
+ const struct clk_mux_sel *sel;
+
+ /* Find max rate from inputs */
+ for (sel = c->inputs; sel->input != NULL; sel++) {
+ c->max_rate = max(sel->input->max_rate, c->max_rate);
+ }
+
/* We could un-tristate the cdev1 or cdev2 pingroup here; this is
* currently done in the pinmux code. */
c->state = ON;
@@ -1299,6 +1468,12 @@ static int tegra2_cdev_clk_enable(struct clk *c)
{
BUG_ON(!c->u.periph.clk_num);
+ if (!c->parent) {
+ /* Set parent from inputs */
+ tegra2_cdev_clk_set_parent(c);
+ clk_enable(c->parent);
+ }
+
clk_writel(PERIPH_CLK_TO_ENB_BIT(c),
CLK_OUT_ENB_SET + PERIPH_CLK_TO_ENB_SET_REG(c));
return 0;
@@ -1326,18 +1501,41 @@ static struct clk_ops tegra_cdev_clk_ops = {
* enabled shared_bus_user clock, with a minimum value set by the
* shared bus.
*/
-static int tegra_clk_shared_bus_update(struct clk *bus)
+static int tegra2_clk_shared_bus_update(struct clk *bus)
{
struct clk *c;
+ unsigned long old_rate;
unsigned long rate = bus->min_rate;
+ int sku_id = tegra_sku_id();
- list_for_each_entry(c, &bus->shared_bus_list, u.shared_bus_user.node)
+ list_for_each_entry(c, &bus->shared_bus_list,
+ u.shared_bus_user.node) {
if (c->u.shared_bus_user.enabled)
rate = max(c->u.shared_bus_user.rate, rate);
+ }
+
+ old_rate = clk_get_rate_locked(bus);
- if (rate == clk_get_rate_locked(bus))
+ if (rate == old_rate)
return 0;
+ /* WAR: For AP25 EMC scaling */
+ if ((sku_id == 0x17) && (bus->flags & PERIPH_EMC_ENB)) {
+ if (old_rate == AP25_EMC_SCALING_STEP &&
+ rate != AP25_EMC_INTERMEDIATE_RATE)
+ clk_set_rate_locked(bus, AP25_EMC_INTERMEDIATE_RATE);
+
+ if (((old_rate > AP25_EMC_BRIDGE_RATE) &&
+ (rate < AP25_EMC_BRIDGE_RATE)) ||
+ ((old_rate < AP25_EMC_BRIDGE_RATE) &&
+ (rate > AP25_EMC_BRIDGE_RATE)))
+ clk_set_rate_locked(bus, AP25_EMC_BRIDGE_RATE);
+
+ if (rate == AP25_EMC_SCALING_STEP &&
+ old_rate != AP25_EMC_INTERMEDIATE_RATE)
+ clk_set_rate_locked(bus, AP25_EMC_INTERMEDIATE_RATE);
+ }
+
return clk_set_rate_locked(bus, rate);
};
@@ -1350,17 +1548,16 @@ static void tegra_clk_shared_bus_init(struct clk *c)
c->state = OFF;
c->set = true;
- spin_lock_irqsave(&c->parent->spinlock, flags);
+ clk_lock_save(c->parent, &flags);
list_add_tail(&c->u.shared_bus_user.node,
&c->parent->shared_bus_list);
- spin_unlock_irqrestore(&c->parent->spinlock, flags);
+ clk_unlock_restore(c->parent, &flags);
}
static int tegra_clk_shared_bus_set_rate(struct clk *c, unsigned long rate)
{
- unsigned long flags;
int ret;
long new_rate = rate;
@@ -1368,13 +1565,9 @@ static int tegra_clk_shared_bus_set_rate(struct clk *c, unsigned long rate)
if (new_rate < 0)
return new_rate;
- spin_lock_irqsave(&c->parent->spinlock, flags);
-
c->u.shared_bus_user.rate = new_rate;
ret = tegra_clk_shared_bus_update(c->parent);
- spin_unlock_irqrestore(&c->parent->spinlock, flags);
-
return ret;
}
@@ -1385,31 +1578,25 @@ static long tegra_clk_shared_bus_round_rate(struct clk *c, unsigned long rate)
static int tegra_clk_shared_bus_enable(struct clk *c)
{
- unsigned long flags;
int ret;
- spin_lock_irqsave(&c->parent->spinlock, flags);
-
c->u.shared_bus_user.enabled = true;
ret = tegra_clk_shared_bus_update(c->parent);
-
- spin_unlock_irqrestore(&c->parent->spinlock, flags);
+ if (strcmp(c->name, "avp.sclk") == 0)
+ tegra2_statmon_start();
return ret;
}
static void tegra_clk_shared_bus_disable(struct clk *c)
{
- unsigned long flags;
int ret;
- spin_lock_irqsave(&c->parent->spinlock, flags);
-
+ if (strcmp(c->name, "avp.sclk") == 0)
+ tegra2_statmon_stop();
c->u.shared_bus_user.enabled = false;
ret = tegra_clk_shared_bus_update(c->parent);
WARN_ON_ONCE(ret);
-
- spin_unlock_irqrestore(&c->parent->spinlock, flags);
}
static struct clk_ops tegra_clk_shared_bus_ops = {
@@ -1473,6 +1660,14 @@ static struct clk tegra_clk_m = {
};
static struct clk_pll_freq_table tegra_pll_c_freq_table[] = {
+ { 12000000, 522000000, 348, 8, 1, 8},
+ { 13000000, 522000000, 522, 13, 1, 8},
+ { 19200000, 522000000, 435, 16, 1, 8},
+ { 26000000, 522000000, 522, 26, 1, 8},
+ { 12000000, 598000000, 598, 12, 1, 8},
+ { 13000000, 598000000, 598, 13, 1, 8},
+ { 19200000, 598000000, 375, 12, 1, 6},
+ { 26000000, 598000000, 598, 26, 1, 8},
{ 0, 0, 0, 0, 0, 0 },
};
@@ -1659,6 +1854,11 @@ static struct clk_pll_freq_table tegra_pll_d_freq_table[] = {
{ 19200000, 216000000, 135, 12, 1, 3},
{ 26000000, 216000000, 216, 26, 1, 4},
+ { 12000000, 5000000, 10, 24, 1, 4},
+ { 12000000, 10000000, 10, 12, 1, 4},
+ { 12000000, 161500000, 323, 24, 1, 4},
+ { 12000000, 162000000, 162, 12, 1, 4},
+
{ 12000000, 594000000, 594, 12, 1, 8},
{ 13000000, 594000000, 594, 13, 1, 8},
{ 19200000, 594000000, 495, 16, 1, 8},
@@ -1669,6 +1869,11 @@ static struct clk_pll_freq_table tegra_pll_d_freq_table[] = {
{ 19200000, 1000000000, 625, 12, 1, 8},
{ 26000000, 1000000000, 1000, 26, 1, 12},
+ { 12000000, 504000000, 504, 12, 1, 8},
+ { 13000000, 504000000, 504, 13, 1, 8},
+ { 19200000, 504000000, 420, 16, 1, 8},
+ { 26000000, 504000000, 504, 26, 1, 8},
+
{ 0, 0, 0, 0, 0, 0 },
};
@@ -1727,6 +1932,12 @@ static struct clk tegra_pll_u = {
};
static struct clk_pll_freq_table tegra_pll_x_freq_table[] = {
+ /* 1.2 GHz */
+ { 12000000, 1200000000, 600, 6, 1, 12},
+ { 13000000, 1200000000, 923, 10, 1, 12},
+ { 19200000, 1200000000, 750, 12, 1, 8},
+ { 26000000, 1200000000, 600, 13, 1, 12},
+
/* 1 GHz */
{ 12000000, 1000000000, 1000, 12, 1, 12},
{ 13000000, 1000000000, 1000, 13, 1, 12},
@@ -1751,6 +1962,12 @@ static struct clk_pll_freq_table tegra_pll_x_freq_table[] = {
{ 19200000, 760000000, 950, 24, 1, 8},
{ 26000000, 760000000, 760, 26, 1, 12},
+ /* 750 MHz */
+ { 12000000, 750000000, 750, 12, 1, 12},
+ { 13000000, 750000000, 750, 13, 1, 12},
+ { 19200000, 750000000, 625, 16, 1, 8},
+ { 26000000, 750000000, 750, 26, 1, 12},
+
/* 608 MHz */
{ 12000000, 608000000, 608, 12, 1, 12},
{ 13000000, 608000000, 608, 13, 1, 12},
@@ -1775,7 +1992,7 @@ static struct clk_pll_freq_table tegra_pll_x_freq_table[] = {
static struct clk tegra_pll_x = {
.name = "pll_x",
.flags = PLL_HAS_CPCON | PLL_ALT_MISC_REG,
- .ops = &tegra_pllx_ops,
+ .ops = &tegra_pll_ops,
.reg = 0xe0,
.parent = &tegra_clk_m,
.max_rate = 1000000000,
@@ -1823,28 +2040,6 @@ static struct clk tegra_clk_d = {
},
};
-/* dap_mclk1, belongs to the cdev1 pingroup. */
-static struct clk tegra_clk_cdev1 = {
- .name = "cdev1",
- .ops = &tegra_cdev_clk_ops,
- .rate = 26000000,
- .max_rate = 26000000,
- .u.periph = {
- .clk_num = 94,
- },
-};
-
-/* dap_mclk2, belongs to the cdev2 pingroup. */
-static struct clk tegra_clk_cdev2 = {
- .name = "cdev2",
- .ops = &tegra_cdev_clk_ops,
- .rate = 26000000,
- .max_rate = 26000000,
- .u.periph = {
- .clk_num = 93,
- },
-};
-
/* initialized before peripheral clocks */
static struct clk_mux_sel mux_audio_sync_clk[8+1];
static const struct audio_sources {
@@ -1955,7 +2150,7 @@ static struct clk tegra_clk_sclk = {
.reg = 0x28,
.ops = &tegra_super_ops,
.max_rate = 240000000,
- .min_rate = 120000000,
+ .min_rate = 40000000,
};
static struct clk tegra_clk_virtual_cpu = {
@@ -1984,6 +2179,7 @@ static struct clk tegra_clk_hclk = {
.reg_shift = 4,
.ops = &tegra_bus_ops,
.max_rate = 240000000,
+ .min_rate = 36000000,
};
static struct clk tegra_clk_pclk = {
@@ -1994,6 +2190,16 @@ static struct clk tegra_clk_pclk = {
.reg_shift = 0,
.ops = &tegra_bus_ops,
.max_rate = 120000000,
+ .min_rate = 36000000,
+};
+
+static struct clk tegra_clk_virtual_sclk = {
+ .name = "virt_sclk",
+ .parent = &tegra_clk_sclk,
+ .ops = &tegra_virtual_sclk_ops,
+ .u.system = {
+ .pclk = &tegra_clk_pclk,
+ },
};
static struct clk tegra_clk_blink = {
@@ -2003,6 +2209,43 @@ static struct clk tegra_clk_blink = {
.ops = &tegra_blink_clk_ops,
.max_rate = 32768,
};
+static struct clk_mux_sel mux_dev1_clk[] = {
+ { .input = &tegra_clk_m, .value = 0 },
+ { .input = &tegra_pll_a_out0, .value = 1 },
+ { .input = &tegra_pll_m_out1, .value = 2 },
+ { .input = &tegra_clk_audio, .value = 3 },
+ { 0, 0 }
+};
+
+static struct clk_mux_sel mux_dev2_clk[] = {
+ { .input = &tegra_clk_m, .value = 0 },
+ { .input = &tegra_clk_hclk, .value = 1 },
+ { .input = &tegra_clk_pclk, .value = 2 },
+ { .input = &tegra_pll_p_out4, .value = 3 },
+ { 0, 0 }
+};
+
+/* dap_mclk1, belongs to the cdev1 pingroup. */
+static struct clk tegra_clk_cdev1 = {
+ .name = "cdev1",
+ .ops = &tegra_cdev_clk_ops,
+ .inputs = mux_dev1_clk,
+ .u.periph = {
+ .clk_num = 94,
+ },
+ .flags = MUX,
+};
+
+/* dap_mclk2, belongs to the cdev2 pingroup. */
+static struct clk tegra_clk_cdev2 = {
+ .name = "cdev2",
+ .ops = &tegra_cdev_clk_ops,
+ .inputs = mux_dev2_clk,
+ .u.periph = {
+ .clk_num = 93,
+ },
+ .flags = MUX,
+};
static struct clk_mux_sel mux_pllm_pllc_pllp_plla[] = {
{ .input = &tegra_pll_m, .value = 0},
@@ -2070,8 +2313,8 @@ static struct clk_mux_sel mux_pllp_out3[] = {
{ 0, 0},
};
-static struct clk_mux_sel mux_plld[] = {
- { .input = &tegra_pll_d, .value = 0},
+static struct clk_mux_sel mux_plld_out0[] = {
+ { .input = &tegra_pll_d_out0, .value = 0},
{ 0, 0},
};
@@ -2097,7 +2340,7 @@ static struct clk tegra_clk_emc = {
},
};
-#define PERIPH_CLK(_name, _dev, _con, _clk_num, _reg, _max, _inputs, _flags) \
+#define PERIPH_CLK(_name, _dev, _con, _clk_num, _reg, _reg_shift, _max, _inputs, _flags) \
{ \
.name = _name, \
.lookup = { \
@@ -2106,6 +2349,7 @@ static struct clk tegra_clk_emc = {
}, \
.ops = &tegra_periph_clk_ops, \
.reg = _reg, \
+ .reg_shift = _reg_shift, \
.inputs = _inputs, \
.flags = _flags, \
.max_rate = _max, \
@@ -2125,83 +2369,92 @@ static struct clk tegra_clk_emc = {
.parent = _parent, \
}
-struct clk tegra_list_clks[] = {
- PERIPH_CLK("apbdma", "tegra-dma", NULL, 34, 0, 108000000, mux_pclk, 0),
- PERIPH_CLK("rtc", "rtc-tegra", NULL, 4, 0, 32768, mux_clk_32k, PERIPH_NO_RESET),
- PERIPH_CLK("timer", "timer", NULL, 5, 0, 26000000, mux_clk_m, 0),
- PERIPH_CLK("i2s1", "tegra-i2s.0", NULL, 11, 0x100, 26000000, mux_pllaout0_audio2x_pllp_clkm, MUX | DIV_U71),
- PERIPH_CLK("i2s2", "tegra-i2s.1", NULL, 18, 0x104, 26000000, mux_pllaout0_audio2x_pllp_clkm, MUX | DIV_U71),
- PERIPH_CLK("spdif_out", "spdif_out", NULL, 10, 0x108, 100000000, mux_pllaout0_audio2x_pllp_clkm, MUX | DIV_U71),
- PERIPH_CLK("spdif_in", "spdif_in", NULL, 10, 0x10c, 100000000, mux_pllp_pllc_pllm, MUX | DIV_U71),
- PERIPH_CLK("pwm", "pwm", NULL, 17, 0x110, 432000000, mux_pllp_pllc_audio_clkm_clk32, MUX | DIV_U71),
- PERIPH_CLK("spi", "spi", NULL, 43, 0x114, 40000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71),
- PERIPH_CLK("xio", "xio", NULL, 45, 0x120, 150000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71),
- PERIPH_CLK("twc", "twc", NULL, 16, 0x12c, 150000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71),
- PERIPH_CLK("sbc1", "spi_tegra.0", NULL, 41, 0x134, 160000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71),
- PERIPH_CLK("sbc2", "spi_tegra.1", NULL, 44, 0x118, 160000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71),
- PERIPH_CLK("sbc3", "spi_tegra.2", NULL, 46, 0x11c, 160000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71),
- PERIPH_CLK("sbc4", "spi_tegra.3", NULL, 68, 0x1b4, 160000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71),
- PERIPH_CLK("ide", "ide", NULL, 25, 0x144, 100000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), /* requires min voltage */
- PERIPH_CLK("ndflash", "tegra_nand", NULL, 13, 0x160, 164000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), /* scales with voltage */
- PERIPH_CLK("vfir", "vfir", NULL, 7, 0x168, 72000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71),
- PERIPH_CLK("sdmmc1", "sdhci-tegra.0", NULL, 14, 0x150, 52000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), /* scales with voltage */
- PERIPH_CLK("sdmmc2", "sdhci-tegra.1", NULL, 9, 0x154, 52000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), /* scales with voltage */
- PERIPH_CLK("sdmmc3", "sdhci-tegra.2", NULL, 69, 0x1bc, 52000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), /* scales with voltage */
- PERIPH_CLK("sdmmc4", "sdhci-tegra.3", NULL, 15, 0x164, 52000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), /* scales with voltage */
- PERIPH_CLK("vcp", "tegra-avp", "vcp", 29, 0, 250000000, mux_clk_m, 0),
- PERIPH_CLK("bsea", "tegra-avp", "bsea", 62, 0, 250000000, mux_clk_m, 0),
- PERIPH_CLK("bsev", "tegra-aes", "bsev", 63, 0, 250000000, mux_clk_m, 0),
- PERIPH_CLK("vde", "tegra-avp", "vde", 61, 0x1c8, 250000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), /* scales with voltage and process_id */
- PERIPH_CLK("csite", "csite", NULL, 73, 0x1d4, 144000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), /* max rate ??? */
+struct clk tegra_list_periph_clks[] = {
+ PERIPH_CLK("apbdma", "tegra-dma", NULL, 34, 0, 0x31E, 108000000, mux_pclk, 0),
+ PERIPH_CLK("rtc", "rtc-tegra", NULL, 4, 0, 0x31E, 32768, mux_clk_32k, PERIPH_NO_RESET | PERIPH_ON_APB),
+ PERIPH_CLK("kbc", "tegra-kbc", NULL, 36, 0, 0x31E, 32768, mux_clk_32k, PERIPH_NO_RESET | PERIPH_ON_APB),
+ PERIPH_CLK("timer", "timer", NULL, 5, 0, 0x31E, 26000000, mux_clk_m, 0),
+ PERIPH_CLK("i2s1", "tegra20-i2s.0", NULL, 11, 0x100, 0x31E, 26000000, mux_pllaout0_audio2x_pllp_clkm, MUX | DIV_U71 | PERIPH_ON_APB),
+ PERIPH_CLK("i2s2", "tegra20-i2s.1", NULL, 18, 0x104, 0x31E, 26000000, mux_pllaout0_audio2x_pllp_clkm, MUX | DIV_U71 | PERIPH_ON_APB),
+ PERIPH_CLK("kfuse", "kfuse-tegra", NULL, 40, 0, 0x31E, 26000000, mux_clk_m, 0),
+ PERIPH_CLK("spdif_out", "tegra20-spdif", "spdif_out", 10, 0x108, 0x31E, 100000000, mux_pllaout0_audio2x_pllp_clkm, MUX | DIV_U71 | PERIPH_ON_APB),
+ PERIPH_CLK("spdif_in", "tegra20-spdif", "spdif_in", 10, 0x10c, 0x31E, 100000000, mux_pllp_pllc_pllm, MUX | DIV_U71 | PERIPH_ON_APB),
+ PERIPH_CLK("pwm", "pwm", NULL, 17, 0x110, 0x71C, 432000000, mux_pllp_pllc_audio_clkm_clk32, MUX | DIV_U71 | PERIPH_ON_APB),
+ PERIPH_CLK("spi", "spi", NULL, 43, 0x114, 0x31E, 40000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71 | PERIPH_ON_APB),
+ PERIPH_CLK("xio", "xio", NULL, 45, 0x120, 0x31E, 150000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71),
+ PERIPH_CLK("twc", "twc", NULL, 16, 0x12c, 0x31E, 150000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71 | PERIPH_ON_APB),
+ PERIPH_CLK("sbc1", "spi_tegra.0", NULL, 41, 0x134, 0x31E, 160000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71 | PERIPH_ON_APB),
+ PERIPH_CLK("sbc2", "spi_tegra.1", NULL, 44, 0x118, 0x31E, 160000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71 | PERIPH_ON_APB),
+ PERIPH_CLK("sbc3", "spi_tegra.2", NULL, 46, 0x11c, 0x31E, 160000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71 | PERIPH_ON_APB),
+ PERIPH_CLK("sbc4", "spi_tegra.3", NULL, 68, 0x1b4, 0x31E, 160000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71 | PERIPH_ON_APB),
+ PERIPH_CLK("ide", "ide", NULL, 25, 0x144, 0x31E, 100000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), /* requires min voltage */
+ PERIPH_CLK("ndflash", "tegra_nand", NULL, 13, 0x160, 0x31E, 164000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), /* scales with voltage */
+ PERIPH_CLK("vfir", "vfir", NULL, 7, 0x168, 0x31E, 72000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71 | PERIPH_ON_APB),
+ PERIPH_CLK("sdmmc1", "sdhci-tegra.0", NULL, 14, 0x150, 0x31E, 52000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), /* scales with voltage */
+ PERIPH_CLK("sdmmc2", "sdhci-tegra.1", NULL, 9, 0x154, 0x31E, 52000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), /* scales with voltage */
+ PERIPH_CLK("sdmmc3", "sdhci-tegra.2", NULL, 69, 0x1bc, 0x31E, 52000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), /* scales with voltage */
+ PERIPH_CLK("sdmmc4", "sdhci-tegra.3", NULL, 15, 0x164, 0x31E, 52000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), /* scales with voltage */
+ PERIPH_CLK("vcp", "tegra-avp", "vcp", 29, 0, 0x31E, 250000000, mux_clk_m, 0),
+ PERIPH_CLK("bsea", "tegra-avp", "bsea", 62, 0, 0x31E, 250000000, mux_clk_m, 0),
+ PERIPH_CLK("bsev", "tegra-aes", "bsev", 63, 0, 0x31E, 250000000, mux_clk_m, 0),
+ PERIPH_CLK("vde", "tegra-avp", "vde", 61, 0x1c8, 0x31E, 250000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), /* scales with voltage and process_id */
+ PERIPH_CLK("csite", "csite", NULL, 73, 0x1d4, 0x31E, 144000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), /* max rate ??? */
/* FIXME: what is la? */
- PERIPH_CLK("la", "la", NULL, 76, 0x1f8, 26000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71),
- PERIPH_CLK("owr", "tegra_w1", NULL, 71, 0x1cc, 26000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71),
- PERIPH_CLK("nor", "nor", NULL, 42, 0x1d0, 92000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), /* requires min voltage */
- PERIPH_CLK("mipi", "mipi", NULL, 50, 0x174, 60000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), /* scales with voltage */
- PERIPH_CLK("i2c1", "tegra-i2c.0", NULL, 12, 0x124, 26000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U16),
- PERIPH_CLK("i2c2", "tegra-i2c.1", NULL, 54, 0x198, 26000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U16),
- PERIPH_CLK("i2c3", "tegra-i2c.2", NULL, 67, 0x1b8, 26000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U16),
- PERIPH_CLK("dvc", "tegra-i2c.3", NULL, 47, 0x128, 26000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U16),
- PERIPH_CLK("i2c1_i2c", "tegra-i2c.0", "i2c", 0, 0, 72000000, mux_pllp_out3, 0),
- PERIPH_CLK("i2c2_i2c", "tegra-i2c.1", "i2c", 0, 0, 72000000, mux_pllp_out3, 0),
- PERIPH_CLK("i2c3_i2c", "tegra-i2c.2", "i2c", 0, 0, 72000000, mux_pllp_out3, 0),
- PERIPH_CLK("dvc_i2c", "tegra-i2c.3", "i2c", 0, 0, 72000000, mux_pllp_out3, 0),
- PERIPH_CLK("uarta", "uart.0", NULL, 6, 0x178, 600000000, mux_pllp_pllc_pllm_clkm, MUX),
- PERIPH_CLK("uartb", "uart.1", NULL, 7, 0x17c, 600000000, mux_pllp_pllc_pllm_clkm, MUX),
- PERIPH_CLK("uartc", "uart.2", NULL, 55, 0x1a0, 600000000, mux_pllp_pllc_pllm_clkm, MUX),
- PERIPH_CLK("uartd", "uart.3", NULL, 65, 0x1c0, 600000000, mux_pllp_pllc_pllm_clkm, MUX),
- PERIPH_CLK("uarte", "uart.4", NULL, 66, 0x1c4, 600000000, mux_pllp_pllc_pllm_clkm, MUX),
- PERIPH_CLK("3d", "3d", NULL, 24, 0x158, 300000000, mux_pllm_pllc_pllp_plla, MUX | DIV_U71 | PERIPH_MANUAL_RESET), /* scales with voltage and process_id */
- PERIPH_CLK("2d", "2d", NULL, 21, 0x15c, 300000000, mux_pllm_pllc_pllp_plla, MUX | DIV_U71), /* scales with voltage and process_id */
- PERIPH_CLK("vi", "tegra_camera", "vi", 20, 0x148, 150000000, mux_pllm_pllc_pllp_plla, MUX | DIV_U71), /* scales with voltage and process_id */
- PERIPH_CLK("vi_sensor", "tegra_camera", "vi_sensor", 20, 0x1a8, 150000000, mux_pllm_pllc_pllp_plla, MUX | DIV_U71 | PERIPH_NO_RESET), /* scales with voltage and process_id */
- PERIPH_CLK("epp", "epp", NULL, 19, 0x16c, 300000000, mux_pllm_pllc_pllp_plla, MUX | DIV_U71), /* scales with voltage and process_id */
- PERIPH_CLK("mpe", "mpe", NULL, 60, 0x170, 250000000, mux_pllm_pllc_pllp_plla, MUX | DIV_U71), /* scales with voltage and process_id */
- PERIPH_CLK("host1x", "host1x", NULL, 28, 0x180, 166000000, mux_pllm_pllc_pllp_plla, MUX | DIV_U71), /* scales with voltage and process_id */
- PERIPH_CLK("cve", "cve", NULL, 49, 0x140, 250000000, mux_pllp_plld_pllc_clkm, MUX | DIV_U71), /* requires min voltage */
- PERIPH_CLK("tvo", "tvo", NULL, 49, 0x188, 250000000, mux_pllp_plld_pllc_clkm, MUX | DIV_U71), /* requires min voltage */
- PERIPH_CLK("hdmi", "hdmi", NULL, 51, 0x18c, 600000000, mux_pllp_plld_pllc_clkm, MUX | DIV_U71), /* requires min voltage */
- PERIPH_CLK("tvdac", "tvdac", NULL, 53, 0x194, 250000000, mux_pllp_plld_pllc_clkm, MUX | DIV_U71), /* requires min voltage */
- PERIPH_CLK("disp1", "tegradc.0", NULL, 27, 0x138, 600000000, mux_pllp_plld_pllc_clkm, MUX), /* scales with voltage and process_id */
- PERIPH_CLK("disp2", "tegradc.1", NULL, 26, 0x13c, 600000000, mux_pllp_plld_pllc_clkm, MUX), /* scales with voltage and process_id */
- PERIPH_CLK("usbd", "fsl-tegra-udc", NULL, 22, 0, 480000000, mux_clk_m, 0), /* requires min voltage */
- PERIPH_CLK("usb2", "tegra-ehci.1", NULL, 58, 0, 480000000, mux_clk_m, 0), /* requires min voltage */
- PERIPH_CLK("usb3", "tegra-ehci.2", NULL, 59, 0, 480000000, mux_clk_m, 0), /* requires min voltage */
- PERIPH_CLK("dsi", "dsi", NULL, 48, 0, 500000000, mux_plld, 0), /* scales with voltage */
- PERIPH_CLK("csi", "tegra_camera", "csi", 52, 0, 72000000, mux_pllp_out3, 0),
- PERIPH_CLK("isp", "tegra_camera", "isp", 23, 0, 150000000, mux_clk_m, 0), /* same frequency as VI */
- PERIPH_CLK("csus", "tegra_camera", "csus", 92, 0, 150000000, mux_clk_m, PERIPH_NO_RESET),
- PERIPH_CLK("pex", NULL, "pex", 70, 0, 26000000, mux_clk_m, PERIPH_MANUAL_RESET),
- PERIPH_CLK("afi", NULL, "afi", 72, 0, 26000000, mux_clk_m, PERIPH_MANUAL_RESET),
- PERIPH_CLK("pcie_xclk", NULL, "pcie_xclk", 74, 0, 26000000, mux_clk_m, PERIPH_MANUAL_RESET),
-
- SHARED_CLK("avp.sclk", "tegra-avp", "sclk", &tegra_clk_sclk),
+ PERIPH_CLK("la", "la", NULL, 76, 0x1f8, 0x31E, 26000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71),
+ PERIPH_CLK("owr", "tegra_w1", NULL, 71, 0x1cc, 0x31E, 26000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71 | PERIPH_ON_APB),
+ PERIPH_CLK("nor", "tegra-nor", NULL, 42, 0x1d0, 0x31E, 92000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), /* requires min voltage */
+ PERIPH_CLK("mipi", "mipi", NULL, 50, 0x174, 0x31E, 60000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71 | PERIPH_ON_APB), /* scales with voltage */
+ PERIPH_CLK("i2c1", "tegra-i2c.0", NULL, 12, 0x124, 0x31E, 26000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U16 | PERIPH_ON_APB),
+ PERIPH_CLK("i2c2", "tegra-i2c.1", NULL, 54, 0x198, 0x31E, 26000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U16 | PERIPH_ON_APB),
+ PERIPH_CLK("i2c3", "tegra-i2c.2", NULL, 67, 0x1b8, 0x31E, 26000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U16 | PERIPH_ON_APB),
+ PERIPH_CLK("dvc", "tegra-i2c.3", NULL, 47, 0x128, 0x31E, 26000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U16 | PERIPH_ON_APB),
+ PERIPH_CLK("uarta", "tegra_uart.0", NULL, 6, 0x178, 0x31E, 600000000, mux_pllp_pllc_pllm_clkm, MUX | PERIPH_ON_APB),
+ PERIPH_CLK("uartb", "tegra_uart.1", NULL, 7, 0x17c, 0x31E, 600000000, mux_pllp_pllc_pllm_clkm, MUX | PERIPH_ON_APB),
+ PERIPH_CLK("uartc", "tegra_uart.2", NULL, 55, 0x1a0, 0x31E, 600000000, mux_pllp_pllc_pllm_clkm, MUX | PERIPH_ON_APB),
+ PERIPH_CLK("uartd", "tegra_uart.3", NULL, 65, 0x1c0, 0x31E, 600000000, mux_pllp_pllc_pllm_clkm, MUX | PERIPH_ON_APB),
+ PERIPH_CLK("uarte", "tegra_uart.4", NULL, 66, 0x1c4, 0x31E, 600000000, mux_pllp_pllc_pllm_clkm, MUX | PERIPH_ON_APB),
+ PERIPH_CLK("3d", "3d", NULL, 24, 0x158, 0x31E, 300000000, mux_pllm_pllc_pllp_plla, MUX | DIV_U71 | PERIPH_MANUAL_RESET), /* scales with voltage and process_id */
+ PERIPH_CLK("2d", "2d", NULL, 21, 0x15c, 0x31E, 300000000, mux_pllm_pllc_pllp_plla, MUX | DIV_U71), /* scales with voltage and process_id */
+ PERIPH_CLK("vi", "tegra_camera", "vi", 20, 0x148, 0x31E, 150000000, mux_pllm_pllc_pllp_plla, MUX | DIV_U71), /* scales with voltage and process_id */
+ PERIPH_CLK("vi_sensor", "tegra_camera", "vi_sensor", 20, 0x1a8, 0x31E, 150000000, mux_pllm_pllc_pllp_plla, MUX | DIV_U71 | PERIPH_NO_RESET), /* scales with voltage and process_id */
+ PERIPH_CLK("epp", "epp", NULL, 19, 0x16c, 0x31E, 300000000, mux_pllm_pllc_pllp_plla, MUX | DIV_U71), /* scales with voltage and process_id */
+ PERIPH_CLK("mpe", "mpe", NULL, 60, 0x170, 0x31E, 300000000, mux_pllm_pllc_pllp_plla, MUX | DIV_U71), /* scales with voltage and process_id */
+ PERIPH_CLK("host1x", "host1x", NULL, 28, 0x180, 0x31E, 166000000, mux_pllm_pllc_pllp_plla, MUX | DIV_U71), /* scales with voltage and process_id */
+ PERIPH_CLK("cve", "cve", NULL, 49, 0x140, 0x31E, 250000000, mux_pllp_plld_pllc_clkm, MUX | DIV_U71), /* requires min voltage */
+ PERIPH_CLK("tvo", "tvo", NULL, 49, 0x188, 0x31E, 250000000, mux_pllp_plld_pllc_clkm, MUX | DIV_U71), /* requires min voltage */
+ PERIPH_CLK("hdmi", "hdmi", NULL, 51, 0x18c, 0x31E, 600000000, mux_pllp_plld_pllc_clkm, MUX | DIV_U71), /* requires min voltage */
+ PERIPH_CLK("tvdac", "tvdac", NULL, 53, 0x194, 0x31E, 250000000, mux_pllp_plld_pllc_clkm, MUX | DIV_U71), /* requires min voltage */
+ PERIPH_CLK("disp1", "tegradc.0", NULL, 27, 0x138, 0x31E, 600000000, mux_pllp_plld_pllc_clkm, MUX), /* scales with voltage and process_id */
+ PERIPH_CLK("disp2", "tegradc.1", NULL, 26, 0x13c, 0x31E, 600000000, mux_pllp_plld_pllc_clkm, MUX), /* scales with voltage and process_id */
+ PERIPH_CLK("usbd", "fsl-tegra-udc", NULL, 22, 0, 0x31E, 480000000, mux_clk_m, 0), /* requires min voltage */
+ PERIPH_CLK("usb2", "tegra-ehci.1", NULL, 58, 0, 0x31E, 480000000, mux_clk_m, 0), /* requires min voltage */
+ PERIPH_CLK("usb3", "tegra-ehci.2", NULL, 59, 0, 0x31E, 480000000, mux_clk_m, 0), /* requires min voltage */
+ PERIPH_CLK("dsia", "tegradc.0", "dsia", 48, 0, 0x31E, 500000000, mux_plld_out0, 0), /* scales with voltage */
+ PERIPH_CLK("csi", "tegra_camera", "csi", 52, 0, 0x31E, 72000000, mux_pllp_out3, 0),
+ PERIPH_CLK("isp", "tegra_camera", "isp", 23, 0, 0x31E, 150000000, mux_clk_m, 0), /* same frequency as VI */
+ PERIPH_CLK("csus", "tegra_camera", "csus", 92, 0, 0x31E, 150000000, mux_clk_m, PERIPH_NO_RESET),
+ PERIPH_CLK("pex", NULL, "pex", 70, 0, 0x31E, 26000000, mux_clk_m, PERIPH_MANUAL_RESET),
+ PERIPH_CLK("afi", NULL, "afi", 72, 0, 0x31E, 26000000, mux_clk_m, PERIPH_MANUAL_RESET),
+ PERIPH_CLK("pcie_xclk", NULL, "pcie_xclk", 74, 0, 0x31E, 26000000, mux_clk_m, PERIPH_MANUAL_RESET),
+ PERIPH_CLK("stat_mon", "tegra-stat-mon", NULL, 37, 0, 0x31E, 26000000, mux_clk_m, 0),
+};
+
+struct clk tegra_list_shared_clks[] = {
+ SHARED_CLK("avp.sclk", "tegra-avp", "sclk", &tegra_clk_virtual_sclk),
+ SHARED_CLK("mon.sclk", "tegra-stat-mon", "sclk", &tegra_clk_virtual_sclk),
+ SHARED_CLK("bsea.sclk", "tegra-aes", "sclk", &tegra_clk_virtual_sclk),
+ SHARED_CLK("usbd.sclk", "fsl-tegra-udc", "sclk", &tegra_clk_virtual_sclk),
+ SHARED_CLK("usb1.sclk", "tegra-ehci.0", "sclk", &tegra_clk_virtual_sclk),
+ SHARED_CLK("usb2.sclk", "tegra-ehci.1", "sclk", &tegra_clk_virtual_sclk),
+ SHARED_CLK("usb3.sclk", "tegra-ehci.2", "sclk", &tegra_clk_virtual_sclk),
SHARED_CLK("avp.emc", "tegra-avp", "emc", &tegra_clk_emc),
SHARED_CLK("cpu.emc", "cpu", "emc", &tegra_clk_emc),
SHARED_CLK("disp1.emc", "tegradc.0", "emc", &tegra_clk_emc),
SHARED_CLK("disp2.emc", "tegradc.1", "emc", &tegra_clk_emc),
SHARED_CLK("hdmi.emc", "hdmi", "emc", &tegra_clk_emc),
- SHARED_CLK("host.emc", "tegra_grhost", "emc", &tegra_clk_emc),
+ SHARED_CLK("3d.emc", "tegra_gr3d", "emc", &tegra_clk_emc),
+ SHARED_CLK("2d.emc", "tegra_gr2d", "emc", &tegra_clk_emc),
+ SHARED_CLK("mpe.emc", "tegra_mpe", "emc", &tegra_clk_emc),
SHARED_CLK("usbd.emc", "fsl-tegra-udc", "emc", &tegra_clk_emc),
SHARED_CLK("usb1.emc", "tegra-ehci.0", "emc", &tegra_clk_emc),
SHARED_CLK("usb2.emc", "tegra-ehci.1", "emc", &tegra_clk_emc),
@@ -2222,27 +2475,30 @@ struct clk tegra_list_clks[] = {
* table under two names.
*/
struct clk_duplicate tegra_clk_duplicates[] = {
- CLK_DUPLICATE("uarta", "tegra_uart.0", NULL),
- CLK_DUPLICATE("uartb", "tegra_uart.1", NULL),
- CLK_DUPLICATE("uartc", "tegra_uart.2", NULL),
- CLK_DUPLICATE("uartd", "tegra_uart.3", NULL),
- CLK_DUPLICATE("uarte", "tegra_uart.4", NULL),
+ CLK_DUPLICATE("uarta", "serial8250.0", "uarta"),
+ CLK_DUPLICATE("uartb", "serial8250.0", "uartb"),
+ CLK_DUPLICATE("uartc", "serial8250.0", "uartc"),
+ CLK_DUPLICATE("uartd", "serial8250.0", "uartd"),
+ CLK_DUPLICATE("uarte", "serial8250.0", "uarte"),
CLK_DUPLICATE("usbd", "utmip-pad", NULL),
CLK_DUPLICATE("usbd", "tegra-ehci.0", NULL),
CLK_DUPLICATE("usbd", "tegra-otg", NULL),
CLK_DUPLICATE("hdmi", "tegradc.0", "hdmi"),
CLK_DUPLICATE("hdmi", "tegradc.1", "hdmi"),
+ CLK_DUPLICATE("dsia", "tegradc.1", "dsia"),
CLK_DUPLICATE("pwm", "tegra_pwm.0", NULL),
CLK_DUPLICATE("pwm", "tegra_pwm.1", NULL),
CLK_DUPLICATE("pwm", "tegra_pwm.2", NULL),
CLK_DUPLICATE("pwm", "tegra_pwm.3", NULL),
- CLK_DUPLICATE("host1x", "tegra_grhost", "host1x"),
- CLK_DUPLICATE("2d", "tegra_grhost", "gr2d"),
- CLK_DUPLICATE("3d", "tegra_grhost", "gr3d"),
- CLK_DUPLICATE("epp", "tegra_grhost", "epp"),
- CLK_DUPLICATE("mpe", "tegra_grhost", "mpe"),
+ CLK_DUPLICATE("host1x", "tegra_host1x", "host1x"),
+ CLK_DUPLICATE("2d", "tegra_gr2d", "gr2d"),
+ CLK_DUPLICATE("3d", "tegra_gr3d", "gr3d"),
+ CLK_DUPLICATE("epp", "tegra_gr2d", "epp"),
+ CLK_DUPLICATE("mpe", "tegra_mpe", "mpe"),
CLK_DUPLICATE("cop", "tegra-avp", "cop"),
CLK_DUPLICATE("vde", "tegra-aes", "vde"),
+ CLK_DUPLICATE("twd", "smp_twd", NULL),
+ CLK_DUPLICATE("bsea", "tegra-aes", "bsea"),
};
#define CLK(dev, con, ck) \
@@ -2280,11 +2536,80 @@ struct clk *tegra_ptr_clks[] = {
&tegra_clk_cdev1,
&tegra_clk_cdev2,
&tegra_clk_virtual_cpu,
+ &tegra_clk_virtual_sclk,
&tegra_clk_blink,
&tegra_clk_cop,
&tegra_clk_emc,
+ &tegra2_clk_twd,
+};
+
+/* For some clocks maximum rate limits depend on tegra2 SKU */
+#define RATE_LIMIT(_name, _max_rate, _skus...) \
+ { \
+ .clk_name = _name, \
+ .max_rate = _max_rate, \
+ .sku_ids = {_skus} \
+ }
+
+static struct tegra_sku_rate_limit sku_limits[] =
+{
+ RATE_LIMIT("cpu", 750000000, 0x07, 0x10),
+ RATE_LIMIT("cclk", 750000000, 0x07, 0x10),
+ RATE_LIMIT("pll_x", 750000000, 0x07, 0x10),
+
+ RATE_LIMIT("cpu", 1000000000, 0x04, 0x08, 0x0F),
+ RATE_LIMIT("cclk", 1000000000, 0x04, 0x08, 0x0F),
+ RATE_LIMIT("pll_x", 1000000000, 0x04, 0x08, 0x0F),
+
+ RATE_LIMIT("cpu", 1200000000, 0x14, 0x17, 0x18, 0x1B, 0x1C),
+ RATE_LIMIT("cclk", 1200000000, 0x14, 0x17, 0x18, 0x1B, 0x1C),
+ RATE_LIMIT("pll_x", 1200000000, 0x14, 0x17, 0x18, 0x1B, 0x1C),
+
+ RATE_LIMIT("sclk", 240000000, 0x04, 0x7, 0x08, 0x0F, 0x10),
+ RATE_LIMIT("hclk", 240000000, 0x04, 0x7, 0x08, 0x0F, 0x10),
+ RATE_LIMIT("vde", 240000000, 0x04, 0x7, 0x08, 0x0F, 0x10),
+ RATE_LIMIT("3d", 300000000, 0x04, 0x7, 0x08, 0x0F, 0x10),
+
+ RATE_LIMIT("host1x", 108000000, 0x0F),
+
+ RATE_LIMIT("sclk", 300000000, 0x14, 0x17, 0x18, 0x1B, 0x1C),
+ RATE_LIMIT("virt_sclk", 300000000, 0x14, 0x17, 0x18, 0x1B, 0x1C),
+ RATE_LIMIT("hclk", 300000000, 0x14, 0x17, 0x18, 0x1B, 0x1C),
+ RATE_LIMIT("pclk", 150000000, 0x14, 0x17, 0x18, 0x1B, 0x1C),
+ RATE_LIMIT("vde", 300000000, 0x14, 0x17, 0x18, 0x1B, 0x1C),
+ RATE_LIMIT("3d", 400000000, 0x14, 0x17, 0x18, 0x1B, 0x1C),
+
+ RATE_LIMIT("uarta", 800000000, 0x14, 0x17, 0x18, 0x1B, 0x1C),
+ RATE_LIMIT("uartb", 800000000, 0x14, 0x17, 0x18, 0x1B, 0x1C),
+ RATE_LIMIT("uartc", 800000000, 0x14, 0x17, 0x18, 0x1B, 0x1C),
+ RATE_LIMIT("uartd", 800000000, 0x14, 0x17, 0x18, 0x1B, 0x1C),
+ RATE_LIMIT("uarte", 800000000, 0x14, 0x17, 0x18, 0x1B, 0x1C),
};
+static void tegra2_init_sku_limits(void)
+{
+ int i, j;
+ struct clk *c;
+ int sku_id = tegra_sku_id();
+
+ for (i = 0; i < ARRAY_SIZE(sku_limits); i++) {
+ struct tegra_sku_rate_limit *limit = &sku_limits[i];
+
+ for (j = 0; (j < MAX_SAME_LIMIT_SKU_IDS) &&
+ (limit->sku_ids[j] != 0); j++) {
+ if (limit->sku_ids[j] == sku_id) {
+ c = tegra_get_clock_by_name(limit->clk_name);
+ if (!c) {
+ pr_err("%s: Unknown sku clock %s\n",
+ __func__, limit->clk_name);
+ continue;
+ }
+ c->max_rate = limit->max_rate;
+ }
+ }
+ }
+}
+
static void tegra2_init_one_clock(struct clk *c)
{
clk_init(c);
@@ -2295,37 +2620,92 @@ static void tegra2_init_one_clock(struct clk *c)
clkdev_add(&c->lookup);
}
-void __init tegra2_init_clocks(void)
-{
- int i;
- struct clk *c;
+#ifdef CONFIG_CPU_FREQ
- for (i = 0; i < ARRAY_SIZE(tegra_ptr_clks); i++)
- tegra2_init_one_clock(tegra_ptr_clks[i]);
-
- for (i = 0; i < ARRAY_SIZE(tegra_list_clks); i++)
- tegra2_init_one_clock(&tegra_list_clks[i]);
-
- for (i = 0; i < ARRAY_SIZE(tegra_clk_duplicates); i++) {
- c = tegra_get_clock_by_name(tegra_clk_duplicates[i].name);
- if (!c) {
- pr_err("%s: Unknown duplicate clock %s\n", __func__,
- tegra_clk_duplicates[i].name);
- continue;
- }
+/*
+ * Frequency table index must be sequential starting at 0 and frequencies
+ * must be ascending.
+ */
- tegra_clk_duplicates[i].lookup.clk = c;
- clkdev_add(&tegra_clk_duplicates[i].lookup);
+static struct cpufreq_frequency_table freq_table_750MHz[] = {
+ { 0, 216000 },
+ { 1, 312000 },
+ { 2, 456000 },
+ { 3, 608000 },
+ { 4, 750000 },
+ { 5, CPUFREQ_TABLE_END },
+};
+
+static struct cpufreq_frequency_table freq_table_1p0GHz[] = {
+ { 0, 216000 },
+ { 1, 312000 },
+ { 2, 456000 },
+ { 3, 608000 },
+ { 4, 760000 },
+ { 5, 816000 },
+ { 6, 912000 },
+ { 7, 1000000 },
+ { 8, CPUFREQ_TABLE_END },
+};
+
+static struct cpufreq_frequency_table freq_table_1p2GHz[] = {
+ { 0, 216000 },
+ { 1, 312000 },
+ { 2, 456000 },
+ { 3, 608000 },
+ { 4, 760000 },
+ { 5, 816000 },
+ { 6, 912000 },
+ { 7, 1000000 },
+ { 8, 1200000 },
+ { 9, CPUFREQ_TABLE_END },
+};
+
+static struct tegra_cpufreq_table_data cpufreq_tables[] = {
+ { freq_table_750MHz, 1, 4 },
+ { freq_table_1p0GHz, 2, 6 },
+ { freq_table_1p2GHz, 2, 7 },
+};
+
+struct tegra_cpufreq_table_data *tegra_cpufreq_table_get(void)
+{
+ int i, ret;
+ struct clk *cpu_clk = tegra_get_clock_by_name("cpu");
+
+ for (i = 0; i < ARRAY_SIZE(cpufreq_tables); i++) {
+ struct cpufreq_policy policy;
+ ret = cpufreq_frequency_table_cpuinfo(
+ &policy, cpufreq_tables[i].freq_table);
+ BUG_ON(ret);
+ if ((policy.max * 1000) == cpu_clk->max_rate)
+ return &cpufreq_tables[i];
}
+ pr_err("%s: No cpufreq table matching cpu range", __func__);
+ BUG();
+ return &cpufreq_tables[0];
+}
- init_audio_sync_clock_mux();
+unsigned long tegra_emc_to_cpu_ratio(unsigned long cpu_rate)
+{
+ /* Vote on memory bus frequency based on cpu frequency */
+ if (cpu_rate >= 816000)
+ return 600000000; /* cpu 816 MHz, emc max */
+ else if (cpu_rate >= 608000)
+ return 300000000; /* cpu 608 MHz, emc 150Mhz */
+ else if (cpu_rate >= 456000)
+ return 150000000; /* cpu 456 MHz, emc 75Mhz */
+ else if (cpu_rate >= 312000)
+ return 100000000; /* cpu 312 MHz, emc 50Mhz */
+ else
+ return 50000000; /* emc 25Mhz */
}
+#endif
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static u32 clk_rst_suspend[RST_DEVICES_NUM + CLK_OUT_ENB_NUM +
PERIPH_CLK_SOURCE_NUM + 22];
-void tegra_clk_suspend(void)
+static int tegra_clk_suspend(void)
{
unsigned long off, i;
u32 *ctx = clk_rst_suspend;
@@ -2374,9 +2754,11 @@ void tegra_clk_suspend(void)
*ctx++ = clk_readl(CLK_MASK_ARM);
BUG_ON(ctx - clk_rst_suspend != ARRAY_SIZE(clk_rst_suspend));
+
+ return 0;
}
-void tegra_clk_resume(void)
+static void tegra_clk_resume(void)
{
unsigned long off, i;
const u32 *ctx = clk_rst_suspend;
@@ -2438,4 +2820,45 @@ void tegra_clk_resume(void)
clk_writel(*ctx++, MISC_CLK_ENB);
clk_writel(*ctx++, CLK_MASK_ARM);
}
+
+#else
+#define tegra_clk_suspend NULL
+#define tegra_clk_resume NULL
#endif
+
+static struct syscore_ops tegra_clk_syscore_ops = {
+ .suspend = tegra_clk_suspend,
+ .resume = tegra_clk_resume,
+};
+
+void __init tegra_soc_init_clocks(void)
+{
+ int i;
+ struct clk *c;
+
+ for (i = 0; i < ARRAY_SIZE(tegra_ptr_clks); i++)
+ tegra2_init_one_clock(tegra_ptr_clks[i]);
+
+ for (i = 0; i < ARRAY_SIZE(tegra_list_periph_clks); i++)
+ tegra2_init_one_clock(&tegra_list_periph_clks[i]);
+
+ for (i = 0; i < ARRAY_SIZE(tegra_clk_duplicates); i++) {
+ c = tegra_get_clock_by_name(tegra_clk_duplicates[i].name);
+ if (!c) {
+ pr_err("%s: Unknown duplicate clock %s\n", __func__,
+ tegra_clk_duplicates[i].name);
+ continue;
+ }
+
+ tegra_clk_duplicates[i].lookup.clk = c;
+ clkdev_add(&tegra_clk_duplicates[i].lookup);
+ }
+
+ init_audio_sync_clock_mux();
+ tegra2_init_sku_limits();
+
+ for (i = 0; i < ARRAY_SIZE(tegra_list_shared_clks); i++)
+ tegra2_init_one_clock(&tegra_list_shared_clks[i]);
+
+ register_syscore_ops(&tegra_clk_syscore_ops);
+}
diff --git a/arch/arm/mach-tegra/tegra2_dvfs.c b/arch/arm/mach-tegra/tegra2_dvfs.c
new file mode 100644
index 000000000000..7ab1f733add7
--- /dev/null
+++ b/arch/arm/mach-tegra/tegra2_dvfs.c
@@ -0,0 +1,357 @@
+/*
+ * arch/arm/mach-tegra/tegra2_dvfs.c
+ *
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * Author:
+ * Colin Cross <ccross@google.com>
+ *
+ * Copyright (C) 2010-2011 NVIDIA Corporation
+ *
+ * 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/init.h>
+#include <linux/string.h>
+#include <linux/module.h>
+
+#include "clock.h"
+#include "dvfs.h"
+#include "fuse.h"
+
+#ifdef CONFIG_TEGRA_CORE_DVFS
+static bool tegra_dvfs_core_disabled;
+#else
+static bool tegra_dvfs_core_disabled = true;
+#endif
+#ifdef CONFIG_TEGRA_CPU_DVFS
+static bool tegra_dvfs_cpu_disabled;
+#else
+static bool tegra_dvfs_cpu_disabled = true;
+#endif
+
+static const int core_millivolts[MAX_DVFS_FREQS] =
+ {950, 1000, 1100, 1200, 1225, 1275, 1300};
+static const int cpu_millivolts[MAX_DVFS_FREQS] =
+ {750, 775, 800, 825, 850, 875, 900, 925, 950, 975, 1000, 1025, 1050, 1100, 1125};
+
+static const int cpu_speedo_nominal_millivolts[] =
+/* spedo_id 0, 1, 2 */
+ { 1100, 1025, 1125 };
+
+static const int core_speedo_nominal_millivolts[] =
+/* spedo_id 0, 1, 2 */
+ { 1225, 1225, 1300 };
+
+#define KHZ 1000
+#define MHZ 1000000
+
+static struct dvfs_rail tegra2_dvfs_rail_vdd_cpu = {
+ .reg_id = "vdd_cpu",
+ .max_millivolts = 1125,
+ .min_millivolts = 750,
+ .nominal_millivolts = 1125,
+};
+
+static struct dvfs_rail tegra2_dvfs_rail_vdd_core = {
+ .reg_id = "vdd_core",
+ .max_millivolts = 1300,
+ .min_millivolts = 950,
+ .nominal_millivolts = 1225,
+ .step = 150, /* step vdd_core by 150 mV to allow vdd_aon to follow */
+};
+
+static struct dvfs_rail tegra2_dvfs_rail_vdd_aon = {
+ .reg_id = "vdd_aon",
+ .max_millivolts = 1300,
+ .min_millivolts = 950,
+ .nominal_millivolts = 1225,
+#ifndef CONFIG_TEGRA_CORE_DVFS
+ .disabled = true,
+#endif
+};
+
+/* vdd_core and vdd_aon must be 50 mV higher than vdd_cpu */
+static int tegra2_dvfs_rel_vdd_cpu_vdd_core(struct dvfs_rail *vdd_cpu,
+ struct dvfs_rail *vdd_core)
+{
+ if (vdd_cpu->new_millivolts > vdd_cpu->millivolts &&
+ vdd_core->new_millivolts < vdd_cpu->new_millivolts + 50)
+ return vdd_cpu->new_millivolts + 50;
+
+ if (vdd_core->new_millivolts < vdd_cpu->millivolts + 50)
+ return vdd_cpu->millivolts + 50;
+
+ return vdd_core->new_millivolts;
+}
+
+/* vdd_aon must be within 170 mV of vdd_core */
+static int tegra2_dvfs_rel_vdd_core_vdd_aon(struct dvfs_rail *vdd_core,
+ struct dvfs_rail *vdd_aon)
+{
+ BUG_ON(abs(vdd_aon->millivolts - vdd_core->millivolts) >
+ vdd_aon->step);
+ return vdd_core->millivolts;
+}
+
+static struct dvfs_relationship tegra2_dvfs_relationships[] = {
+ {
+ /* vdd_core must be 50 mV higher than vdd_cpu */
+ .from = &tegra2_dvfs_rail_vdd_cpu,
+ .to = &tegra2_dvfs_rail_vdd_core,
+ .solve = tegra2_dvfs_rel_vdd_cpu_vdd_core,
+ },
+ {
+ /* vdd_aon must be 50 mV higher than vdd_cpu */
+ .from = &tegra2_dvfs_rail_vdd_cpu,
+ .to = &tegra2_dvfs_rail_vdd_aon,
+ .solve = tegra2_dvfs_rel_vdd_cpu_vdd_core,
+ },
+ {
+ /* vdd_aon must be within 170 mV of vdd_core */
+ .from = &tegra2_dvfs_rail_vdd_core,
+ .to = &tegra2_dvfs_rail_vdd_aon,
+ .solve = tegra2_dvfs_rel_vdd_core_vdd_aon,
+ },
+};
+
+static struct dvfs_rail *tegra2_dvfs_rails[] = {
+ &tegra2_dvfs_rail_vdd_cpu,
+ &tegra2_dvfs_rail_vdd_core,
+ &tegra2_dvfs_rail_vdd_aon,
+};
+
+#define CPU_DVFS(_clk_name, _speedo_id, _process_id, _mult, _freqs...) \
+ { \
+ .clk_name = _clk_name, \
+ .speedo_id = _speedo_id, \
+ .process_id = _process_id, \
+ .freqs = {_freqs}, \
+ .freqs_mult = _mult, \
+ .millivolts = cpu_millivolts, \
+ .auto_dvfs = true, \
+ .dvfs_rail = &tegra2_dvfs_rail_vdd_cpu, \
+ }
+
+#define CORE_DVFS(_clk_name, _process_id, _auto, _mult, _freqs...) \
+ { \
+ .clk_name = _clk_name, \
+ .speedo_id = -1, \
+ .process_id = _process_id, \
+ .freqs = {_freqs}, \
+ .freqs_mult = _mult, \
+ .millivolts = core_millivolts, \
+ .auto_dvfs = _auto, \
+ .dvfs_rail = &tegra2_dvfs_rail_vdd_core, \
+ }
+
+static struct dvfs dvfs_init[] = {
+ /* Cpu voltages (mV): 750, 775, 800, 825, 850, 875, 900, 925, 950, 975, 1000, 1025, 1050, 1100, 1125 */
+ CPU_DVFS("cpu", 0, 0, MHZ, 314, 314, 314, 456, 456, 456, 608, 608, 608, 760, 817, 817, 912, 1000),
+ CPU_DVFS("cpu", 0, 1, MHZ, 314, 314, 314, 456, 456, 456, 618, 618, 618, 770, 827, 827, 922, 1000),
+ CPU_DVFS("cpu", 0, 2, MHZ, 494, 494, 494, 675, 675, 817, 817, 922, 922, 1000),
+ CPU_DVFS("cpu", 0, 3, MHZ, 730, 760, 845, 845, 940, 1000),
+
+ CPU_DVFS("cpu", 1, 0, MHZ, 380, 380, 503, 503, 655, 655, 798, 798, 902, 902, 960, 1000),
+ CPU_DVFS("cpu", 1, 1, MHZ, 389, 389, 503, 503, 655, 760, 798, 798, 950, 950, 1000),
+ CPU_DVFS("cpu", 1, 2, MHZ, 598, 598, 750, 750, 893, 893, 1000),
+ CPU_DVFS("cpu", 1, 3, MHZ, 730, 760, 845, 845, 940, 1000),
+
+ CPU_DVFS("cpu", 2, 0, MHZ, 0, 0, 0, 0, 655, 655, 798, 798, 902, 902, 960, 1000, 1100, 1100, 1200),
+ CPU_DVFS("cpu", 2, 1, MHZ, 0, 0, 0, 0, 655, 760, 798, 798, 950, 950, 1015, 1015, 1100, 1200),
+ CPU_DVFS("cpu", 2, 2, MHZ, 0, 0, 0, 0, 769, 769, 902, 902, 1026, 1026, 1140, 1140, 1200),
+ CPU_DVFS("cpu", 2, 3, MHZ, 0, 0, 0, 0, 940, 1000, 1000, 1000, 1130, 1130, 1200),
+
+ /* Core voltages (mV): 950, 1000, 1100, 1200, 1225, 1275, 1300 */
+ CORE_DVFS("emc", -1, 1, KHZ, 57000, 333000, 380000, 666000, 666000, 666000, 760000),
+
+#if 0
+ /*
+ * The sdhci core calls the clock ops with a spinlock held, which
+ * conflicts with the sleeping dvfs api.
+ * For now, boards must ensure that the core voltage does not drop
+ * below 1V, or that the sdmmc busses are set to 44 MHz or less.
+ */
+ CORE_DVFS("sdmmc1", -1, 1, KHZ, 44000, 52000, 52000, 52000, 52000, 52000, 52000),
+ CORE_DVFS("sdmmc2", -1, 1, KHZ, 44000, 52000, 52000, 52000, 52000, 52000, 52000),
+ CORE_DVFS("sdmmc3", -1, 1, KHZ, 44000, 52000, 52000, 52000, 52000, 52000, 52000),
+ CORE_DVFS("sdmmc4", -1, 1, KHZ, 44000, 52000, 52000, 52000, 52000, 52000, 52000),
+#endif
+
+ CORE_DVFS("ndflash", -1, 1, KHZ, 130000, 150000, 158000, 164000, 164000, 164000, 164000),
+ CORE_DVFS("nor", -1, 1, KHZ, 0, 92000, 92000, 92000, 92000, 92000, 92000),
+ CORE_DVFS("ide", -1, 1, KHZ, 0, 0, 100000, 100000, 100000, 100000, 100000),
+ CORE_DVFS("mipi", -1, 1, KHZ, 0, 40000, 40000, 40000, 40000, 60000, 60000),
+ CORE_DVFS("usbd", -1, 1, KHZ, 0, 0, 480000, 480000, 480000, 480000, 480000),
+ CORE_DVFS("usb2", -1, 1, KHZ, 0, 0, 480000, 480000, 480000, 480000, 480000),
+ CORE_DVFS("usb3", -1, 1, KHZ, 0, 0, 480000, 480000, 480000, 480000, 480000),
+ CORE_DVFS("pcie", -1, 1, KHZ, 0, 0, 0, 250000, 250000, 250000, 250000),
+ CORE_DVFS("dsi", -1, 1, KHZ, 100000, 100000, 100000, 500000, 500000, 500000, 500000),
+ CORE_DVFS("tvo", -1, 1, KHZ, 0, 0, 0, 250000, 250000, 250000, 250000),
+
+ /*
+ * The clock rate for the display controllers that determines the
+ * necessary core voltage depends on a divider that is internal
+ * to the display block. Disable auto-dvfs on the display clocks,
+ * and let the display driver call tegra_dvfs_set_rate manually
+ */
+ CORE_DVFS("disp1", -1, 0, KHZ, 158000, 158000, 190000, 190000, 190000, 190000, 190000),
+ CORE_DVFS("disp2", -1, 0, KHZ, 158000, 158000, 190000, 190000, 190000, 190000, 190000),
+ CORE_DVFS("hdmi", -1, 0, KHZ, 0, 0, 0, 148500, 148500, 148500, 148500),
+
+ /*
+ * Clocks below depend on the core process id. Define per process_id
+ * tables for SCLK/VDE/3D clocks (maximum rate for these clocks is
+ * increased depending on tegra2 sku). Use the worst case value for
+ * other clocks for now.
+ */
+ CORE_DVFS("host1x", -1, 1, KHZ, 104500, 133000, 166000, 166000, 166000, 166000, 166000),
+ CORE_DVFS("epp", -1, 1, KHZ, 133000, 171000, 247000, 300000, 300000, 300000, 300000),
+ CORE_DVFS("2d", -1, 1, KHZ, 133000, 171000, 247000, 300000, 300000, 300000, 300000),
+
+ CORE_DVFS("3d", 0, 1, KHZ, 114000, 161500, 247000, 304000, 304000, 333500, 333500),
+ CORE_DVFS("3d", 1, 1, KHZ, 161500, 209000, 285000, 333500, 333500, 361000, 361000),
+ CORE_DVFS("3d", 2, 1, KHZ, 218500, 256500, 323000, 380000, 380000, 400000, 400000),
+ CORE_DVFS("3d", 3, 1, KHZ, 247000, 285000, 351500, 400000, 400000, 400000, 400000),
+
+ CORE_DVFS("mpe", 0, 1, KHZ, 104500, 152000, 228000, 300000, 300000, 300000, 300000),
+ CORE_DVFS("mpe", 1, 1, KHZ, 142500, 190000, 275500, 300000, 300000, 300000, 300000),
+ CORE_DVFS("mpe", 2, 1, KHZ, 190000, 237500, 300000, 300000, 300000, 300000, 300000),
+ CORE_DVFS("mpe", 3, 1, KHZ, 228000, 266000, 300000, 300000, 300000, 300000, 300000),
+
+ CORE_DVFS("vi", -1, 1, KHZ, 85000, 100000, 150000, 150000, 150000, 150000, 150000),
+
+ CORE_DVFS("sclk", 0, 1, KHZ, 95000, 133000, 190000, 222500, 240000, 247000, 262000),
+ CORE_DVFS("sclk", 1, 1, KHZ, 123500, 159500, 207000, 240000, 240000, 264000, 277500),
+ CORE_DVFS("sclk", 2, 1, KHZ, 152000, 180500, 229500, 260000, 260000, 285000, 300000),
+ CORE_DVFS("sclk", 3, 1, KHZ, 171000, 218500, 256500, 292500, 292500, 300000, 300000),
+
+ CORE_DVFS("vde", 0, 1, KHZ, 95000, 123500, 209000, 275500, 275500, 300000, 300000),
+ CORE_DVFS("vde", 1, 1, KHZ, 123500, 152000, 237500, 300000, 300000, 300000, 300000),
+ CORE_DVFS("vde", 2, 1, KHZ, 152000, 209000, 285000, 300000, 300000, 300000, 300000),
+ CORE_DVFS("vde", 3, 1, KHZ, 171000, 218500, 300000, 300000, 300000, 300000, 300000),
+ /* What is this? */
+ CORE_DVFS("NVRM_DEVID_CLK_SRC", -1, 1, MHZ, 480, 600, 800, 1067, 1067, 1067, 1067),
+};
+
+int tegra_dvfs_disable_core_set(const char *arg, const struct kernel_param *kp)
+{
+ int ret;
+
+ ret = param_set_bool(arg, kp);
+ if (ret)
+ return ret;
+
+ if (tegra_dvfs_core_disabled)
+ tegra_dvfs_rail_disable(&tegra2_dvfs_rail_vdd_core);
+ else
+ tegra_dvfs_rail_enable(&tegra2_dvfs_rail_vdd_core);
+
+ return 0;
+}
+
+int tegra_dvfs_disable_cpu_set(const char *arg, const struct kernel_param *kp)
+{
+ int ret;
+
+ ret = param_set_bool(arg, kp);
+ if (ret)
+ return ret;
+
+ if (tegra_dvfs_cpu_disabled)
+ tegra_dvfs_rail_disable(&tegra2_dvfs_rail_vdd_cpu);
+ else
+ tegra_dvfs_rail_enable(&tegra2_dvfs_rail_vdd_cpu);
+
+ return 0;
+}
+
+int tegra_dvfs_disable_get(char *buffer, const struct kernel_param *kp)
+{
+ return param_get_bool(buffer, kp);
+}
+
+static struct kernel_param_ops tegra_dvfs_disable_core_ops = {
+ .set = tegra_dvfs_disable_core_set,
+ .get = tegra_dvfs_disable_get,
+};
+
+static struct kernel_param_ops tegra_dvfs_disable_cpu_ops = {
+ .set = tegra_dvfs_disable_cpu_set,
+ .get = tegra_dvfs_disable_get,
+};
+
+module_param_cb(disable_core, &tegra_dvfs_disable_core_ops,
+ &tegra_dvfs_core_disabled, 0644);
+module_param_cb(disable_cpu, &tegra_dvfs_disable_cpu_ops,
+ &tegra_dvfs_cpu_disabled, 0644);
+
+void __init tegra_soc_init_dvfs(void)
+{
+ int i;
+ struct clk *c;
+ struct dvfs *d;
+ int process_id;
+ int ret;
+ int cpu_process_id = tegra_cpu_process_id();
+ int core_process_id = tegra_core_process_id();
+ int speedo_id = tegra_soc_speedo_id();
+
+ BUG_ON(speedo_id >= ARRAY_SIZE(cpu_speedo_nominal_millivolts));
+ tegra2_dvfs_rail_vdd_cpu.nominal_millivolts =
+ cpu_speedo_nominal_millivolts[speedo_id];
+ BUG_ON(speedo_id >= ARRAY_SIZE(core_speedo_nominal_millivolts));
+ tegra2_dvfs_rail_vdd_core.nominal_millivolts =
+ core_speedo_nominal_millivolts[speedo_id];
+ tegra2_dvfs_rail_vdd_aon.nominal_millivolts =
+ core_speedo_nominal_millivolts[speedo_id];
+
+ tegra_dvfs_init_rails(tegra2_dvfs_rails, ARRAY_SIZE(tegra2_dvfs_rails));
+ tegra_dvfs_add_relationships(tegra2_dvfs_relationships,
+ ARRAY_SIZE(tegra2_dvfs_relationships));
+ /*
+ * VDD_CORE must always be at least 50 mV higher than VDD_CPU
+ * Fill out cpu_core_millivolts based on cpu_millivolts
+ */
+ for (i = 0; i < ARRAY_SIZE(dvfs_init); i++) {
+ d = &dvfs_init[i];
+
+ process_id = strcmp(d->clk_name, "cpu") ?
+ core_process_id : cpu_process_id;
+ if ((d->process_id != -1 && d->process_id != process_id) ||
+ (d->speedo_id != -1 && d->speedo_id != speedo_id)) {
+ pr_debug("tegra_dvfs: rejected %s speedo %d,"
+ " process %d\n", d->clk_name, d->speedo_id,
+ d->process_id);
+ continue;
+ }
+
+ c = tegra_get_clock_by_name(d->clk_name);
+
+ if (!c) {
+ pr_debug("tegra_dvfs: no clock found for %s\n",
+ d->clk_name);
+ continue;
+ }
+
+ ret = tegra_enable_dvfs_on_clk(c, d);
+ if (ret)
+ pr_err("tegra_dvfs: failed to enable dvfs on %s\n",
+ c->name);
+ }
+
+ if (tegra_dvfs_core_disabled)
+ tegra_dvfs_rail_disable(&tegra2_dvfs_rail_vdd_core);
+
+ if (tegra_dvfs_cpu_disabled)
+ tegra_dvfs_rail_disable(&tegra2_dvfs_rail_vdd_cpu);
+}
diff --git a/arch/arm/mach-tegra/tegra2_emc.c b/arch/arm/mach-tegra/tegra2_emc.c
index 0f7ae6e90b55..f1ac82ad5c15 100644
--- a/arch/arm/mach-tegra/tegra2_emc.c
+++ b/arch/arm/mach-tegra/tegra2_emc.c
@@ -25,6 +25,11 @@
#include "tegra2_emc.h"
+#define TEGRA_MRR_DIVLD (1<<20)
+#define TEGRA_EMC_STATUS 0x02b4
+#define TEGRA_EMC_MRR 0x00ec
+static DEFINE_MUTEX(tegra_emc_mrr_lock);
+
#ifdef CONFIG_TEGRA_EMC_SCALING_ENABLE
static bool emc_enable = true;
#else
@@ -36,6 +41,9 @@ static void __iomem *emc = IO_ADDRESS(TEGRA_EMC_BASE);
static const struct tegra_emc_table *tegra_emc_table;
static int tegra_emc_table_size;
+static unsigned long tegra_emc_max_bus_rate; /* 2 * 1000 * maximum emc_clock rate */
+static unsigned long tegra_emc_min_bus_rate; /* 2 * 1000 * minimum emc_clock rate */
+
static inline void emc_writel(u32 val, unsigned long addr)
{
writel(val, emc + addr);
@@ -46,6 +54,35 @@ static inline u32 emc_readl(unsigned long addr)
return readl(emc + addr);
}
+/* read LPDDR2 memory modes */
+static int tegra_emc_read_mrr(unsigned long addr)
+{
+ u32 value;
+ int count = 100;
+
+ mutex_lock(&tegra_emc_mrr_lock);
+ do {
+ emc_readl(TEGRA_EMC_MRR);
+ } while (--count && (emc_readl(TEGRA_EMC_STATUS) & TEGRA_MRR_DIVLD));
+ if (count == 0) {
+ pr_err("%s: Failed to read memory type\n", __func__);
+ BUG();
+ }
+ value = (1 << 30) | (addr << 16);
+ emc_writel(value, TEGRA_EMC_MRR);
+
+ count = 100;
+ while (--count && !(emc_readl(TEGRA_EMC_STATUS) & TEGRA_MRR_DIVLD));
+ if (count == 0) {
+ pr_err("%s: Failed to read memory type\n", __func__);
+ BUG();
+ }
+ value = emc_readl(TEGRA_EMC_MRR) & 0xFFFF;
+ mutex_unlock(&tegra_emc_mrr_lock);
+
+ return value;
+}
+
static const unsigned long emc_reg_addr[TEGRA_EMC_NUM_REGS] = {
0x2c, /* RC */
0x30, /* RFC */
@@ -108,6 +145,14 @@ long tegra_emc_round_rate(unsigned long rate)
if (!emc_enable)
return -EINVAL;
+ if (rate >= tegra_emc_max_bus_rate) {
+ best = tegra_emc_table_size - 1;
+ goto round_out;
+ } else if (rate <= tegra_emc_min_bus_rate) {
+ best = 0;
+ goto round_out;
+ }
+
pr_debug("%s: %lu\n", __func__, rate);
/*
@@ -126,7 +171,7 @@ long tegra_emc_round_rate(unsigned long rate)
if (best < 0)
return -EINVAL;
-
+round_out:
pr_debug("%s: using %lu\n", __func__, tegra_emc_table[best].rate);
return tegra_emc_table[best].rate * 2 * 1000;
@@ -154,11 +199,11 @@ int tegra_emc_set_rate(unsigned long rate)
*/
rate = rate / 2 / 1000;
- for (i = 0; i < tegra_emc_table_size; i++)
+ for (i = tegra_emc_table_size - 1; i >= 0; i--)
if (tegra_emc_table[i].rate == rate)
break;
- if (i >= tegra_emc_table_size)
+ if (i < 0)
return -EINVAL;
pr_debug("%s: setting to %lu\n", __func__, rate);
@@ -171,8 +216,57 @@ int tegra_emc_set_rate(unsigned long rate)
return 0;
}
-void tegra_init_emc(const struct tegra_emc_table *table, int table_size)
+void tegra_init_emc(const struct tegra_emc_chip *chips, int chips_size)
{
- tegra_emc_table = table;
- tegra_emc_table_size = table_size;
+ int i;
+ int vid;
+ int rev_id1;
+ int rev_id2;
+ int pid;
+ int chip_matched = -1;
+
+ vid = tegra_emc_read_mrr(5);
+ rev_id1 = tegra_emc_read_mrr(6);
+ rev_id2 = tegra_emc_read_mrr(7);
+ pid = tegra_emc_read_mrr(8);
+
+ for (i = 0; i < chips_size; i++) {
+ if (chips[i].mem_manufacturer_id >= 0) {
+ if (chips[i].mem_manufacturer_id != vid)
+ continue;
+ }
+ if (chips[i].mem_revision_id1 >= 0) {
+ if (chips[i].mem_revision_id1 != rev_id1)
+ continue;
+ }
+ if (chips[i].mem_revision_id2 >= 0) {
+ if (chips[i].mem_revision_id2 != rev_id2)
+ continue;
+ }
+ if (chips[i].mem_pid >= 0) {
+ if (chips[i].mem_pid != pid)
+ continue;
+ }
+
+ chip_matched = i;
+ break;
+ }
+
+ if (chip_matched >= 0) {
+ pr_info("%s: %s memory found\n", __func__,
+ chips[chip_matched].description);
+ tegra_emc_table = chips[chip_matched].table;
+ tegra_emc_table_size = chips[chip_matched].table_size;
+
+ tegra_emc_min_bus_rate = tegra_emc_table[0].rate * 2 * 1000;
+ tegra_emc_max_bus_rate = tegra_emc_table[tegra_emc_table_size - 1].rate * 2 * 1000;
+
+ } else {
+ pr_err("%s: Memory not recognized, memory scaling disabled\n",
+ __func__);
+ pr_info("%s: Memory vid = 0x%04x", __func__, vid);
+ pr_info("%s: Memory rev_id1 = 0x%04x", __func__, rev_id1);
+ pr_info("%s: Memory rev_id2 = 0x%04x", __func__, rev_id2);
+ pr_info("%s: Memory pid = 0x%04x", __func__, pid);
+ }
}
diff --git a/arch/arm/mach-tegra/tegra2_emc.h b/arch/arm/mach-tegra/tegra2_emc.h
index 19f08cb31603..a40937dd7fcf 100644
--- a/arch/arm/mach-tegra/tegra2_emc.h
+++ b/arch/arm/mach-tegra/tegra2_emc.h
@@ -22,6 +22,15 @@ struct tegra_emc_table {
u32 regs[TEGRA_EMC_NUM_REGS];
};
-int tegra_emc_set_rate(unsigned long rate);
-long tegra_emc_round_rate(unsigned long rate);
-void tegra_init_emc(const struct tegra_emc_table *table, int table_size);
+struct tegra_emc_chip {
+ const char *description;
+ int mem_manufacturer_id; /* LPDDR2 MR5 or -1 to ignore */
+ int mem_revision_id1; /* LPDDR2 MR6 or -1 to ignore */
+ int mem_revision_id2; /* LPDDR2 MR7 or -1 to ignore */
+ int mem_pid; /* LPDDR2 MR8 or -1 to ignore */
+
+ const struct tegra_emc_table *table;
+ int table_size;
+};
+
+void tegra_init_emc(const struct tegra_emc_chip *chips, int chips_size);
diff --git a/arch/arm/mach-tegra/tegra2_mc.c b/arch/arm/mach-tegra/tegra2_mc.c
new file mode 100644
index 000000000000..6df9c232c02f
--- /dev/null
+++ b/arch/arm/mach-tegra/tegra2_mc.c
@@ -0,0 +1,1017 @@
+/*
+ * arch/arm/mach-tegra/tegra2_mc.c
+ *
+ * Memory controller bandwidth profiling interface
+ *
+ * Copyright (c) 2009-2011, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/slab.h>
+#include <linux/kobject.h>
+#include <linux/string.h>
+#include <linux/sysfs.h>
+#include <linux/sysdev.h>
+#include <linux/ktime.h>
+#include <linux/hrtimer.h>
+#include <linux/parser.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/clk.h>
+
+#include <mach/iomap.h>
+
+#include <asm/uaccess.h>
+
+#include "clock.h"
+#include "tegra2_mc.h"
+
+static void stat_start(void);
+static void stat_stop(void);
+static void stat_log(void);
+
+static struct hrtimer sample_timer;
+
+#define MC_COUNTER_INITIALIZER() \
+ { \
+ .enabled = false, \
+ .period = 10, \
+ .mode = FILTER_CLIENT, \
+ .address_low = 0, \
+ .address_length = 0xfffffffful, \
+ .sample_data = { \
+ .signature = 0xdeadbeef, \
+ } \
+ }
+
+static struct tegra_mc_counter mc_counter0 = MC_COUNTER_INITIALIZER();
+static struct tegra_mc_counter mc_counter1 = MC_COUNTER_INITIALIZER();
+static struct tegra_mc_counter emc_llp_counter = MC_COUNTER_INITIALIZER();
+
+/* /sys/devices/system/tegra_mc */
+static bool sample_enable = SAMPLE_ENABLE_DEFAULT;
+static u16 sample_quantum = SAMPLE_QUANTUM_DEFAULT;
+static u8 sample_log[SAMPLE_LOG_SIZE];
+
+static DEFINE_SPINLOCK(sample_enable_lock);
+static DEFINE_SPINLOCK(sample_log_lock);
+
+static u8 *sample_log_wptr = sample_log, *sample_log_rptr = sample_log;
+static int sample_log_size = SAMPLE_LOG_SIZE - 1;
+static struct clk *emc_clock = NULL;
+
+static bool sampling(void)
+{
+ bool ret;
+
+ spin_lock_bh(&sample_enable_lock);
+ ret = (sample_enable == true)? true : false;
+ spin_unlock_bh(&sample_enable_lock);
+
+ return ret;
+}
+
+static struct sysdev_class tegra_mc_sysclass = {
+ .name = "tegra_mc",
+};
+
+static ssize_t tegra_mc_enable_show(struct sysdev_class *class,
+ struct sysdev_class_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%d\n", sample_enable);
+}
+
+static ssize_t tegra_mc_enable_store(struct sysdev_class *class,
+ struct sysdev_class_attribute *attr,
+ const char *buf, size_t count)
+{
+ int value, i;
+ struct tegra_mc_counter *counters[] = {
+ &mc_counter0,
+ &mc_counter1,
+ &emc_llp_counter
+ };
+
+ sscanf(buf, "%d", &value);
+
+ if (value == 0 || value == 1)
+ sample_enable = value;
+ else
+ return -EINVAL;
+
+ if (!sample_enable) {
+ stat_stop();
+ hrtimer_cancel(&sample_timer);
+ return count;
+ }
+
+ hrtimer_cancel(&sample_timer);
+
+ /* we need to initialize variables that change during sampling */
+ sample_log_wptr = sample_log_rptr = sample_log;
+ sample_log_size = SAMPLE_LOG_SIZE - 1;
+
+ for (i = 0; i < ARRAY_SIZE(counters); i++) {
+ struct tegra_mc_counter *c = counters[i];
+
+ if (!c->enabled)
+ continue;
+
+ c->current_client_index = 0;
+ }
+
+ stat_start();
+
+ hrtimer_start(&sample_timer,
+ ktime_add_ns(ktime_get(), (u64)sample_quantum * 1000000),
+ HRTIMER_MODE_ABS);
+
+ return count;
+}
+
+static ssize_t tegra_mc_log_show(struct sysdev_class *class,
+ struct sysdev_class_attribute *attr, char *buf)
+{
+ int index = 0, count = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&sample_log_lock, flags);
+
+ while (sample_log_rptr != sample_log_wptr) {
+ if (sample_log_rptr < sample_log_wptr) {
+ count = sample_log_wptr - sample_log_rptr;
+ memcpy(buf + index, sample_log_rptr, count);
+ sample_log_rptr = sample_log_wptr;
+ sample_log_size += count;
+ } else {
+ count = SAMPLE_LOG_SIZE -
+ (sample_log_rptr - sample_log);
+ memcpy(buf + index, sample_log_rptr, count);
+ sample_log_rptr = sample_log;
+ sample_log_size += count;
+ }
+ index += count;
+ }
+
+ spin_unlock_irqrestore(&sample_log_lock, flags);
+
+ return index;
+}
+
+static ssize_t tegra_mc_log_store(struct sysdev_class *class,
+ struct sysdev_class_attribute *attr,
+ const char *buf, size_t count)
+{
+ return -EPERM;
+}
+
+static ssize_t tegra_mc_quantum_show(struct sysdev_class *class,
+ struct sysdev_class_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%d\n", sample_quantum);
+}
+
+static ssize_t tegra_mc_quantum_store(struct sysdev_class *class,
+ struct sysdev_class_attribute *attr,
+ const char *buf, size_t count)
+{
+ int value;
+
+ if (sampling())
+ return -EINVAL;
+
+ sscanf(buf, "%d", &value);
+ sample_quantum = value;
+
+ return count;
+}
+
+#define TEGRA_MC_EXPAND(_attr,_mode) \
+ static SYSDEV_CLASS_ATTR( \
+ _attr, _mode, tegra_mc_##_attr##_show, tegra_mc_##_attr##_store);
+
+#define TEGRA_MC_ATTRIBUTES(_attr1,_mode1,_attr2,_mode2,_attr3,_mode3) \
+ TEGRA_MC_EXPAND(_attr1,_mode1) \
+ TEGRA_MC_EXPAND(_attr2,_mode2) \
+ TEGRA_MC_EXPAND(_attr3,_mode3)
+
+TEGRA_MC_ATTRIBUTES(enable,0666,log,0444,quantum,0666)
+
+#undef TEGRA_MC_EXPAND
+
+#define TEGRA_MC_EXPAND(_attr,_mode) \
+ &attr_##_attr,
+
+static struct sysdev_class_attribute *tegra_mc_attrs[] = {
+ TEGRA_MC_ATTRIBUTES(enable,0666,log,0444,quantum,0666)
+ NULL
+};
+
+/* /sys/devices/system/tegra_mc/client */
+static bool tegra_mc_client_0_enabled = CLIENT_ENABLED_DEFAULT;
+static u8 tegra_mc_client_0_on_schedule_buffer[CLIENT_ON_SCHEDULE_LENGTH];
+static struct kobject *tegra_mc_client_kobj, *tegra_mc_client_0_kobj;
+
+struct match_mode {
+ const char *name;
+ int mode;
+};
+
+static const struct match_mode mode_list[] = {
+ [0] = {
+ .name = "none",
+ .mode = FILTER_NONE,
+ },
+ [1] = {
+ .name = "address",
+ .mode = FILTER_ADDR,
+ },
+ [2] = {
+ .name = "client",
+ .mode = FILTER_CLIENT,
+ },
+};
+
+static int tegra_mc_parse_mode(const char* str) {
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mode_list); i++) {
+ if (!strncmp(str, mode_list[i].name, strlen(mode_list[i].name)))
+ return mode_list[i].mode;
+ }
+ return -EINVAL;
+}
+
+static int tegra_mc_client_parse(const char *buf, size_t count,
+ tegra_mc_counter_t *counter0, tegra_mc_counter_t *counter1,
+ tegra_mc_counter_t *llp)
+{
+ char *options, *p, *ptr;
+ tegra_mc_counter_t *counter;
+ substring_t args[MAX_OPT_ARGS];
+ enum {
+ opt_period,
+ opt_mode,
+ opt_client,
+ opt_address_low,
+ opt_address_length,
+ opt_err,
+ };
+ const match_table_t tokens = {
+ {opt_period, "period=%s"},
+ {opt_mode, "mode=%s"},
+ {opt_client, "client=%s"},
+ {opt_address_low, "address_low=%s"},
+ {opt_address_length, "address_length=%s"},
+ {opt_err, NULL},
+ };
+ int ret = 0, i, token, index = 0;
+ bool aggregate = false;
+ int period, *client_ids, mode;
+ u64 address_low = 0;
+ u64 address_length = 1ull << 32;
+
+ client_ids = kmalloc(sizeof(int) * (MC_COUNTER_CLIENT_SIZE + 1),
+ GFP_KERNEL);
+ if (!client_ids)
+ return -ENOMEM;
+
+ memset(client_ids, -1, (sizeof(int) * (MC_COUNTER_CLIENT_SIZE + 1)));
+
+ options = kstrdup(buf, GFP_KERNEL);
+ if (!options) {
+ ret = -ENOMEM;
+ goto end;
+ }
+
+ while ((p = strsep(&options, " ")) != NULL) {
+ if (!*p)
+ continue;
+
+ pr_debug("\t %s\n", p);
+
+ token = match_token(p, tokens, args);
+ switch (token) {
+ case opt_period:
+ if (match_int(&args[0], &period) || period <= 0) {
+ ret = -EINVAL;
+ goto end;
+ }
+ break;
+
+ case opt_mode:
+ mode = tegra_mc_parse_mode(args[0].from);
+ if (mode < 0) {
+ ret = mode;
+ goto end;
+ }
+ break;
+
+ case opt_client:
+ ptr = get_options(args[0].from,
+ MC_COUNTER_CLIENT_SIZE + 1, client_ids);
+
+ if (client_ids[1] == MC_STAT_AGGREGATE) {
+ aggregate = true;
+ break;
+ }
+ break;
+
+ case opt_address_low:
+ address_low = simple_strtoull(args[0].from, NULL, 0);
+ break;
+
+ case opt_address_length:
+ address_length = simple_strtoull(args[0].from, NULL, 0);
+ break;
+
+ default:
+ ret = -EINVAL;
+ goto end;
+ }
+ }
+
+ address_low &= PAGE_MASK;
+ address_length += PAGE_SIZE - 1;
+ address_length &= ~((1ull << PAGE_SHIFT) - 1ull);
+
+ if (mode == FILTER_CLIENT) {
+ counter = counter0;
+ llp->enabled = false;
+ counter1->enabled = false;
+ } else if (mode == FILTER_ADDR || mode == FILTER_NONE) {
+ if (aggregate) {
+ counter = counter1;
+ llp->enabled = false;
+ counter0->enabled = false;
+ } else {
+ counter = counter0;
+ counter1->enabled = false;
+ llp->enabled = false;
+ }
+ } else {
+ ret = -EINVAL;
+ goto end;
+ }
+
+ counter->mode = mode;
+ counter->enabled = true;
+ counter->address_low = (u32)address_low;
+ counter->address_length = (u32)(address_length - 1);
+
+ for (i = 1; i < MC_COUNTER_CLIENT_SIZE; i++) {
+ if (client_ids[i] != -1)
+ counter->clients[index++] = client_ids[i];
+ }
+
+ counter->total_clients = index;
+
+ if (llp->enabled) {
+ llp->mode = counter->mode;
+ llp->period = counter->period;
+ llp->address_low = counter->address_low;
+ llp->address_length = counter->address_length;
+ }
+
+end:
+ if (options)
+ kfree(options);
+ if (client_ids)
+ kfree(client_ids);
+
+ return ret;
+}
+
+static ssize_t tegra_mc_client_0_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ if (strcmp(attr->attr.name, "enable") == 0)
+ return sprintf(buf, "%d\n", tegra_mc_client_0_enabled);
+ else if (strcmp(attr->attr.name, "on_schedule") == 0)
+ return sprintf(buf, "%s", tegra_mc_client_0_on_schedule_buffer);
+ else
+ return -EINVAL;
+}
+
+static ssize_t tegra_mc_client_0_store(struct kobject *kobj,
+ struct kobj_attribute *attr, const char *buf, size_t count)
+{
+ int value;
+
+ if (sampling())
+ return -EINVAL;
+
+ if (strcmp(attr->attr.name, "enable") == 0) {
+ sscanf(buf, "%d\n", &value);
+ if (value == 0 || value == 1)
+ tegra_mc_client_0_enabled = value;
+ else
+ return -EINVAL;
+
+ return count;
+ } else if (strcmp(attr->attr.name, "on_schedule") == 0) {
+ if (tegra_mc_client_parse(buf, count,
+ &mc_counter0, &mc_counter1,
+ &emc_llp_counter) == 0) {
+
+ strncpy(tegra_mc_client_0_on_schedule_buffer,
+ buf, count);
+
+ return count;
+ } else
+ return -EINVAL;
+ } else
+ return -EINVAL;
+}
+
+static struct kobj_attribute tegra_mc_client_0_enable =
+ __ATTR(enable, 0660, tegra_mc_client_0_show, tegra_mc_client_0_store);
+
+static struct kobj_attribute tegra_mc_client_0_on_schedule =
+ __ATTR(on_schedule, 0660, tegra_mc_client_0_show, tegra_mc_client_0_store);
+
+static struct attribute *tegra_mc_client_0_attrs[] = {
+ &tegra_mc_client_0_enable.attr,
+ &tegra_mc_client_0_on_schedule.attr,
+ NULL,
+};
+
+static struct attribute_group tegra_mc_client_0_attr_group = {
+ .attrs = tegra_mc_client_0_attrs
+};
+
+/* /sys/devices/system/tegra_mc/dram */
+#define dram_counters(_x) \
+ _x(activate_cnt, ACTIVATE_CNT) \
+ _x(read_cnt, READ_CNT) \
+ _x(write_cnt, WRITE_CNT) \
+ _x(ref_cnt, REF_CNT) \
+ _x(cumm_banks_active_cke_eq1, CUMM_BANKS_ACTIVE_CKE_EQ1) \
+ _x(cumm_banks_active_cke_eq0, CUMM_BANKS_ACTIVE_CKE_EQ0) \
+ _x(cke_eq1_clks, CKE_EQ1_CLKS) \
+ _x(extclks_cke_eq1, EXTCLKS_CKE_EQ1) \
+ _x(extclks_cke_eq0, EXTCLKS_CKE_EQ0) \
+ _x(no_banks_active_cke_eq1, NO_BANKS_ACTIVE_CKE_EQ1) \
+ _x(no_banks_active_cke_eq0, NO_BANKS_ACTIVE_CKE_EQ0)
+
+#define DEFINE_COUNTER(_name, _val) { .enabled = false, .device_mask = 0, },
+
+static tegra_emc_dram_counter_t dram_counters[] = {
+ dram_counters(DEFINE_COUNTER)
+};
+
+#define DEFINE_SYSFS(_name, _val) \
+ \
+static struct kobject *tegra_mc_dram_##_name##_kobj; \
+ \
+static ssize_t tegra_mc_dram_##_name##_show(struct kobject *kobj, \
+ struct kobj_attribute *attr, char *buf) \
+{ \
+ return tegra_mc_dram_show(kobj, attr, buf, \
+ _val - EMC_DRAM_STAT_BEGIN); \
+} \
+ \
+static ssize_t tegra_mc_dram_##_name##_store(struct kobject *kobj, \
+ struct kobj_attribute *attr, const char *buf, size_t count) \
+{ \
+ if (sampling()) \
+ return 0; \
+ \
+ return tegra_mc_dram_store(kobj, attr, buf, count, \
+ _val - EMC_DRAM_STAT_BEGIN); \
+} \
+ \
+ \
+static struct kobj_attribute tegra_mc_dram_##_name##_enable = \
+ __ATTR(enable, 0660, tegra_mc_dram_##_name##_show, \
+ tegra_mc_dram_##_name##_store); \
+ \
+static struct kobj_attribute tegra_mc_dram_##_name##_device_mask = \
+ __ATTR(device_mask, 0660, tegra_mc_dram_##_name##_show, \
+ tegra_mc_dram_##_name##_store); \
+ \
+static struct attribute *tegra_mc_dram_##_name##_attrs[] = { \
+ &tegra_mc_dram_##_name##_enable.attr, \
+ &tegra_mc_dram_##_name##_device_mask.attr, \
+ NULL, \
+}; \
+ \
+static struct attribute_group tegra_mc_dram_##_name##_attr_group = { \
+ .attrs = tegra_mc_dram_##_name##_attrs, \
+};
+
+static struct kobject *tegra_mc_dram_kobj;
+
+static ssize_t tegra_mc_dram_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf, int index)
+{
+ if (index >= EMC_DRAM_STAT_END - EMC_DRAM_STAT_BEGIN)
+ return -EINVAL;
+
+ if (strcmp(attr->attr.name, "enable") == 0)
+ return sprintf(buf, "%d\n", dram_counters[index].enabled);
+ else if (strcmp(attr->attr.name, "device_mask") == 0)
+ return sprintf(buf, "%d\n", dram_counters[index].device_mask);
+ else
+ return -EINVAL;
+}
+static ssize_t tegra_mc_dram_store(struct kobject *kobj,
+ struct kobj_attribute *attr, const char *buf, size_t count, int index)
+{
+ int value;
+
+ if (index >= EMC_DRAM_STAT_END - EMC_DRAM_STAT_BEGIN)
+ return -EINVAL;
+
+ if (strcmp(attr->attr.name, "enable") == 0) {
+ sscanf(buf, "%d\n", &value);
+ if (value == 0 || value == 1)
+ dram_counters[index].enabled = value;
+ else
+ return -EINVAL;
+
+ return count;
+ } else if (strcmp(attr->attr.name, "device_mask") == 0) {
+ sscanf(buf, "%d\n", &value);
+ dram_counters[index].device_mask = (u8)value;
+
+ return count;
+ } else
+ return -EINVAL;
+}
+
+dram_counters(DEFINE_SYSFS)
+
+/* Tegra Statistics */
+typedef struct {
+ void __iomem *mmio;
+} tegra_device_t;
+
+static tegra_device_t mc = {
+ .mmio = IO_ADDRESS(TEGRA_MC_BASE),
+};
+
+static tegra_device_t emc = {
+ .mmio = IO_ADDRESS(TEGRA_EMC_BASE),
+};
+
+void mc_stat_start(tegra_mc_counter_t *counter0, tegra_mc_counter_t *counter1)
+{
+ struct tegra_mc_counter *c;
+ u32 filter_client = ARMC_STAT_CONTROL_FILTER_CLIENT_DISABLE;
+ u32 filter_addr = ARMC_STAT_CONTROL_FILTER_ADDR_DISABLE;
+
+ if (!tegra_mc_client_0_enabled)
+ return;
+
+ c = (counter0->enabled) ? counter0 : counter1;
+
+ /* disable statistics */
+ writel((MC_STAT_CONTROL_0_EMC_GATHER_DISABLE << MC_STAT_CONTROL_0_EMC_GATHER_SHIFT),
+ mc.mmio + MC_STAT_CONTROL_0);
+
+ if (c->enabled && c->mode == FILTER_ADDR)
+ filter_addr = ARMC_STAT_CONTROL_FILTER_ADDR_ENABLE;
+ else if (c->enabled && c->mode == FILTER_CLIENT)
+ filter_client = ARMC_STAT_CONTROL_FILTER_CLIENT_ENABLE;
+
+ filter_addr <<= ARMC_STAT_CONTROL_FILTER_ADDR_SHIFT;
+ filter_client <<= ARMC_STAT_CONTROL_FILTER_CLIENT_SHIFT;
+
+ if (c->enabled) {
+ u32 reg = 0;
+ reg |= (ARMC_STAT_CONTROL_MODE_BANDWIDTH <<
+ ARMC_STAT_CONTROL_MODE_SHIFT);
+ reg |= (ARMC_STAT_CONTROL_EVENT_QUALIFIED <<
+ ARMC_STAT_CONTROL_EVENT_SHIFT);
+ reg |= (ARMC_STAT_CONTROL_FILTER_PRI_DISABLE <<
+ ARMC_STAT_CONTROL_FILTER_PRI_SHIFT);
+ reg |= (ARMC_STAT_CONTROL_FILTER_COALESCED_DISABLE <<
+ ARMC_STAT_CONTROL_FILTER_COALESCED_SHIFT);
+ reg |= filter_client;
+ reg |= filter_addr;
+ reg |= (c->clients[c->current_client_index] <<
+ ARMC_STAT_CONTROL_CLIENT_ID_SHIFT);
+
+ /* note these registers are shared */
+ writel(c->address_low,
+ mc.mmio + MC_STAT_EMC_ADDR_LOW_0);
+ writel((c->address_low + c->address_length),
+ mc.mmio + MC_STAT_EMC_ADDR_HIGH_0);
+ writel(0xFFFFFFFF, mc.mmio + MC_STAT_EMC_CLOCK_LIMIT_0);
+
+ writel(reg, mc.mmio + MC_STAT_EMC_CONTROL_0_0);
+ }
+
+ /* reset then enable statistics */
+ writel((MC_STAT_CONTROL_0_EMC_GATHER_CLEAR << MC_STAT_CONTROL_0_EMC_GATHER_SHIFT),
+ mc.mmio + MC_STAT_CONTROL_0);
+
+ writel((MC_STAT_CONTROL_0_EMC_GATHER_ENABLE << MC_STAT_CONTROL_0_EMC_GATHER_SHIFT),
+ mc.mmio + MC_STAT_CONTROL_0);
+}
+
+void mc_stat_stop(tegra_mc_counter_t *counter0,
+ tegra_mc_counter_t *counter1)
+{
+ u32 total_counts = readl(mc.mmio + MC_STAT_EMC_CLOCKS_0);
+
+ /* Disable statistics */
+ writel((MC_STAT_CONTROL_0_EMC_GATHER_DISABLE << MC_STAT_CONTROL_0_EMC_GATHER_SHIFT),
+ mc.mmio + MC_STAT_CONTROL_0);
+
+ if (counter0->enabled) {
+ counter0->sample_data.client_counts = readl(mc.mmio + MC_STAT_EMC_COUNT_0_0);
+ counter0->sample_data.total_counts = total_counts;
+ counter0->sample_data.emc_clock_rate = clk_get_rate(emc_clock);
+ }
+ else {
+ counter1->sample_data.client_counts = readl(mc.mmio + MC_STAT_EMC_COUNT_1_0);
+ counter1->sample_data.total_counts = total_counts;
+ counter1->sample_data.emc_clock_rate = clk_get_rate(emc_clock);
+ }
+}
+
+void emc_stat_start(tegra_mc_counter_t *llp_counter,
+ tegra_emc_dram_counter_t *dram_counter)
+{
+ u32 llmc_stat = 0;
+ u32 llmc_ctrl =
+ (AREMC_STAT_CONTROL_MODE_BANDWIDTH <<
+ AREMC_STAT_CONTROL_MODE_SHIFT) |
+ (AREMC_STAT_CONTROL_CLIENT_TYPE_MPCORER <<
+ AREMC_STAT_CONTROL_CLIENT_TYPE_SHIFT) |
+ (AREMC_STAT_CONTROL_EVENT_QUALIFIED <<
+ AREMC_STAT_CONTROL_EVENT_SHIFT);
+
+ /* disable statistics */
+ llmc_stat |= (EMC_STAT_CONTROL_0_LLMC_GATHER_DISABLE <<
+ EMC_STAT_CONTROL_0_LLMC_GATHER_SHIFT);
+ llmc_stat |= (EMC_STAT_CONTROL_0_DRAM_GATHER_DISABLE <<
+ EMC_STAT_CONTROL_0_DRAM_GATHER_SHIFT);
+ writel(llmc_stat, emc.mmio + EMC_STAT_CONTROL_0);
+
+ if (tegra_mc_client_0_enabled && llp_counter->enabled) {
+ if (llp_counter->mode == FILTER_ADDR) {
+ llmc_ctrl |=
+ (AREMC_STAT_CONTROL_FILTER_ADDR_ENABLE <<
+ AREMC_STAT_CONTROL_FILTER_ADDR_SHIFT);
+ llmc_ctrl |=
+ (AREMC_STAT_CONTROL_FILTER_CLIENT_DISABLE <<
+ AREMC_STAT_CONTROL_FILTER_CLIENT_SHIFT);
+ } else if (llp_counter->mode == FILTER_CLIENT) {
+ /* not allow aggregate client in client mode */
+ llmc_ctrl |=
+ (AREMC_STAT_CONTROL_FILTER_ADDR_DISABLE <<
+ AREMC_STAT_CONTROL_FILTER_ADDR_SHIFT);
+ llmc_ctrl |=
+ (AREMC_STAT_CONTROL_FILTER_CLIENT_DISABLE <<
+ AREMC_STAT_CONTROL_FILTER_CLIENT_SHIFT);
+ } else if (llp_counter->mode == FILTER_NONE) {
+ llmc_ctrl |=
+ (AREMC_STAT_CONTROL_FILTER_ADDR_DISABLE <<
+ AREMC_STAT_CONTROL_FILTER_ADDR_SHIFT);
+ llmc_ctrl |=
+ (AREMC_STAT_CONTROL_FILTER_CLIENT_DISABLE <<
+ AREMC_STAT_CONTROL_FILTER_CLIENT_SHIFT);
+ }
+
+ writel(llp_counter->address_low,
+ emc.mmio + EMC_STAT_LLMC_ADDR_LOW_0);
+ writel( (llp_counter->address_low + llp_counter->address_length),
+ emc.mmio + EMC_STAT_LLMC_ADDR_HIGH_0);
+ writel(0xFFFFFFFF, emc.mmio + EMC_STAT_LLMC_CLOCK_LIMIT_0);
+ writel(llmc_ctrl, emc.mmio + EMC_STAT_LLMC_CONTROL_0_0);
+ }
+
+ writel(0xFFFFFFFF, emc.mmio + EMC_STAT_DRAM_CLOCK_LIMIT_LO_0);
+ writel(0xFF, emc.mmio + EMC_STAT_DRAM_CLOCK_LIMIT_HI_0);
+
+ llmc_stat = 0;
+ /* Reset then enable statistics */
+ llmc_stat |= (EMC_STAT_CONTROL_0_LLMC_GATHER_CLEAR <<
+ EMC_STAT_CONTROL_0_LLMC_GATHER_SHIFT);
+ llmc_stat |= (EMC_STAT_CONTROL_0_DRAM_GATHER_CLEAR <<
+ EMC_STAT_CONTROL_0_DRAM_GATHER_SHIFT);
+ writel(llmc_stat, emc.mmio + EMC_STAT_CONTROL_0);
+
+ llmc_stat = 0;
+ llmc_stat |= (EMC_STAT_CONTROL_0_LLMC_GATHER_ENABLE <<
+ EMC_STAT_CONTROL_0_LLMC_GATHER_SHIFT);
+ llmc_stat |= (EMC_STAT_CONTROL_0_DRAM_GATHER_ENABLE <<
+ EMC_STAT_CONTROL_0_DRAM_GATHER_SHIFT);
+ writel(llmc_stat, emc.mmio + EMC_STAT_CONTROL_0);
+}
+
+void emc_stat_stop(tegra_mc_counter_t *llp_counter,
+ tegra_emc_dram_counter_t *dram_counter)
+{
+ u32 llmc_stat = 0;
+ int i;
+ int dev0_offsets_lo[] = {
+ EMC_STAT_DRAM_DEV0_ACTIVATE_CNT_LO_0,
+ EMC_STAT_DRAM_DEV0_READ_CNT_LO_0,
+ EMC_STAT_DRAM_DEV0_WRITE_CNT_LO_0,
+ EMC_STAT_DRAM_DEV0_REF_CNT_LO_0,
+ EMC_STAT_DRAM_DEV0_CUMM_BANKS_ACTIVE_CKE_EQ1_LO_0,
+ EMC_STAT_DRAM_DEV0_CUMM_BANKS_ACTIVE_CKE_EQ0_LO_0,
+ EMC_STAT_DRAM_DEV0_CKE_EQ1_CLKS_LO_0,
+ EMC_STAT_DRAM_DEV0_EXTCLKS_CKE_EQ1_LO_0,
+ EMC_STAT_DRAM_DEV0_EXTCLKS_CKE_EQ0_LO_0,
+ EMC_STAT_DRAM_DEV0_NO_BANKS_ACTIVE_CKE_EQ1_LO_0,
+ EMC_STAT_DRAM_DEV0_NO_BANKS_ACTIVE_CKE_EQ0_LO_0,
+ };
+ int dev0_offsets_hi[] = {
+ EMC_STAT_DRAM_DEV0_ACTIVATE_CNT_HI_0,
+ EMC_STAT_DRAM_DEV0_READ_CNT_HI_0,
+ EMC_STAT_DRAM_DEV0_WRITE_CNT_HI_0,
+ EMC_STAT_DRAM_DEV0_REF_CNT_HI_0,
+ EMC_STAT_DRAM_DEV0_CUMM_BANKS_ACTIVE_CKE_EQ1_HI_0,
+ EMC_STAT_DRAM_DEV0_CUMM_BANKS_ACTIVE_CKE_EQ0_HI_0,
+ EMC_STAT_DRAM_DEV0_CKE_EQ1_CLKS_HI_0,
+ EMC_STAT_DRAM_DEV0_EXTCLKS_CKE_EQ1_HI_0,
+ EMC_STAT_DRAM_DEV0_EXTCLKS_CKE_EQ0_HI_0,
+ EMC_STAT_DRAM_DEV0_NO_BANKS_ACTIVE_CKE_EQ1_HI_0,
+ EMC_STAT_DRAM_DEV0_NO_BANKS_ACTIVE_CKE_EQ0_HI_0,
+ };
+ int dev1_offsets_lo[] = {
+ EMC_STAT_DRAM_DEV1_ACTIVATE_CNT_LO_0,
+ EMC_STAT_DRAM_DEV1_READ_CNT_LO_0,
+ EMC_STAT_DRAM_DEV1_WRITE_CNT_LO_0,
+ EMC_STAT_DRAM_DEV1_REF_CNT_LO_0,
+ EMC_STAT_DRAM_DEV1_CUMM_BANKS_ACTIVE_CKE_EQ1_LO_0,
+ EMC_STAT_DRAM_DEV1_CUMM_BANKS_ACTIVE_CKE_EQ0_LO_0,
+ EMC_STAT_DRAM_DEV1_CKE_EQ1_CLKS_LO_0,
+ EMC_STAT_DRAM_DEV1_EXTCLKS_CKE_EQ1_LO_0,
+ EMC_STAT_DRAM_DEV1_EXTCLKS_CKE_EQ0_LO_0,
+ EMC_STAT_DRAM_DEV1_NO_BANKS_ACTIVE_CKE_EQ1_LO_0,
+ EMC_STAT_DRAM_DEV1_NO_BANKS_ACTIVE_CKE_EQ0_LO_0,
+ };
+ int dev1_offsets_hi[] = {
+ EMC_STAT_DRAM_DEV1_ACTIVATE_CNT_HI_0,
+ EMC_STAT_DRAM_DEV1_READ_CNT_HI_0,
+ EMC_STAT_DRAM_DEV1_WRITE_CNT_HI_0,
+ EMC_STAT_DRAM_DEV1_REF_CNT_HI_0,
+ EMC_STAT_DRAM_DEV1_CUMM_BANKS_ACTIVE_CKE_EQ1_HI_0,
+ EMC_STAT_DRAM_DEV1_CUMM_BANKS_ACTIVE_CKE_EQ0_HI_0,
+ EMC_STAT_DRAM_DEV1_CKE_EQ1_CLKS_HI_0,
+ EMC_STAT_DRAM_DEV1_EXTCLKS_CKE_EQ1_HI_0,
+ EMC_STAT_DRAM_DEV1_EXTCLKS_CKE_EQ0_HI_0,
+ EMC_STAT_DRAM_DEV1_NO_BANKS_ACTIVE_CKE_EQ1_HI_0,
+ EMC_STAT_DRAM_DEV1_NO_BANKS_ACTIVE_CKE_EQ0_HI_0,
+ };
+
+ /* Disable statistics */
+ llmc_stat |= (EMC_STAT_CONTROL_0_LLMC_GATHER_DISABLE <<
+ EMC_STAT_CONTROL_0_LLMC_GATHER_SHIFT);
+ llmc_stat |= (EMC_STAT_CONTROL_0_DRAM_GATHER_DISABLE <<
+ EMC_STAT_CONTROL_0_DRAM_GATHER_SHIFT);
+ writel(llmc_stat, emc.mmio + EMC_STAT_CONTROL_0);
+
+ if (tegra_mc_client_0_enabled == true && llp_counter->enabled) {
+ u32 total_counts = readl(mc.mmio + MC_STAT_EMC_CLOCKS_0);
+ llp_counter->sample_data.client_counts = readl(emc.mmio + EMC_STAT_LLMC_COUNT_0_0);
+ llp_counter->sample_data.total_counts = total_counts;
+ llp_counter->sample_data.emc_clock_rate = clk_get_rate(emc_clock);
+ }
+
+ for (i = 0; i < EMC_DRAM_STAT_END - EMC_DRAM_STAT_BEGIN; i++) {
+ if (dram_counter[i].enabled) {
+
+ dram_counter[i].sample_data.client_counts = 0;
+ dram_counter[i].sample_data.emc_clock_rate = clk_get_rate(emc_clock);
+
+ if (!(dram_counter[i].device_mask & 0x1)) {
+ if (readl(emc.mmio + dev0_offsets_hi[i]) != 0) {
+ dram_counter[i].sample_data.client_counts = 0xFFFFFFFF;
+ continue;
+ }
+ dram_counter[i].sample_data.client_counts +=
+ readl(emc.mmio + dev0_offsets_lo[i]);
+ }
+
+ if (!(dram_counter[i].device_mask & 0x2)) {
+ if (readl(emc.mmio + dev1_offsets_hi[i]) != 0) {
+ dram_counter[i].sample_data.client_counts = 0xFFFFFFFF;
+ continue;
+ }
+ dram_counter[i].sample_data.client_counts +=
+ readl(emc.mmio + dev1_offsets_lo[i]);
+ }
+ }
+ }
+}
+
+static void stat_start(void)
+{
+ mc_stat_start(&mc_counter0, &mc_counter1);
+ emc_stat_start(&emc_llp_counter, dram_counters);
+}
+
+static void stat_stop(void)
+{
+ mc_stat_stop(&mc_counter0, &mc_counter1);
+ emc_stat_stop(&emc_llp_counter, dram_counters);
+}
+
+#define statcpy(_buf, _bufstart, _buflen, _elem) \
+ do { \
+ size_t s = sizeof(_elem); \
+ memcpy(_buf, &_elem, s); \
+ _buf += s; \
+ if (_buf >= _bufstart + _buflen) \
+ _buf = _bufstart; \
+ } while (0);
+
+static void stat_log(void)
+{
+ int i;
+ unsigned long flags;
+
+ struct tegra_mc_counter *counters[] = {
+ &mc_counter0,
+ &mc_counter1,
+ &emc_llp_counter
+ };
+
+ spin_lock_irqsave(&sample_log_lock, flags);
+
+ if (tegra_mc_client_0_enabled) {
+ for (i = 0; i < ARRAY_SIZE(counters); i++) {
+ struct tegra_mc_counter *c = counters[i];
+
+ if (!c->enabled)
+ continue;
+
+ c->sample_data.client_number = c->clients[c->current_client_index];
+
+ c->current_client_index++;
+ if (c->current_client_index == c->total_clients)
+ c->current_client_index = 0;
+
+ statcpy(sample_log_wptr, sample_log,
+ SAMPLE_LOG_SIZE, c->sample_data);
+ }
+ }
+
+ for (i = 0; i < EMC_DRAM_STAT_END - EMC_DRAM_STAT_BEGIN; i++) {
+ if (dram_counters[i].enabled) {
+ statcpy(sample_log_wptr, sample_log,
+ SAMPLE_LOG_SIZE, dram_counters[i].sample_data);
+ }
+ }
+
+ spin_unlock_irqrestore(&sample_log_lock, flags);
+}
+
+static enum hrtimer_restart sample_timer_function(struct hrtimer *handle)
+{
+ stat_stop();
+ stat_log();
+
+ if (!sample_enable)
+ return HRTIMER_NORESTART;
+
+ stat_start();
+
+ hrtimer_add_expires_ns(&sample_timer, (u64)sample_quantum * 1000000);
+ return HRTIMER_RESTART;
+}
+
+/* module init */
+#define REGISTER_SYSFS(_name, _val) \
+ tegra_mc_dram_##_name##_kobj = \
+ kobject_create_and_add(#_name, tegra_mc_dram_kobj); \
+ sysfs_create_group(tegra_mc_dram_##_name##_kobj, \
+ &tegra_mc_dram_##_name##_attr_group);
+
+static int tegra_mc_init(void)
+{
+ int i;
+ int rc;
+
+ /* /sys/devices/system/tegra_mc */
+ rc = sysdev_class_register(&tegra_mc_sysclass);
+ if(rc)
+ goto out;
+
+ for (i = 0; i < ARRAY_SIZE(tegra_mc_attrs)-1; i++) {
+ rc = sysdev_class_create_file(&tegra_mc_sysclass,
+ tegra_mc_attrs[i]);
+ if(rc) {
+ printk("\n sysdev_class_create_file : failed \n");
+ goto out_unreg_class;
+ }
+ }
+
+ /* /sys/devices/system/tegra_mc/client */
+ tegra_mc_client_kobj = kobject_create_and_add("client",
+ &tegra_mc_sysclass.kset.kobj);
+ if(!tegra_mc_client_kobj)
+ goto out_remove_sysdev_files;
+
+ tegra_mc_client_0_kobj = kobject_create_and_add("0",
+ tegra_mc_client_kobj);
+ if(!tegra_mc_client_0_kobj)
+ goto out_put_kobject_client;
+
+ rc = sysfs_create_group(tegra_mc_client_0_kobj,
+ &tegra_mc_client_0_attr_group);
+ if(rc)
+ goto out_put_kobject_client_0;
+
+ /* /sys/devices/system/tegra_mc/dram */
+ tegra_mc_dram_kobj = kobject_create_and_add("dram",
+ &tegra_mc_sysclass.kset.kobj);
+ if(!tegra_mc_dram_kobj)
+ goto out_remove_group_client_0;
+
+ dram_counters(REGISTER_SYSFS)
+
+ /* hrtimer */
+ hrtimer_init(&sample_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
+ sample_timer.function = sample_timer_function;
+
+ for (i = 0; i < EMC_DRAM_STAT_END - EMC_DRAM_STAT_BEGIN; i++) {
+ dram_counters[i].sample_data.client_number = EMC_DRAM_STAT_BEGIN + i;
+ dram_counters[i].sample_data.signature = 0xdeadbeef;
+ }
+
+ emc_clock = clk_get_sys(NULL, "emc");
+ if (!emc_clock) {
+ pr_err("Could not get EMC clock\n");
+ goto out_remove_group_client_0;
+ }
+
+ return 0;
+
+out_remove_group_client_0:
+ sysfs_remove_group(tegra_mc_client_0_kobj, &tegra_mc_client_0_attr_group);
+
+out_put_kobject_client_0:
+ kobject_put(tegra_mc_client_0_kobj);
+
+out_put_kobject_client:
+ kobject_put(tegra_mc_client_kobj);
+
+out_remove_sysdev_files:
+ for (i = 0; i < ARRAY_SIZE(tegra_mc_attrs)-1; i++) {
+ sysdev_class_remove_file(&tegra_mc_sysclass, tegra_mc_attrs[i]);
+ }
+
+out_unreg_class:
+ sysdev_class_unregister(&tegra_mc_sysclass);
+
+out:
+ return rc;
+}
+
+/* module deinit */
+#define REMOVE_SYSFS(_name, _val) \
+ sysfs_remove_group(tegra_mc_dram_##_name##_kobj, \
+ &tegra_mc_dram_##_name##_attr_group); \
+ kobject_put(tegra_mc_dram_##_name##_kobj);
+
+static void tegra_mc_exit(void)
+{
+ int i;
+
+ stat_stop();
+
+ /* hrtimer */
+ hrtimer_cancel(&sample_timer);
+
+ /* /sys/devices/system/tegra_mc/client */
+ sysfs_remove_group(tegra_mc_client_0_kobj,
+ &tegra_mc_client_0_attr_group);
+ kobject_put(tegra_mc_client_0_kobj);
+ kobject_put(tegra_mc_client_kobj);
+
+ /* /sys/devices/system/tegra_mc/dram */
+ dram_counters(REMOVE_SYSFS)
+ kobject_put(tegra_mc_dram_kobj);
+
+ /* /sys/devices/system/tegra_mc */
+ for (i = 0; i < ARRAY_SIZE(tegra_mc_attrs)-1; i++) {
+ sysdev_class_remove_file(&tegra_mc_sysclass, tegra_mc_attrs[i]);
+ }
+ sysdev_class_unregister(&tegra_mc_sysclass);
+}
+
+module_init(tegra_mc_init);
+module_exit(tegra_mc_exit);
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/arch/arm/mach-tegra/tegra2_mc.h b/arch/arm/mach-tegra/tegra2_mc.h
new file mode 100644
index 000000000000..211213c5f585
--- /dev/null
+++ b/arch/arm/mach-tegra/tegra2_mc.h
@@ -0,0 +1,250 @@
+/*
+ * arch/arm/mach-tegra/tegra2_mc.c
+ *
+ * Memory controller bandwidth profiling interface
+ *
+ * Copyright (c) 2009-2011, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _INCLUDE_TEGRA2_MC_H_
+#define _INCLUDE_TEGRA2_MC_H_
+
+#define SAMPLE_ENABLE_DEFAULT 0
+#define SAMPLE_LOG_SIZE 1024 /* need to be DWORD aligned */
+#define SAMPLE_QUANTUM_DEFAULT 1 /* in milliseconds */
+#define CLIENT_ENABLED_DEFAULT false
+#define CLIENT_ON_SCHEDULE_LENGTH 256
+#define SHIFT_4K 12
+
+typedef enum {
+ FILTER_NONE,
+ FILTER_ADDR,
+ FILTER_CLIENT,
+} FILTER_MODE;
+
+#define MC_COUNTER_CLIENT_SIZE 256
+
+#define MC_STAT_CONTROL_0 0x90
+#define MC_STAT_CONTROL_0_EMC_GATHER_SHIFT 8
+#define MC_STAT_CONTROL_0_EMC_GATHER_CLEAR 1
+#define MC_STAT_CONTROL_0_EMC_GATHER_DISABLE 2
+#define MC_STAT_CONTROL_0_EMC_GATHER_ENABLE 3
+
+#define MC_STAT_EMC_ADDR_LOW_0 0x98
+#define MC_STAT_EMC_ADDR_HIGH_0 0x9c
+#define MC_STAT_EMC_CLOCK_LIMIT_0 0xa0
+#define MC_STAT_EMC_CLOCKS_0 0xa4
+#define MC_STAT_EMC_CONTROL_0_0 0xa8
+#define MC_STAT_EMC_COUNT_0_0 0xb8
+#define MC_STAT_EMC_COUNT_1_0 0xbc
+
+#define ARMC_STAT_CONTROL_FILTER_ADDR_SHIFT 27
+#define ARMC_STAT_CONTROL_FILTER_ADDR_DISABLE 0
+#define ARMC_STAT_CONTROL_FILTER_ADDR_ENABLE 1
+#define ARMC_STAT_CONTROL_FILTER_CLIENT_SHIFT 26
+#define ARMC_STAT_CONTROL_FILTER_CLIENT_DISABLE 0
+#define ARMC_STAT_CONTROL_FILTER_CLIENT_ENABLE 1
+#define ARMC_STAT_CONTROL_FILTER_PRI_SHIFT 28
+#define ARMC_STAT_CONTROL_FILTER_PRI_DISABLE 0
+#define ARMC_STAT_CONTROL_FILTER_COALESCED_SHIFT 30
+#define ARMC_STAT_CONTROL_FILTER_COALESCED_DISABLE 0
+#define ARMC_STAT_CONTROL_CLIENT_ID_SHIFT 8
+#define ARMC_STAT_CONTROL_MODE_SHIFT 0
+#define ARMC_STAT_CONTROL_MODE_BANDWIDTH 0
+#define ARMC_STAT_CONTROL_EVENT_SHIFT 16
+#define ARMC_STAT_CONTROL_EVENT_QUALIFIED 0
+
+#define EMC_STAT_CONTROL_0 0x160
+#define EMC_STAT_CONTROL_0_LLMC_GATHER_SHIFT 0
+#define EMC_STAT_CONTROL_0_LLMC_GATHER_CLEAR 1
+#define EMC_STAT_CONTROL_0_LLMC_GATHER_DISABLE 2
+#define EMC_STAT_CONTROL_0_LLMC_GATHER_ENABLE 3
+#define EMC_STAT_CONTROL_0_DRAM_GATHER_SHIFT 16
+#define EMC_STAT_CONTROL_0_DRAM_GATHER_CLEAR 1
+#define EMC_STAT_CONTROL_0_DRAM_GATHER_DISABLE 2
+#define EMC_STAT_CONTROL_0_DRAM_GATHER_ENABLE 3
+
+#define AREMC_STAT_CONTROL_MODE_SHIFT 0
+#define AREMC_STAT_CONTROL_MODE_BANDWIDTH 0
+#define AREMC_STAT_CONTROL_FILTER_ADDR_SHIFT 27
+#define AREMC_STAT_CONTROL_FILTER_ADDR_ENABLE 1
+#define AREMC_STAT_CONTROL_CLIENT_TYPE_SHIFT 8
+#define AREMC_STAT_CONTROL_CLIENT_TYPE_MPCORER 0
+#define AREMC_STAT_CONTROL_FILTER_CLIENT_SHIFT 26
+#define AREMC_STAT_CONTROL_FILTER_CLIENT_DISABLE 0
+#define AREMC_STAT_CONTROL_FILTER_ADDR_DISABLE 0
+#define AREMC_STAT_CONTROL_EVENT_SHIFT 16
+#define AREMC_STAT_CONTROL_EVENT_QUALIFIED 0
+
+#define EMC_STAT_LLMC_ADDR_LOW_0 0x168
+#define EMC_STAT_LLMC_ADDR_HIGH_0 0x16c
+#define EMC_STAT_LLMC_CLOCK_LIMIT_0 0x170
+#define EMC_STAT_LLMC_CONTROL_0_0 0x178
+#define EMC_STAT_LLMC_COUNT_0_0 0x188
+
+#define EMC_STAT_DRAM_CLOCK_LIMIT_LO_0 0x1a4
+#define EMC_STAT_DRAM_CLOCK_LIMIT_HI_0 0x1a8
+#define EMC_STAT_DRAM_DEV0_ACTIVATE_CNT_LO_0 0x1b4
+#define EMC_STAT_DRAM_DEV0_ACTIVATE_CNT_HI_0 0x1b8
+#define EMC_STAT_DRAM_DEV0_READ_CNT_LO_0 0x1bc
+#define EMC_STAT_DRAM_DEV0_READ_CNT_HI_0 0x1c0
+#define EMC_STAT_DRAM_DEV0_WRITE_CNT_LO_0 0x1c4
+#define EMC_STAT_DRAM_DEV0_WRITE_CNT_HI_0 0x1c8
+#define EMC_STAT_DRAM_DEV0_REF_CNT_LO_0 0x1cc
+#define EMC_STAT_DRAM_DEV0_REF_CNT_HI_0 0x1d0
+#define EMC_STAT_DRAM_DEV0_CUMM_BANKS_ACTIVE_CKE_EQ1_LO_0 0x1d4
+#define EMC_STAT_DRAM_DEV0_CUMM_BANKS_ACTIVE_CKE_EQ1_HI_0 0x1d8
+#define EMC_STAT_DRAM_DEV0_CUMM_BANKS_ACTIVE_CKE_EQ0_LO_0 0x1dc
+#define EMC_STAT_DRAM_DEV0_CUMM_BANKS_ACTIVE_CKE_EQ0_HI_0 0x1e0
+#define EMC_STAT_DRAM_DEV0_CKE_EQ1_CLKS_LO_0 0x1e4
+#define EMC_STAT_DRAM_DEV0_CKE_EQ1_CLKS_HI_0 0x1e8
+#define EMC_STAT_DRAM_DEV0_EXTCLKS_CKE_EQ1_LO_0 0x1ec
+#define EMC_STAT_DRAM_DEV0_EXTCLKS_CKE_EQ1_HI_0 0x1f0
+#define EMC_STAT_DRAM_DEV0_EXTCLKS_CKE_EQ0_LO_0 0x1f4
+#define EMC_STAT_DRAM_DEV0_EXTCLKS_CKE_EQ0_HI_0 0x1f8
+#define EMC_STAT_DRAM_DEV1_ACTIVATE_CNT_LO_0 0x1fc
+#define EMC_STAT_DRAM_DEV1_ACTIVATE_CNT_HI_0 0x200
+#define EMC_STAT_DRAM_DEV1_READ_CNT_LO_0 0x204
+#define EMC_STAT_DRAM_DEV1_READ_CNT_HI_0 0x208
+#define EMC_STAT_DRAM_DEV1_WRITE_CNT_LO_0 0x20c
+#define EMC_STAT_DRAM_DEV1_WRITE_CNT_HI_0 0x210
+#define EMC_STAT_DRAM_DEV1_REF_CNT_LO_0 0x214
+#define EMC_STAT_DRAM_DEV1_REF_CNT_HI_0 0x218
+#define EMC_STAT_DRAM_DEV1_CUMM_BANKS_ACTIVE_CKE_EQ1_LO_0 0x21c
+#define EMC_STAT_DRAM_DEV1_CUMM_BANKS_ACTIVE_CKE_EQ1_HI_0 0x220
+#define EMC_STAT_DRAM_DEV1_CUMM_BANKS_ACTIVE_CKE_EQ0_LO_0 0x224
+#define EMC_STAT_DRAM_DEV1_CUMM_BANKS_ACTIVE_CKE_EQ0_HI_0 0x228
+#define EMC_STAT_DRAM_DEV1_CKE_EQ1_CLKS_LO_0 0x22c
+#define EMC_STAT_DRAM_DEV1_CKE_EQ1_CLKS_HI_0 0x230
+#define EMC_STAT_DRAM_DEV1_EXTCLKS_CKE_EQ1_LO_0 0x234
+#define EMC_STAT_DRAM_DEV1_EXTCLKS_CKE_EQ1_HI_0 0x238
+#define EMC_STAT_DRAM_DEV1_EXTCLKS_CKE_EQ0_LO_0 0x23c
+#define EMC_STAT_DRAM_DEV1_EXTCLKS_CKE_EQ0_HI_0 0x240
+#define EMC_STAT_DRAM_DEV0_NO_BANKS_ACTIVE_CKE_EQ1_LO_0 0x244
+#define EMC_STAT_DRAM_DEV0_NO_BANKS_ACTIVE_CKE_EQ1_HI_0 0x248
+#define EMC_STAT_DRAM_DEV0_NO_BANKS_ACTIVE_CKE_EQ0_LO_0 0x24c
+#define EMC_STAT_DRAM_DEV0_NO_BANKS_ACTIVE_CKE_EQ0_HI_0 0x250
+#define EMC_STAT_DRAM_DEV1_NO_BANKS_ACTIVE_CKE_EQ1_LO_0 0x254
+#define EMC_STAT_DRAM_DEV1_NO_BANKS_ACTIVE_CKE_EQ1_HI_0 0x258
+#define EMC_STAT_DRAM_DEV1_NO_BANKS_ACTIVE_CKE_EQ0_LO_0 0x25c
+#define EMC_STAT_DRAM_DEV1_NO_BANKS_ACTIVE_CKE_EQ0_HI_0 0x260
+
+#pragma pack(push)
+#pragma pack(1)
+
+typedef struct {
+ u32 signature;
+ u32 client_number;
+ u32 client_counts;
+ u32 total_counts;
+ u32 emc_clock_rate;
+} sample_data_t;
+
+#pragma pack(pop)
+
+typedef struct tegra_mc_counter {
+ bool enabled;
+ u32 period;
+ FILTER_MODE mode;
+ u32 address_low;
+ u32 address_length;
+ u32 current_client_index;
+ u32 total_clients;
+ u8 clients[MC_COUNTER_CLIENT_SIZE];
+ sample_data_t sample_data;
+} tegra_mc_counter_t;
+
+typedef struct tegra_emc_dram_counter {
+ bool enabled;
+ u8 device_mask;
+
+ sample_data_t sample_data;
+} tegra_emc_dram_counter_t;
+
+/* client ids of mc/emc */
+typedef enum {
+ MC_STAT_BEGIN = 0,
+ CBR_DISPLAY0A = 0,
+ CBR_DISPLAY0AB,
+ CBR_DISPLAY0B,
+ CBR_DISPLAY0BB,
+ CBR_DISPLAY0C,
+ CBR_DISPLAY0CB,
+ CBR_DISPLAY1B,
+ CBR_DISPLAY1BB,
+ CBR_EPPUP,
+ CBR_G2PR,
+ CBR_G2SR,
+ CBR_MPEUNIFBR,
+ CBR_VIRUV,
+ CSR_AVPCARM7R,
+ CSR_DISPLAYHC,
+ CSR_DISPLAYHCB,
+ CSR_FDCDRD,
+ CSR_G2DR,
+ CSR_HOST1XDMAR,
+ CSR_HOST1XR,
+ CSR_IDXSRD,
+ CSR_MPCORER,
+ CSR_MPE_IPRED,
+ CSR_MPEAMEMRD,
+ CSR_MPECSRD,
+ CSR_PPCSAHBDMAR,
+ CSR_PPCSAHBSLVR,
+ CSR_TEXSRD,
+ CSR_VDEBSEVR,
+ CSR_VDEMBER,
+ CSR_VDEMCER,
+ CSR_VDETPER,
+ CBW_EPPU,
+ CBW_EPPV,
+ CBW_EPPY,
+ CBW_MPEUNIFBW,
+ CBW_VIWSB,
+ CBW_VIWU,
+ CBW_VIWV,
+ CBW_VIWY,
+ CCW_G2DW,
+ CSW_AVPCARM7W,
+ CSW_FDCDWR,
+ CSW_HOST1XW,
+ CSW_ISPW,
+ CSW_MPCOREW,
+ CSW_MPECSWR,
+ CSW_PPCSAHBDMAW,
+ CSW_PPCSAHBSLVW,
+ CSW_VDEBSEVW,
+ CSW_VDEMBEW,
+ CSW_VDETPMW,
+ MC_STAT_END,
+ EMC_DRAM_STAT_BEGIN = 128,
+ ACTIVATE_CNT = 128,
+ READ_CNT,
+ WRITE_CNT,
+ REF_CNT,
+ CUMM_BANKS_ACTIVE_CKE_EQ1,
+ CUMM_BANKS_ACTIVE_CKE_EQ0,
+ CKE_EQ1_CLKS,
+ EXTCLKS_CKE_EQ1,
+ EXTCLKS_CKE_EQ0,
+ NO_BANKS_ACTIVE_CKE_EQ1,
+ NO_BANKS_ACTIVE_CKE_EQ0,
+ EMC_DRAM_STAT_END,
+ MC_STAT_AGGREGATE = 255,
+} device_id;
+
+#endif
diff --git a/arch/arm/mach-tegra/tegra2_speedo.c b/arch/arm/mach-tegra/tegra2_speedo.c
new file mode 100644
index 000000000000..1e5fa26a5c41
--- /dev/null
+++ b/arch/arm/mach-tegra/tegra2_speedo.c
@@ -0,0 +1,140 @@
+/*
+ * arch/arm/mach-tegra/tegra2_speedo.c
+ *
+ * Copyright (c) 2010, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/err.h>
+
+#include <mach/iomap.h>
+
+#include "fuse.h"
+
+#define CPU_SPEEDO_LSBIT 20
+#define CPU_SPEEDO_MSBIT 29
+#define CPU_SPEEDO_REDUND_LSBIT 30
+#define CPU_SPEEDO_REDUND_MSBIT 39
+#define CPU_SPEEDO_REDUND_OFFS (CPU_SPEEDO_REDUND_MSBIT - CPU_SPEEDO_MSBIT)
+
+#define CORE_SPEEDO_LSBIT 40
+#define CORE_SPEEDO_MSBIT 47
+#define CORE_SPEEDO_REDUND_LSBIT 48
+#define CORE_SPEEDO_REDUND_MSBIT 55
+#define CORE_SPEEDO_REDUND_OFFS (CORE_SPEEDO_REDUND_MSBIT - CORE_SPEEDO_MSBIT)
+
+#define SPEEDO_MULT 4
+
+#define CHIP_ID 0x804
+#define CHIP_MINOR_SHIFT 16
+#define CHIP_MINOR_MASK (0xF << CHIP_MINOR_SHIFT)
+
+#define PROCESS_CORNERS_NUM 4
+
+#define SPEEDO_ID_SELECT_0(rev) ((rev) <= 2)
+#define SPEEDO_ID_SELECT_1(sku) \
+ (((sku) != 20) && ((sku) != 23) && ((sku) != 24) && \
+ ((sku) != 27) && ((sku) != 28))
+
+/* Maximum speedo levels for each CPU process corner */
+static const u32 cpu_process_speedos[][PROCESS_CORNERS_NUM] = {
+/* proc_id 0 1 2 3 */
+ {315, 366, 420, UINT_MAX}, /* speedo_id 0 */
+ {303, 368, 419, UINT_MAX}, /* speedo_id 1 */
+ {316, 331, 383, UINT_MAX}, /* speedo_id 2 */
+};
+
+/* Maximum speedo levels for each core process corner */
+static const u32 core_process_speedos[][PROCESS_CORNERS_NUM] = {
+/* proc_id 0 1 2 3 */
+ {165, 195, 224, UINT_MAX}, /* speedo_id 0 */
+ {165, 195, 224, UINT_MAX}, /* speedo_id 1 */
+ {165, 195, 224, UINT_MAX}, /* speedo_id 2 */
+};
+
+static int cpu_process_id;
+static int core_process_id;
+static int soc_speedo_id;
+
+void tegra_init_speedo_data(void)
+{
+ u32 reg, val;
+ int i, bit, rev;
+ int sku = tegra_sku_id();
+ void __iomem *apb_misc = IO_ADDRESS(TEGRA_APB_MISC_BASE);
+
+ reg = readl(apb_misc + CHIP_ID);
+ rev = (reg & CHIP_MINOR_MASK) >> CHIP_MINOR_SHIFT;
+ if (SPEEDO_ID_SELECT_0(rev))
+ soc_speedo_id = 0;
+ else if (SPEEDO_ID_SELECT_1(sku))
+ soc_speedo_id = 1;
+ else
+ soc_speedo_id = 2;
+ BUG_ON(soc_speedo_id >= ARRAY_SIZE(cpu_process_speedos));
+ BUG_ON(soc_speedo_id >= ARRAY_SIZE(core_process_speedos));
+
+ val = 0;
+ for (bit = CPU_SPEEDO_MSBIT; bit >= CPU_SPEEDO_LSBIT; bit--) {
+ reg = tegra_spare_fuse(bit) |
+ tegra_spare_fuse(bit + CPU_SPEEDO_REDUND_OFFS);
+ val = (val << 1) | (reg & 0x1);
+ }
+ val = val * SPEEDO_MULT;
+ pr_debug("%s CPU speedo level %u\n", __func__, val);
+
+ for (i = 0; i < (PROCESS_CORNERS_NUM - 1); i++) {
+ if (val <= cpu_process_speedos[soc_speedo_id][i])
+ break;
+ }
+ cpu_process_id = i;
+
+ val = 0;
+ for (bit = CORE_SPEEDO_MSBIT; bit >= CORE_SPEEDO_LSBIT; bit--) {
+ reg = tegra_spare_fuse(bit) |
+ tegra_spare_fuse(bit + CORE_SPEEDO_REDUND_OFFS);
+ val = (val << 1) | (reg & 0x1);
+ }
+ val = val * SPEEDO_MULT;
+ pr_debug("%s Core speedo level %u\n", __func__, val);
+
+ for (i = 0; i < (PROCESS_CORNERS_NUM - 1); i++) {
+ if (val <= core_process_speedos[soc_speedo_id][i])
+ break;
+ }
+ core_process_id = i;
+
+ pr_info("Tegra SKU: %d Rev: A%.2d CPU Process: %d Core Process: %d"
+ " Speedo ID: %d\n", sku, rev, cpu_process_id, core_process_id,
+ soc_speedo_id);
+}
+
+int tegra_cpu_process_id(void)
+{
+ return cpu_process_id;
+}
+
+int tegra_core_process_id(void)
+{
+ return core_process_id;
+}
+
+int tegra_soc_speedo_id(void)
+{
+ return soc_speedo_id;
+}
diff --git a/arch/arm/mach-tegra/tegra2_statmon.c b/arch/arm/mach-tegra/tegra2_statmon.c
new file mode 100644
index 000000000000..92f9b883f93d
--- /dev/null
+++ b/arch/arm/mach-tegra/tegra2_statmon.c
@@ -0,0 +1,440 @@
+/*
+ * arch/arm/mach-tegra/tegra2_statmon.c
+ *
+ * Copyright (c) 2011, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/sysdev.h>
+#include <linux/bitops.h>
+
+#include <mach/iomap.h>
+#include <mach/irqs.h>
+#include <mach/io.h>
+#include <mach/clk.h>
+
+#include "clock.h"
+#include "tegra2_statmon.h"
+
+#define COP_MON_CTRL 0x120
+#define COP_MON_STATUS 0x124
+
+#define SAMPLE_PERIOD_SHIFT 20
+#define SAMPLE_PERIOD_MASK (0xFF << SAMPLE_PERIOD_SHIFT)
+#define INT_STATUS BIT(29) /* write 1 to clear */
+#define INT_ENABLE BIT(30)
+#define MON_ENABLE BIT(31)
+
+#define WINDOW_SIZE 128
+#define FREQ_MULT 1000
+#define UPPER_BAND 1000
+#define LOWER_BAND 1000
+#define BOOST_FRACTION_BITS 8
+
+struct sampler {
+ struct clk *clock;
+ unsigned long active_cycles[WINDOW_SIZE];
+ unsigned long total_active_cycles;
+ unsigned long avg_freq;
+ unsigned long *last_sample;
+ unsigned long idle_cycles;
+ unsigned long boost_freq;
+ unsigned long bumped_freq;
+ unsigned long *table;
+ int table_size;
+ u32 sample_count;
+ bool enable;
+ int sample_time;
+ int window_ms;
+ int min_samples;
+ unsigned long boost_step;
+ u8 boost_inc_coef;
+ u8 boost_dec_coef;
+};
+
+struct tegra2_stat_mon {
+ void __iomem *stat_mon_base;
+ void __iomem *vde_mon_base;
+ struct clk *stat_mon_clock;
+ struct mutex stat_mon_lock;
+ struct sampler avp_sampler;
+};
+
+static unsigned long sclk_table[] = {
+ 300000,
+ 240000,
+ 200000,
+ 150000,
+ 120000,
+ 100000,
+ 80000,
+ 75000,
+ 60000,
+ 50000,
+ 48000,
+ 40000
+};
+
+static struct tegra2_stat_mon *stat_mon;
+
+static inline u32 tegra2_stat_mon_read(u32 offset)
+{
+ return readl(stat_mon->stat_mon_base + offset);
+}
+
+static inline void tegra2_stat_mon_write(u32 value, u32 offset)
+{
+ writel(value, stat_mon->stat_mon_base + offset);
+}
+
+static inline u32 tegra2_vde_mon_read(u32 offset)
+{
+ return readl(stat_mon->vde_mon_base + offset);
+}
+
+static inline void tegra2_vde_mon_write(u32 value, u32 offset)
+{
+ writel(value, stat_mon->vde_mon_base + offset);
+}
+
+/* read the ticks in ISR and store */
+static irqreturn_t stat_mon_isr(int irq, void *data)
+{
+ u32 reg_val;
+
+ /* disable AVP monitor */
+ reg_val = tegra2_stat_mon_read(COP_MON_CTRL);
+ reg_val |= INT_STATUS;
+ tegra2_stat_mon_write(reg_val, COP_MON_CTRL);
+
+ stat_mon->avp_sampler.idle_cycles =
+ tegra2_stat_mon_read(COP_MON_STATUS);
+
+ return IRQ_WAKE_THREAD;
+}
+
+
+static void add_active_sample(struct sampler *s, unsigned long cycles)
+{
+ if (s->last_sample == &s->active_cycles[WINDOW_SIZE - 1])
+ s->last_sample = &s->active_cycles[0];
+ else
+ s->last_sample++;
+
+ s->total_active_cycles -= *s->last_sample;
+ *s->last_sample = cycles;
+ s->total_active_cycles += *s->last_sample;
+}
+
+static unsigned long round_rate(struct sampler *s, unsigned long rate)
+{
+ int i;
+ unsigned long *table = s->table;
+
+ if (rate >= table[0])
+ return table[0];
+
+ for (i = 1; i < s->table_size; i++) {
+ if (rate <= table[i])
+ continue;
+ else {
+ return table[i-1];
+ break;
+ }
+ }
+ if (rate <= table[s->table_size - 1])
+ return table[s->table_size - 1];
+ return rate;
+}
+
+static void set_target_freq(struct sampler *s)
+{
+ unsigned long clock_rate;
+ unsigned long target_freq;
+ unsigned long active_count;
+
+ clock_rate = clk_get_rate(s->clock) / FREQ_MULT;
+ active_count = (s->sample_time + 1) * clock_rate;
+ active_count = (active_count > s->idle_cycles) ?
+ (active_count - s->idle_cycles) : (0);
+
+ s->sample_count++;
+
+ add_active_sample(s, active_count);
+
+ s->avg_freq = s->total_active_cycles / s->window_ms;
+
+ if ((s->idle_cycles >= (1 + (active_count >> 3))) &&
+ (s->bumped_freq >= s->avg_freq)) {
+ s->boost_freq = (s->boost_freq *
+ ((0x1 << BOOST_FRACTION_BITS) - s->boost_dec_coef))
+ >> BOOST_FRACTION_BITS;
+ if (s->boost_freq < s->boost_step)
+ s->boost_freq = 0;
+ } else if (s->sample_count < s->min_samples) {
+ s->sample_count++;
+ } else {
+ s->boost_freq = ((s->boost_freq *
+ ((0x1 << BOOST_FRACTION_BITS) + s->boost_inc_coef))
+ >> BOOST_FRACTION_BITS) + s->boost_step;
+ if (s->boost_freq > s->clock->max_rate)
+ s->boost_freq = s->clock->max_rate;
+ }
+
+ if ((s->avg_freq + LOWER_BAND) < s->bumped_freq)
+ s->bumped_freq = s->avg_freq + LOWER_BAND;
+ else if (s->avg_freq > (s->bumped_freq + UPPER_BAND))
+ s->bumped_freq = s->avg_freq - UPPER_BAND;
+
+ s->bumped_freq += (s->bumped_freq >> 3);
+
+ target_freq = max(s->bumped_freq, s->clock->min_rate);
+ target_freq += s->boost_freq;
+
+ active_count = target_freq;
+ target_freq = round_rate(s, target_freq) * FREQ_MULT;
+ clk_set_rate(s->clock, target_freq);
+}
+
+/* - process ticks in thread context
+ */
+static irqreturn_t stat_mon_isr_thread_fn(int irq, void *data)
+{
+ u32 reg_val = 0;
+
+ mutex_lock(&stat_mon->stat_mon_lock);
+ set_target_freq(&stat_mon->avp_sampler);
+ mutex_unlock(&stat_mon->stat_mon_lock);
+
+ /* start AVP sampler */
+ reg_val = tegra2_stat_mon_read(COP_MON_CTRL);
+ reg_val |= MON_ENABLE;
+ tegra2_stat_mon_write(reg_val, COP_MON_CTRL);
+ return IRQ_HANDLED;
+}
+
+void tegra2_statmon_stop(void)
+{
+ u32 reg_val = 0;
+
+ /* disable AVP monitor */
+ reg_val |= INT_STATUS;
+ tegra2_stat_mon_write(reg_val, COP_MON_CTRL);
+
+ clk_disable(stat_mon->stat_mon_clock);
+ clk_disable(stat_mon->avp_sampler.clock);
+}
+
+int tegra2_statmon_start(void)
+{
+ u32 reg_val = 0;
+
+ clk_enable(stat_mon->avp_sampler.clock);
+ clk_enable(stat_mon->stat_mon_clock);
+
+ /* disable AVP monitor */
+ reg_val |= INT_STATUS;
+ tegra2_stat_mon_write(reg_val, COP_MON_CTRL);
+
+ /* start AVP sampler. also enable INT to CPU */
+ reg_val = 0;
+ reg_val |= MON_ENABLE;
+ reg_val |= INT_ENABLE;
+ reg_val |= ((stat_mon->avp_sampler.sample_time \
+ << SAMPLE_PERIOD_SHIFT) & SAMPLE_PERIOD_MASK);
+ tegra2_stat_mon_write(reg_val, COP_MON_CTRL);
+ return 0;
+}
+
+static ssize_t tegra2_statmon_enable_show(struct sysdev_class *class,
+ struct sysdev_class_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%d\n", stat_mon->avp_sampler.enable);
+}
+
+static ssize_t tegra2_statmon_enable_store(struct sysdev_class *class,
+ struct sysdev_class_attribute *attr, const char *buf, size_t count)
+{
+ int value;
+
+ mutex_lock(&stat_mon->stat_mon_lock);
+ sscanf(buf, "%d", &value);
+
+ if (value == 0 || value == 1)
+ stat_mon->avp_sampler.enable = value;
+ else {
+ mutex_unlock(&stat_mon->stat_mon_lock);
+ return -EINVAL;
+ }
+ mutex_unlock(&stat_mon->stat_mon_lock);
+
+ if (stat_mon->avp_sampler.enable)
+ tegra2_statmon_start();
+ else
+ tegra2_statmon_stop();
+
+ return 0;
+}
+
+static ssize_t tegra2_statmon_sample_time_show(struct sysdev_class *class,
+ struct sysdev_class_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%d\n", stat_mon->avp_sampler.sample_time);
+}
+
+static ssize_t tegra2_statmon_sample_time_store(struct sysdev_class *class,
+ struct sysdev_class_attribute *attr,
+ const char *buf, size_t count)
+{
+ int value;
+
+ mutex_lock(&stat_mon->stat_mon_lock);
+ sscanf(buf, "%d", &value);
+ stat_mon->avp_sampler.sample_time = value;
+ mutex_unlock(&stat_mon->stat_mon_lock);
+
+ return count;
+}
+
+static struct sysdev_class tegra2_statmon_sysclass = {
+ .name = "tegra2_statmon",
+};
+
+#define TEGRA2_STATMON_ATTRIBUTE_EXPAND(_attr, _mode) \
+ static SYSDEV_CLASS_ATTR(_attr, _mode, \
+ tegra2_statmon_##_attr##_show, tegra2_statmon_##_attr##_store)
+
+TEGRA2_STATMON_ATTRIBUTE_EXPAND(enable, 0666);
+TEGRA2_STATMON_ATTRIBUTE_EXPAND(sample_time, 0666);
+
+#define TEGRA2_STATMON_ATTRIBUTE(_name) (&attr_##_name)
+
+static struct sysdev_class_attribute *tegra2_statmon_attrs[] = {
+ TEGRA2_STATMON_ATTRIBUTE(enable),
+ TEGRA2_STATMON_ATTRIBUTE(sample_time),
+ NULL,
+};
+
+static int sampler_init(struct sampler *s)
+{
+ int i;
+ struct clk *clock;
+ unsigned long clock_rate;
+ unsigned long active_count;
+
+ s->enable = false;
+ s->sample_time = 9;
+
+ clock = tegra_get_clock_by_name("mon.sclk");
+ if (IS_ERR(clock)) {
+ pr_err("%s: Couldn't get mon.sckl\n", __func__);
+ return -1;
+ }
+
+ if (clk_set_rate(clock, clock->min_rate)) {
+ pr_err("%s: Failed to set rate\n", __func__);
+ return -1;
+ }
+ clock_rate = clk_get_rate(clock) / FREQ_MULT;
+ active_count = clock_rate * (s->sample_time + 1);
+
+ for (i = 0; i < WINDOW_SIZE; i++)
+ s->active_cycles[i] = active_count;
+
+ s->clock = clock;
+ s->last_sample = &s->active_cycles[0];
+ s->total_active_cycles = active_count << 7;
+ s->window_ms = (s->sample_time + 1) << 7;
+ s->avg_freq = s->total_active_cycles / s->window_ms;
+ s->bumped_freq = s->avg_freq;
+ s->boost_freq = 0;
+
+ return 0;
+}
+
+static int tegra2_stat_mon_init(void)
+{
+ int rc, i;
+ int ret_val = 0;
+
+ stat_mon = kzalloc(sizeof(struct tegra2_stat_mon), GFP_KERNEL);
+ if (stat_mon == NULL) {
+ pr_err("%s: unable to alloc data struct.\n", __func__);
+ return -ENOMEM;
+ }
+
+ stat_mon->stat_mon_base = IO_ADDRESS(TEGRA_STATMON_BASE);
+ stat_mon->vde_mon_base = IO_ADDRESS(TEGRA_VDE_BASE);
+
+ stat_mon->stat_mon_clock = tegra_get_clock_by_name("stat_mon");
+ if (stat_mon->stat_mon_clock == NULL) {
+ pr_err("Failed to get stat mon clock");
+ return -1;
+ }
+
+ if (sampler_init(&stat_mon->avp_sampler))
+ return -1;
+
+ stat_mon->avp_sampler.table = sclk_table;
+ stat_mon->avp_sampler.table_size = ARRAY_SIZE(sclk_table);
+ stat_mon->avp_sampler.boost_step = 1000;
+ stat_mon->avp_sampler.boost_inc_coef = 255;
+ stat_mon->avp_sampler.boost_dec_coef = 128;
+ stat_mon->avp_sampler.min_samples = 3;
+
+ mutex_init(&stat_mon->stat_mon_lock);
+
+ /* /sys/devices/system/tegra2_statmon */
+ rc = sysdev_class_register(&tegra2_statmon_sysclass);
+ if (rc) {
+ pr_err("%s : Couldn't create statmon sysfs entry\n", __func__);
+ return 0;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(tegra2_statmon_attrs) - 1; i++) {
+ rc = sysdev_class_create_file(&tegra2_statmon_sysclass,
+ tegra2_statmon_attrs[i]);
+ if (rc) {
+ pr_err("%s: Failed to create sys class\n", __func__);
+ sysdev_class_unregister(&tegra2_statmon_sysclass);
+ kfree(stat_mon);
+ return 0;
+ }
+ }
+
+ ret_val = request_threaded_irq(INT_SYS_STATS_MON, stat_mon_isr,
+ stat_mon_isr_thread_fn, 0, "stat_mon_int", NULL);
+ if (ret_val) {
+ pr_err("%s: cannot register INT_SYS_STATS_MON handler, \
+ ret_val = 0x%x\n", __func__, ret_val);
+ tegra2_statmon_stop();
+ stat_mon->avp_sampler.enable = false;
+ kfree(stat_mon);
+ return ret_val;
+ }
+
+ return 0;
+}
+
+late_initcall(tegra2_stat_mon_init);
diff --git a/arch/arm/mach-tegra/tegra2_statmon.h b/arch/arm/mach-tegra/tegra2_statmon.h
new file mode 100644
index 000000000000..ae1094eb1c33
--- /dev/null
+++ b/arch/arm/mach-tegra/tegra2_statmon.h
@@ -0,0 +1,33 @@
+/*
+ * arch/arm/mach-tegra/tegra2_statmon.h
+ *
+ * Copyright (c) 2011, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifdef CONFIG_TEGRA_STAT_MON
+int tegra2_statmon_start(void);
+void tegra2_statmon_stop(void);
+#else
+static inline int tegra2_statmon_start(void)
+{
+ return 0;
+}
+
+static inline void tegra2_statmon_stop(void)
+{
+}
+#endif
diff --git a/arch/arm/mach-tegra/tegra2_throttle.c b/arch/arm/mach-tegra/tegra2_throttle.c
new file mode 100644
index 000000000000..6114b20c6f5c
--- /dev/null
+++ b/arch/arm/mach-tegra/tegra2_throttle.c
@@ -0,0 +1,180 @@
+/*
+ * arch/arm/mach-tegra/tegra2_throttle.c
+ *
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * Author:
+ * Colin Cross <ccross@google.com>
+ * Based on arch/arm/plat-omap/cpu-omap.c, (C) 2005 Nokia Corporation
+ *
+ * Copyright (C) 2010-2011 NVIDIA Corporation
+ *
+ * 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/module.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/cpufreq.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/debugfs.h>
+
+#include "clock.h"
+#include "cpu-tegra.h"
+
+/* tegra throttling require frequencies in the table to be in ascending order */
+static struct cpufreq_frequency_table *throttle_table;
+static struct mutex *cpu_throttle_lock;
+
+/* CPU frequency is gradually lowered when throttling is enabled */
+#define THROTTLE_DELAY msecs_to_jiffies(2000)
+
+static int is_throttling;
+static int throttle_lowest_index;
+static int throttle_highest_index;
+static int throttle_index;
+static int throttle_next_index;
+static struct delayed_work throttle_work;
+static struct workqueue_struct *workqueue;
+static DEFINE_MUTEX(tegra_throttle_lock);
+
+static void tegra_throttle_work_func(struct work_struct *work)
+{
+ unsigned int current_freq;
+
+ mutex_lock(cpu_throttle_lock);
+ if (!is_throttling)
+ goto out;
+
+ current_freq = tegra_getspeed(0);
+ throttle_index = throttle_next_index;
+
+ if (throttle_table[throttle_index].frequency < current_freq)
+ tegra_cpu_set_speed_cap(NULL);
+
+ if (throttle_index > throttle_lowest_index) {
+ throttle_next_index = throttle_index - 1;
+ queue_delayed_work(workqueue, &throttle_work, THROTTLE_DELAY);
+ }
+out:
+ mutex_unlock(cpu_throttle_lock);
+}
+
+/*
+ * tegra_throttling_enable
+ * This function may sleep
+ */
+void tegra_throttling_enable(bool enable)
+{
+ mutex_lock(&tegra_throttle_lock);
+ mutex_lock(cpu_throttle_lock);
+
+ if (enable && !(is_throttling++)) {
+ unsigned int current_freq = tegra_getspeed(0);
+
+ for (throttle_index = throttle_highest_index;
+ throttle_index >= throttle_lowest_index;
+ throttle_index--)
+ if (throttle_table[throttle_index].frequency
+ < current_freq)
+ break;
+
+ throttle_index = max(throttle_index, throttle_lowest_index);
+ throttle_next_index = throttle_index;
+ queue_delayed_work(workqueue, &throttle_work, 0);
+ } else if (!enable && is_throttling) {
+ if (!(--is_throttling)) {
+ /* restore speed requested by governor */
+ tegra_cpu_set_speed_cap(NULL);
+
+ mutex_unlock(cpu_throttle_lock);
+ cancel_delayed_work_sync(&throttle_work);
+ mutex_unlock(&tegra_throttle_lock);
+ return;
+ }
+ }
+ mutex_unlock(cpu_throttle_lock);
+ mutex_unlock(&tegra_throttle_lock);
+}
+EXPORT_SYMBOL_GPL(tegra_throttling_enable);
+
+unsigned int tegra_throttle_governor_speed(unsigned int requested_speed)
+{
+ return is_throttling ?
+ min(requested_speed, throttle_table[throttle_index].frequency) :
+ requested_speed;
+}
+
+bool tegra_is_throttling(void)
+{
+ return is_throttling;
+}
+
+int __init tegra_throttle_init(struct mutex *cpu_lock)
+{
+ struct tegra_cpufreq_table_data *table_data =
+ tegra_cpufreq_table_get();
+ if (IS_ERR_OR_NULL(table_data))
+ return -EINVAL;
+
+ /*
+ * High-priority, others flags default: not bound to a specific
+ * CPU, has rescue worker task (in case of allocation deadlock,
+ * etc.). Single-threaded.
+ */
+ workqueue = alloc_workqueue("cpu-tegra",
+ WQ_HIGHPRI | WQ_UNBOUND | WQ_RESCUER, 1);
+ if (!workqueue)
+ return -ENOMEM;
+ INIT_DELAYED_WORK(&throttle_work, tegra_throttle_work_func);
+
+ throttle_lowest_index = table_data->throttle_lowest_index;
+ throttle_highest_index = table_data->throttle_highest_index;
+ throttle_table = table_data->freq_table;
+ cpu_throttle_lock = cpu_lock;
+
+ return 0;
+}
+
+void tegra_throttle_exit(void)
+{
+ destroy_workqueue(workqueue);
+}
+
+#ifdef CONFIG_DEBUG_FS
+
+static int throttle_debug_set(void *data, u64 val)
+{
+ tegra_throttling_enable(val);
+ return 0;
+}
+static int throttle_debug_get(void *data, u64 *val)
+{
+ *val = (u64) is_throttling;
+ return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(throttle_fops, throttle_debug_get, throttle_debug_set,
+ "%llu\n");
+
+int __init tegra_throttle_debug_init(struct dentry *cpu_tegra_debugfs_root)
+{
+ if (!debugfs_create_file("throttle", 0644, cpu_tegra_debugfs_root,
+ NULL, &throttle_fops))
+ return -ENOMEM;
+ return 0;
+}
+#endif /* CONFIG_DEBUG_FS */
+
diff --git a/arch/arm/mach-tegra/tegra3_actmon.c b/arch/arm/mach-tegra/tegra3_actmon.c
new file mode 100644
index 000000000000..05cdc1f86465
--- /dev/null
+++ b/arch/arm/mach-tegra/tegra3_actmon.c
@@ -0,0 +1,848 @@
+/*
+ * Copyright (c) 2011, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/suspend.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/uaccess.h>
+
+#include <mach/iomap.h>
+#include <mach/irqs.h>
+#include <mach/clk.h>
+
+#include "clock.h"
+
+#define ACTMON_GLB_STATUS 0x00
+#define ACTMON_GLB_PERIOD_CTRL 0x04
+
+#define ACTMON_DEV_CTRL 0x00
+#define ACTMON_DEV_CTRL_ENB (0x1 << 31)
+#define ACTMON_DEV_CTRL_UP_WMARK_ENB (0x1 << 30)
+#define ACTMON_DEV_CTRL_DOWN_WMARK_ENB (0x1 << 29)
+#define ACTMON_DEV_CTRL_UP_WMARK_NUM_SHIFT 26
+#define ACTMON_DEV_CTRL_UP_WMARK_NUM_MASK (0x7 << 26)
+#define ACTMON_DEV_CTRL_DOWN_WMARK_NUM_SHIFT 23
+#define ACTMON_DEV_CTRL_DOWN_WMARK_NUM_MASK (0x7 << 23)
+#define ACTMON_DEV_CTRL_AVG_UP_WMARK_ENB (0x1 << 21)
+#define ACTMON_DEV_CTRL_AVG_DOWN_WMARK_ENB (0x1 << 20)
+#define ACTMON_DEV_CTRL_PERIODIC_ENB (0x1 << 18)
+#define ACTMON_DEV_CTRL_K_VAL_SHIFT 10
+#define ACTMON_DEV_CTRL_K_VAL_MASK (0x7 << 10)
+
+#define ACTMON_DEV_UP_WMARK 0x04
+#define ACTMON_DEV_DOWN_WMARK 0x08
+#define ACTMON_DEV_INIT_AVG 0x0c
+#define ACTMON_DEV_AVG_UP_WMARK 0x10
+#define ACTMON_DEV_AVG_DOWN_WMARK 0x14
+
+#define ACTMON_DEV_COUNT_WEGHT 0x18
+#define ACTMON_DEV_COUNT 0x1c
+#define ACTMON_DEV_AVG_COUNT 0x20
+
+#define ACTMON_DEV_INTR_STATUS 0x24
+#define ACTMON_DEV_INTR_UP_WMARK (0x1 << 31)
+#define ACTMON_DEV_INTR_DOWN_WMARK (0x1 << 30)
+#define ACTMON_DEV_INTR_AVG_DOWN_WMARK (0x1 << 25)
+#define ACTMON_DEV_INTR_AVG_UP_WMARK (0x1 << 24)
+
+#define ACTMON_DEFAULT_AVG_WINDOW_LOG2 6
+#define ACTMON_DEFAULT_AVG_BAND 6 /* 1/10 of % */
+
+enum actmon_type {
+ ACTMON_LOAD_SAMPLER,
+ ACTMON_FREQ_SAMPLER,
+};
+
+enum actmon_state {
+ ACTMON_UNINITIALIZED = -1,
+ ACTMON_OFF = 0,
+ ACTMON_ON = 1,
+ ACTMON_SUSPENDED = 2,
+};
+
+#define ACTMON_DEFAULT_SAMPLING_PERIOD 12
+static u8 actmon_sampling_period;
+
+static unsigned long actmon_clk_freq;
+
+
+/* Units:
+ * - frequency in kHz
+ * - coefficients, and thresholds in %
+ * - sampling period in ms
+ * - window in sample periods (value = setting + 1)
+ */
+struct actmon_dev {
+ u32 reg;
+ u32 glb_status_irq_mask;
+ const char *dev_id;
+ const char *con_id;
+ struct clk *clk;
+
+ unsigned long max_freq;
+ unsigned long target_freq;
+ unsigned long cur_freq;
+
+ unsigned long avg_actv_freq;
+ unsigned long avg_band_freq;
+ unsigned int avg_sustain_coef;
+ u32 avg_count;
+
+ unsigned long boost_freq;
+ unsigned long boost_freq_step;
+ unsigned int boost_up_coef;
+ unsigned int boost_down_coef;
+ unsigned int boost_up_threshold;
+ unsigned int boost_down_threshold;
+
+ u8 up_wmark_window;
+ u8 down_wmark_window;
+ u8 avg_window_log2;
+ u32 count_weight;
+
+ enum actmon_type type;
+ enum actmon_state state;
+ enum actmon_state saved_state;
+
+ spinlock_t lock;
+
+ struct notifier_block rate_change_nb;
+};
+
+static void __iomem *actmon_base = IO_ADDRESS(TEGRA_ACTMON_BASE);
+
+static inline u32 actmon_readl(u32 offset)
+{
+ return __raw_readl((u32)actmon_base + offset);
+}
+static inline void actmon_writel(u32 val, u32 offset)
+{
+ __raw_writel(val, (u32)actmon_base + offset);
+}
+static inline void actmon_wmb(void)
+{
+ wmb();
+ actmon_readl(ACTMON_GLB_STATUS);
+}
+
+#define offs(x) (dev->reg + x)
+
+static inline unsigned long do_percent(unsigned long val, unsigned int pct)
+{
+ return val * pct / 100;
+}
+
+static inline void actmon_dev_up_wmark_set(struct actmon_dev *dev)
+{
+ u32 val;
+ unsigned long freq = (dev->type == ACTMON_FREQ_SAMPLER) ?
+ dev->cur_freq : actmon_clk_freq;
+
+ val = freq * actmon_sampling_period;
+ actmon_writel(do_percent(val, dev->boost_up_threshold),
+ offs(ACTMON_DEV_UP_WMARK));
+}
+
+static inline void actmon_dev_down_wmark_set(struct actmon_dev *dev)
+{
+ u32 val;
+ unsigned long freq = (dev->type == ACTMON_FREQ_SAMPLER) ?
+ dev->cur_freq : actmon_clk_freq;
+
+ val = freq * actmon_sampling_period;
+ actmon_writel(do_percent(val, dev->boost_down_threshold),
+ offs(ACTMON_DEV_DOWN_WMARK));
+}
+
+static inline void actmon_dev_wmark_set(struct actmon_dev *dev)
+{
+ u32 val;
+ unsigned long freq = (dev->type == ACTMON_FREQ_SAMPLER) ?
+ dev->cur_freq : actmon_clk_freq;
+
+ val = freq * actmon_sampling_period;
+ actmon_writel(do_percent(val, dev->boost_up_threshold),
+ offs(ACTMON_DEV_UP_WMARK));
+ actmon_writel(do_percent(val, dev->boost_down_threshold),
+ offs(ACTMON_DEV_DOWN_WMARK));
+}
+
+static inline void actmon_dev_avg_wmark_set(struct actmon_dev *dev)
+{
+ u32 avg = dev->avg_count;
+ u32 band = dev->avg_band_freq * actmon_sampling_period;
+
+ actmon_writel(avg + band, offs(ACTMON_DEV_AVG_UP_WMARK));
+ avg = max(avg, band);
+ actmon_writel(avg - band, offs(ACTMON_DEV_AVG_DOWN_WMARK));
+}
+
+static unsigned long actmon_dev_avg_freq_get(struct actmon_dev *dev)
+{
+ u64 val;
+
+ if (dev->type == ACTMON_FREQ_SAMPLER)
+ return dev->avg_count / actmon_sampling_period;
+
+ val = (u64)dev->avg_count * dev->cur_freq;
+ do_div(val, actmon_clk_freq * actmon_sampling_period);
+ return (u32)val;
+}
+
+/* Activity monitor sampling operations */
+irqreturn_t actmon_dev_isr(int irq, void *dev_id)
+{
+ u32 val;
+ unsigned long flags;
+ struct actmon_dev *dev = (struct actmon_dev *)dev_id;
+
+ val = actmon_readl(ACTMON_GLB_STATUS) & dev->glb_status_irq_mask;
+ if (!val)
+ return IRQ_NONE;
+
+ spin_lock_irqsave(&dev->lock, flags);
+
+ dev->avg_count = actmon_readl(offs(ACTMON_DEV_AVG_COUNT));
+ actmon_dev_avg_wmark_set(dev);
+
+ val = actmon_readl(offs(ACTMON_DEV_INTR_STATUS));
+ if (val & ACTMON_DEV_INTR_UP_WMARK) {
+ val = actmon_readl(offs(ACTMON_DEV_CTRL)) |
+ ACTMON_DEV_CTRL_UP_WMARK_ENB |
+ ACTMON_DEV_CTRL_DOWN_WMARK_ENB;
+
+ dev->boost_freq = dev->boost_freq_step +
+ do_percent(dev->boost_freq, dev->boost_up_coef);
+ if (dev->boost_freq >= dev->max_freq) {
+ dev->boost_freq = dev->max_freq;
+ val &= ~ACTMON_DEV_CTRL_UP_WMARK_ENB;
+ }
+ actmon_writel(val, offs(ACTMON_DEV_CTRL));
+ } else if (val & ACTMON_DEV_INTR_DOWN_WMARK) {
+ val = actmon_readl(offs(ACTMON_DEV_CTRL)) |
+ ACTMON_DEV_CTRL_UP_WMARK_ENB |
+ ACTMON_DEV_CTRL_DOWN_WMARK_ENB;
+
+ dev->boost_freq =
+ do_percent(dev->boost_freq, dev->boost_down_coef);
+ if (dev->boost_freq < (dev->boost_freq_step >> 1)) {
+ dev->boost_freq = 0;
+ val &= ~ACTMON_DEV_CTRL_DOWN_WMARK_ENB;
+ }
+ actmon_writel(val, offs(ACTMON_DEV_CTRL));
+ }
+
+ actmon_writel(0xffffffff, offs(ACTMON_DEV_INTR_STATUS)); /* clr all */
+ actmon_wmb();
+
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return IRQ_WAKE_THREAD;
+}
+
+irqreturn_t actmon_dev_fn(int irq, void *dev_id)
+{
+ unsigned long flags, freq;
+ struct actmon_dev *dev = (struct actmon_dev *)dev_id;
+
+ spin_lock_irqsave(&dev->lock, flags);
+
+ if (dev->state != ACTMON_ON) {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return IRQ_HANDLED;
+ }
+
+ freq = actmon_dev_avg_freq_get(dev);
+ dev->avg_actv_freq = freq;
+ freq = do_percent(freq, dev->avg_sustain_coef);
+ freq += dev->boost_freq;
+ dev->target_freq = freq;
+
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ pr_debug("%s.%s(kHz): avg: %lu, target: %lu current: %lu\n",
+ dev->dev_id, dev->con_id, dev->avg_actv_freq,
+ dev->target_freq, dev->cur_freq);
+ clk_set_rate(dev->clk, freq * 1000);
+
+ return IRQ_HANDLED;
+}
+
+static int actmon_rate_notify_cb(
+ struct notifier_block *nb, unsigned long rate, void *v)
+{
+ unsigned long flags;
+ struct actmon_dev *dev = container_of(
+ nb, struct actmon_dev, rate_change_nb);
+
+ spin_lock_irqsave(&dev->lock, flags);
+
+ dev->cur_freq = rate / 1000;
+ if (dev->type == ACTMON_FREQ_SAMPLER) {
+ actmon_dev_wmark_set(dev);
+ actmon_wmb();
+ }
+
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return NOTIFY_OK;
+};
+
+/* Activity monitor configuration and control */
+static void actmon_dev_configure(struct actmon_dev *dev, unsigned long freq)
+{
+ u32 val;
+
+ dev->cur_freq = freq;
+ dev->target_freq = freq;
+ dev->avg_actv_freq = freq;
+
+ if (dev->type == ACTMON_FREQ_SAMPLER) {
+ dev->avg_count = dev->cur_freq * actmon_sampling_period;
+ dev->avg_band_freq = dev->max_freq *
+ ACTMON_DEFAULT_AVG_BAND / 1000;
+ } else {
+ dev->avg_count = actmon_clk_freq * actmon_sampling_period;
+ dev->avg_band_freq = actmon_clk_freq *
+ ACTMON_DEFAULT_AVG_BAND / 1000;
+ }
+ actmon_writel(dev->avg_count, offs(ACTMON_DEV_INIT_AVG));
+
+ BUG_ON(!dev->boost_up_threshold);
+ dev->avg_sustain_coef = 100 * 100 / dev->boost_up_threshold;
+ actmon_dev_avg_wmark_set(dev);
+ actmon_dev_wmark_set(dev);
+
+ actmon_writel(dev->count_weight, offs(ACTMON_DEV_COUNT_WEGHT));
+ actmon_writel(0xffffffff, offs(ACTMON_DEV_INTR_STATUS)); /* clr all */
+
+ val = ACTMON_DEV_CTRL_PERIODIC_ENB | ACTMON_DEV_CTRL_AVG_UP_WMARK_ENB |
+ ACTMON_DEV_CTRL_AVG_DOWN_WMARK_ENB;
+ val |= ((dev->avg_window_log2 - 1) << ACTMON_DEV_CTRL_K_VAL_SHIFT) &
+ ACTMON_DEV_CTRL_K_VAL_MASK;
+ val |= ((dev->down_wmark_window - 1) <<
+ ACTMON_DEV_CTRL_DOWN_WMARK_NUM_SHIFT) &
+ ACTMON_DEV_CTRL_DOWN_WMARK_NUM_MASK;
+ val |= ((dev->up_wmark_window - 1) <<
+ ACTMON_DEV_CTRL_UP_WMARK_NUM_SHIFT) &
+ ACTMON_DEV_CTRL_UP_WMARK_NUM_MASK;
+ val |= ACTMON_DEV_CTRL_DOWN_WMARK_ENB | ACTMON_DEV_CTRL_UP_WMARK_ENB;
+ actmon_writel(val, offs(ACTMON_DEV_CTRL));
+ actmon_wmb();
+}
+
+static void actmon_dev_enable(struct actmon_dev *dev)
+{
+ u32 val;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->lock, flags);
+
+ if (dev->state == ACTMON_OFF) {
+ dev->state = ACTMON_ON;
+
+ val = actmon_readl(offs(ACTMON_DEV_CTRL));
+ val |= ACTMON_DEV_CTRL_ENB;
+ actmon_writel(val, offs(ACTMON_DEV_CTRL));
+ actmon_wmb();
+ }
+ spin_unlock_irqrestore(&dev->lock, flags);
+}
+
+static void actmon_dev_disable(struct actmon_dev *dev)
+{
+ u32 val;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->lock, flags);
+
+ if (dev->state == ACTMON_ON) {
+ dev->state = ACTMON_OFF;
+
+ val = actmon_readl(offs(ACTMON_DEV_CTRL));
+ val &= ~ACTMON_DEV_CTRL_ENB;
+ actmon_writel(val, offs(ACTMON_DEV_CTRL));
+ actmon_writel(0xffffffff, offs(ACTMON_DEV_INTR_STATUS));
+ actmon_wmb();
+ }
+ spin_unlock_irqrestore(&dev->lock, flags);
+}
+
+static void actmon_dev_suspend(struct actmon_dev *dev)
+{
+ u32 val;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->lock, flags);
+
+ if ((dev->state == ACTMON_ON) || (dev->state == ACTMON_OFF)){
+ dev->saved_state = dev->state;
+ dev->state = ACTMON_SUSPENDED;
+
+ val = actmon_readl(offs(ACTMON_DEV_CTRL));
+ val &= ~ACTMON_DEV_CTRL_ENB;
+ actmon_writel(val, offs(ACTMON_DEV_CTRL));
+ actmon_writel(0xffffffff, offs(ACTMON_DEV_INTR_STATUS));
+ actmon_wmb();
+ }
+ spin_unlock_irqrestore(&dev->lock, flags);
+}
+
+static void actmon_dev_resume(struct actmon_dev *dev)
+{
+ u32 val;
+ unsigned long flags;
+ unsigned long freq = clk_get_rate(dev->clk) / 1000;
+
+ spin_lock_irqsave(&dev->lock, flags);
+
+ if (dev->state == ACTMON_SUSPENDED) {
+ actmon_dev_configure(dev, freq);
+ dev->state = dev->saved_state;
+ if (dev->state == ACTMON_ON) {
+ val = actmon_readl(offs(ACTMON_DEV_CTRL));
+ val |= ACTMON_DEV_CTRL_ENB;
+ actmon_writel(val, offs(ACTMON_DEV_CTRL));
+ actmon_wmb();
+ }
+ }
+ spin_unlock_irqrestore(&dev->lock, flags);
+}
+
+static int __init actmon_dev_init(struct actmon_dev *dev)
+{
+ int ret;
+ struct clk *p;
+ unsigned long freq;
+
+ spin_lock_init(&dev->lock);
+
+ dev->clk = clk_get_sys(dev->dev_id, dev->con_id);
+ if (IS_ERR(dev->clk)) {
+ pr_err("Failed to find %s.%s clock\n",
+ dev->dev_id, dev->con_id);
+ return -ENODEV;
+ }
+ dev->max_freq = clk_round_rate(dev->clk, ULONG_MAX);
+ clk_set_rate(dev->clk, dev->max_freq);
+ dev->max_freq /= 1000;
+ freq = clk_get_rate(dev->clk) / 1000;
+ actmon_dev_configure(dev, freq);
+
+ /* actmon device controls shared bus user clock, but rate
+ change notification should come from bus clock itself */
+ p = clk_get_parent(dev->clk);
+ BUG_ON(!p);
+
+ if (dev->rate_change_nb.notifier_call) {
+ ret = tegra_register_clk_rate_notifier(p, &dev->rate_change_nb);
+ if (ret) {
+ pr_err("Failed to register %s rate change notifier"
+ " for %s\n", p->name, dev->dev_id);
+ return ret;
+ }
+ }
+
+ ret = request_threaded_irq(INT_ACTMON, actmon_dev_isr, actmon_dev_fn,
+ IRQF_SHARED, dev->dev_id, dev);
+ if (ret) {
+ pr_err("Failed irq %d request for %s.%s\n",
+ INT_ACTMON, dev->dev_id, dev->con_id);
+ tegra_unregister_clk_rate_notifier(p, &dev->rate_change_nb);
+ return ret;
+ }
+
+ dev->state = ACTMON_OFF;
+ actmon_dev_enable(dev);
+ clk_enable(dev->clk);
+ return 0;
+}
+
+/* EMC activity monitor: frequency sampling device:
+ * activity counter is incremented every 256 memory transactions, and
+ * each transaction takes 2 EMC clocks; count_weight = 512.
+ */
+static struct actmon_dev actmon_dev_emc = {
+ .reg = 0x1c0,
+ .glb_status_irq_mask = (0x1 << 26),
+ .dev_id = "tegra_actmon",
+ .con_id = "emc",
+
+ .boost_freq_step = 16000,
+ .boost_up_coef = 200,
+ .boost_down_coef = 50,
+ .boost_up_threshold = 60,
+ .boost_down_threshold = 40,
+
+ .up_wmark_window = 1,
+ .down_wmark_window = 3,
+ .avg_window_log2 = ACTMON_DEFAULT_AVG_WINDOW_LOG2,
+ .count_weight = 0x200,
+
+ .type = ACTMON_FREQ_SAMPLER,
+ .state = ACTMON_UNINITIALIZED,
+
+ .rate_change_nb = {
+ .notifier_call = actmon_rate_notify_cb,
+ },
+};
+
+/* AVP activity monitor: load sampling device:
+ * activity counter is incremented on every actmon clock pulse while
+ * AVP is not halted by flow controller; count_weight = 1.
+ */
+static struct actmon_dev actmon_dev_avp = {
+ .reg = 0x0c0,
+ .glb_status_irq_mask = (0x1 << 30),
+ .dev_id = "tegra_actmon",
+ .con_id = "avp",
+
+ .boost_freq_step = 8000,
+ .boost_up_coef = 200,
+ .boost_down_coef = 50,
+ .boost_up_threshold = 75,
+ .boost_down_threshold = 50,
+
+ .up_wmark_window = 1,
+ .down_wmark_window = 3,
+ .avg_window_log2 = ACTMON_DEFAULT_AVG_WINDOW_LOG2,
+ .count_weight = 0x1,
+
+ .type = ACTMON_LOAD_SAMPLER,
+ .state = ACTMON_UNINITIALIZED,
+
+ .rate_change_nb = {
+ .notifier_call = actmon_rate_notify_cb,
+ },
+};
+
+static struct actmon_dev *actmon_devices[] = {
+ &actmon_dev_emc,
+ &actmon_dev_avp,
+};
+
+/* Activity monitor suspend/resume */
+static int actmon_pm_notify(struct notifier_block *nb,
+ unsigned long event, void *data)
+{
+ int i;
+
+ switch (event) {
+ case PM_SUSPEND_PREPARE:
+ for (i = 0; i < ARRAY_SIZE(actmon_devices); i++)
+ actmon_dev_suspend(actmon_devices[i]);
+ break;
+ case PM_POST_SUSPEND:
+ for (i = 0; i < ARRAY_SIZE(actmon_devices); i++)
+ actmon_dev_resume(actmon_devices[i]);
+ break;
+ }
+
+ return NOTIFY_OK;
+};
+
+static struct notifier_block actmon_pm_nb = {
+ .notifier_call = actmon_pm_notify,
+};
+
+#ifdef CONFIG_DEBUG_FS
+
+#define RW_MODE (S_IWUSR | S_IRUGO)
+#define RO_MODE S_IRUGO
+
+static struct dentry *clk_debugfs_root;
+
+static int type_show(struct seq_file *s, void *data)
+{
+ struct actmon_dev *dev = s->private;
+
+ seq_printf(s, "%s\n", (dev->type == ACTMON_LOAD_SAMPLER) ?
+ "Load Activity Monitor" : "Frequency Activity Monitor");
+ return 0;
+}
+static int type_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, type_show, inode->i_private);
+}
+static const struct file_operations type_fops = {
+ .open = type_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int actv_get(void *data, u64 *val)
+{
+ unsigned long flags;
+ struct actmon_dev *dev = data;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ *val = actmon_dev_avg_freq_get(dev);
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(actv_fops, actv_get, NULL, "%llu\n");
+
+static int step_get(void *data, u64 *val)
+{
+ struct actmon_dev *dev = data;
+ *val = dev->boost_freq_step * 100 / dev->max_freq;
+ return 0;
+}
+static int step_set(void *data, u64 val)
+{
+ unsigned long flags;
+ struct actmon_dev *dev = data;
+
+ if (val > 100)
+ val = 100;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->boost_freq_step = do_percent(dev->max_freq, (unsigned int)val);
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(step_fops, step_get, step_set, "%llu\n");
+
+static int up_threshold_get(void *data, u64 *val)
+{
+ struct actmon_dev *dev = data;
+ *val = dev->boost_up_threshold;
+ return 0;
+}
+static int up_threshold_set(void *data, u64 val)
+{
+ unsigned long flags;
+ struct actmon_dev *dev = data;
+ unsigned int up_threshold = (unsigned int)val;
+
+ if (up_threshold > 100)
+ up_threshold = 100;
+
+ spin_lock_irqsave(&dev->lock, flags);
+
+ if (up_threshold <= dev->boost_down_threshold)
+ up_threshold = dev->boost_down_threshold;
+ if (up_threshold)
+ dev->avg_sustain_coef = 100 * 100 / up_threshold;
+ dev->boost_up_threshold = up_threshold;
+
+ actmon_dev_up_wmark_set(dev);
+ actmon_wmb();
+
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(up_threshold_fops, up_threshold_get,
+ up_threshold_set, "%llu\n");
+
+static int down_threshold_get(void *data, u64 *val)
+{
+ struct actmon_dev *dev = data;
+ *val = dev->boost_down_threshold;
+ return 0;
+}
+static int down_threshold_set(void *data, u64 val)
+{
+ unsigned long flags;
+ struct actmon_dev *dev = data;
+ unsigned int down_threshold = (unsigned int)val;
+
+ if (down_threshold < 0)
+ down_threshold = 0;
+
+ spin_lock_irqsave(&dev->lock, flags);
+
+ if (down_threshold >= dev->boost_up_threshold)
+ down_threshold = dev->boost_up_threshold;
+ dev->boost_down_threshold = down_threshold;
+
+ actmon_dev_down_wmark_set(dev);
+ actmon_wmb();
+
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(down_threshold_fops, down_threshold_get,
+ down_threshold_set, "%llu\n");
+
+static int state_get(void *data, u64 *val)
+{
+ struct actmon_dev *dev = data;
+ *val = dev->state;
+ return 0;
+}
+static int state_set(void *data, u64 val)
+{
+ struct actmon_dev *dev = data;
+
+ if (val)
+ actmon_dev_enable(dev);
+ else
+ actmon_dev_disable(dev);
+ return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(state_fops, state_get, state_set, "%llu\n");
+
+static int period_get(void *data, u64 *val)
+{
+ *val = actmon_sampling_period;
+ return 0;
+}
+static int period_set(void *data, u64 val)
+{
+ int i;
+ unsigned long flags;
+ u8 period = (u8)val;
+
+ if (period) {
+ actmon_sampling_period = period;
+ actmon_writel(period - 1, ACTMON_GLB_PERIOD_CTRL);
+
+ for (i = 0; i < ARRAY_SIZE(actmon_devices); i++) {
+ struct actmon_dev *dev = actmon_devices[i];
+ spin_lock_irqsave(&dev->lock, flags);
+ actmon_dev_wmark_set(dev);
+ spin_unlock_irqrestore(&dev->lock, flags);
+ }
+ actmon_wmb();
+ return 0;
+ }
+ return -EINVAL;
+}
+DEFINE_SIMPLE_ATTRIBUTE(period_fops, period_get, period_set, "%llu\n");
+
+
+static int actmon_debugfs_create_dev(struct actmon_dev *dev)
+{
+ struct dentry *dir, *d;
+
+ if (dev->state == ACTMON_UNINITIALIZED)
+ return 0;
+
+ dir = debugfs_create_dir(dev->con_id, clk_debugfs_root);
+ if (!dir)
+ return -ENOMEM;
+
+ d = debugfs_create_file(
+ "actv_type", RO_MODE, dir, dev, &type_fops);
+ if (!d)
+ return -ENOMEM;
+
+ d = debugfs_create_file(
+ "avg_activity", RO_MODE, dir, dev, &actv_fops);
+ if (!d)
+ return -ENOMEM;
+
+ d = debugfs_create_file(
+ "boost_step", RW_MODE, dir, dev, &step_fops);
+ if (!d)
+ return -ENOMEM;
+
+ d = debugfs_create_u32(
+ "boost_rate_dec", RW_MODE, dir, (u32 *)&dev->boost_down_coef);
+ if (!d)
+ return -ENOMEM;
+
+ d = debugfs_create_u32(
+ "boost_rate_inc", RW_MODE, dir, (u32 *)&dev->boost_up_coef);
+ if (!d)
+ return -ENOMEM;
+
+ d = debugfs_create_file(
+ "boost_threshold_dn", RW_MODE, dir, dev, &down_threshold_fops);
+ if (!d)
+ return -ENOMEM;
+
+ d = debugfs_create_file(
+ "boost_threshold_up", RW_MODE, dir, dev, &up_threshold_fops);
+ if (!d)
+ return -ENOMEM;
+
+ d = debugfs_create_file(
+ "state", RW_MODE, dir, dev, &state_fops);
+ if (!d)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int __init actmon_debugfs_init(void)
+{
+ int i;
+ int ret = -ENOMEM;
+ struct dentry *d;
+
+ d = debugfs_create_dir("tegra_actmon", NULL);
+ if (!d)
+ return ret;
+ clk_debugfs_root = d;
+
+ d = debugfs_create_file("period", RW_MODE, d, NULL, &period_fops);
+ if (!d)
+ goto err_out;
+
+ for (i = 0; i < ARRAY_SIZE(actmon_devices); i++) {
+ ret = actmon_debugfs_create_dev(actmon_devices[i]);
+ if (ret)
+ goto err_out;
+ }
+ return 0;
+
+err_out:
+ debugfs_remove_recursive(clk_debugfs_root);
+ return ret;
+}
+
+#endif
+
+static int __init tegra_actmon_init(void)
+{
+ int i, ret;
+ struct clk *c = tegra_get_clock_by_name("actmon");
+
+ if (!c) {
+ pr_err("%s: Failed to find actmon clock\n", __func__);
+ return 0;
+ }
+ actmon_clk_freq = clk_get_rate(c) / 1000;
+ ret = clk_enable(c);
+ if (ret) {
+ pr_err("%s: Failed to enable actmon clock\n", __func__);
+ return 0;
+ }
+ actmon_sampling_period = ACTMON_DEFAULT_SAMPLING_PERIOD;
+ actmon_writel(actmon_sampling_period - 1, ACTMON_GLB_PERIOD_CTRL);
+
+ for (i = 0; i < ARRAY_SIZE(actmon_devices); i++) {
+ ret = actmon_dev_init(actmon_devices[i]);
+ pr_info("%s.%s: %s initialization (%d)\n",
+ actmon_devices[i]->dev_id, actmon_devices[i]->con_id,
+ ret ? "Failed" : "Completed", ret);
+ }
+ register_pm_notifier(&actmon_pm_nb);
+
+#ifdef CONFIG_DEBUG_FS
+ actmon_debugfs_init();
+#endif
+ return 0;
+}
+late_initcall(tegra_actmon_init);
diff --git a/arch/arm/mach-tegra/tegra3_clocks.c b/arch/arm/mach-tegra/tegra3_clocks.c
new file mode 100644
index 000000000000..c0bd12a4f2b8
--- /dev/null
+++ b/arch/arm/mach-tegra/tegra3_clocks.c
@@ -0,0 +1,4703 @@
+/*
+ * arch/arm/mach-tegra/tegra3_clocks.c
+ *
+ * Copyright (C) 2010-2011 NVIDIA Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/cpufreq.h>
+#include <linux/syscore_ops.h>
+
+#include <asm/clkdev.h>
+
+#include <mach/iomap.h>
+#include <mach/edp.h>
+
+#include "clock.h"
+#include "fuse.h"
+#include "dvfs.h"
+#include "pm.h"
+#include "sleep.h"
+#include "tegra3_emc.h"
+
+#define RST_DEVICES_L 0x004
+#define RST_DEVICES_H 0x008
+#define RST_DEVICES_U 0x00C
+#define RST_DEVICES_V 0x358
+#define RST_DEVICES_W 0x35C
+#define RST_DEVICES_SET_L 0x300
+#define RST_DEVICES_CLR_L 0x304
+#define RST_DEVICES_SET_V 0x430
+#define RST_DEVICES_CLR_V 0x434
+#define RST_DEVICES_NUM 5
+
+#define CLK_OUT_ENB_L 0x010
+#define CLK_OUT_ENB_H 0x014
+#define CLK_OUT_ENB_U 0x018
+#define CLK_OUT_ENB_V 0x360
+#define CLK_OUT_ENB_W 0x364
+#define CLK_OUT_ENB_SET_L 0x320
+#define CLK_OUT_ENB_CLR_L 0x324
+#define CLK_OUT_ENB_SET_V 0x440
+#define CLK_OUT_ENB_CLR_V 0x444
+#define CLK_OUT_ENB_NUM 5
+
+#define RST_DEVICES_V_SWR_CPULP_RST_DIS (0x1 << 1)
+#define CLK_OUT_ENB_V_CLK_ENB_CPULP_EN (0x1 << 1)
+
+#define PERIPH_CLK_TO_BIT(c) (1 << (c->u.periph.clk_num % 32))
+#define PERIPH_CLK_TO_RST_REG(c) \
+ periph_clk_to_reg((c), RST_DEVICES_L, RST_DEVICES_V, 4)
+#define PERIPH_CLK_TO_RST_SET_REG(c) \
+ periph_clk_to_reg((c), RST_DEVICES_SET_L, RST_DEVICES_SET_V, 8)
+#define PERIPH_CLK_TO_RST_CLR_REG(c) \
+ periph_clk_to_reg((c), RST_DEVICES_CLR_L, RST_DEVICES_CLR_V, 8)
+
+#define PERIPH_CLK_TO_ENB_REG(c) \
+ periph_clk_to_reg((c), CLK_OUT_ENB_L, CLK_OUT_ENB_V, 4)
+#define PERIPH_CLK_TO_ENB_SET_REG(c) \
+ periph_clk_to_reg((c), CLK_OUT_ENB_SET_L, CLK_OUT_ENB_SET_V, 8)
+#define PERIPH_CLK_TO_ENB_CLR_REG(c) \
+ periph_clk_to_reg((c), CLK_OUT_ENB_CLR_L, CLK_OUT_ENB_CLR_V, 8)
+
+#define CLK_MASK_ARM 0x44
+#define MISC_CLK_ENB 0x48
+
+#define OSC_CTRL 0x50
+#define OSC_CTRL_OSC_FREQ_MASK (0xF<<28)
+#define OSC_CTRL_OSC_FREQ_13MHZ (0x0<<28)
+#define OSC_CTRL_OSC_FREQ_19_2MHZ (0x4<<28)
+#define OSC_CTRL_OSC_FREQ_12MHZ (0x8<<28)
+#define OSC_CTRL_OSC_FREQ_26MHZ (0xC<<28)
+#define OSC_CTRL_OSC_FREQ_16_8MHZ (0x1<<28)
+#define OSC_CTRL_OSC_FREQ_38_4MHZ (0x5<<28)
+#define OSC_CTRL_OSC_FREQ_48MHZ (0x9<<28)
+#define OSC_CTRL_MASK (0x3f2 | OSC_CTRL_OSC_FREQ_MASK)
+
+#define OSC_CTRL_PLL_REF_DIV_MASK (3<<26)
+#define OSC_CTRL_PLL_REF_DIV_1 (0<<26)
+#define OSC_CTRL_PLL_REF_DIV_2 (1<<26)
+#define OSC_CTRL_PLL_REF_DIV_4 (2<<26)
+
+#define OSC_FREQ_DET 0x58
+#define OSC_FREQ_DET_TRIG (1<<31)
+
+#define OSC_FREQ_DET_STATUS 0x5C
+#define OSC_FREQ_DET_BUSY (1<<31)
+#define OSC_FREQ_DET_CNT_MASK 0xFFFF
+
+#define PERIPH_CLK_SOURCE_I2S1 0x100
+#define PERIPH_CLK_SOURCE_EMC 0x19c
+#define PERIPH_CLK_SOURCE_OSC 0x1fc
+#define PERIPH_CLK_SOURCE_NUM1 \
+ ((PERIPH_CLK_SOURCE_OSC - PERIPH_CLK_SOURCE_I2S1) / 4)
+
+#define PERIPH_CLK_SOURCE_G3D2 0x3b0
+#define PERIPH_CLK_SOURCE_SE 0x42c
+#define PERIPH_CLK_SOURCE_NUM2 \
+ ((PERIPH_CLK_SOURCE_SE - PERIPH_CLK_SOURCE_G3D2) / 4 + 1)
+
+#define AUDIO_DLY_CLK 0x49c
+#define AUDIO_SYNC_CLK_SPDIF 0x4b4
+#define PERIPH_CLK_SOURCE_NUM3 \
+ ((AUDIO_SYNC_CLK_SPDIF - AUDIO_DLY_CLK) / 4 + 1)
+
+#define PERIPH_CLK_SOURCE_NUM (PERIPH_CLK_SOURCE_NUM1 + \
+ PERIPH_CLK_SOURCE_NUM2 + \
+ PERIPH_CLK_SOURCE_NUM3)
+
+#define CPU_SOFTRST_CTRL 0x380
+
+#define PERIPH_CLK_SOURCE_DIVU71_MASK 0xFF
+#define PERIPH_CLK_SOURCE_DIVU16_MASK 0xFFFF
+#define PERIPH_CLK_SOURCE_DIV_SHIFT 0
+#define PERIPH_CLK_SOURCE_DIVIDLE_SHIFT 8
+#define PERIPH_CLK_SOURCE_DIVIDLE_VAL 50
+#define PERIPH_CLK_UART_DIV_ENB (1<<24)
+#define PERIPH_CLK_VI_SEL_EX_SHIFT 24
+#define PERIPH_CLK_VI_SEL_EX_MASK (0x3<<PERIPH_CLK_VI_SEL_EX_SHIFT)
+#define PERIPH_CLK_NAND_DIV_EX_ENB (1<<8)
+#define PERIPH_CLK_DTV_POLARITY_INV (1<<25)
+
+#define AUDIO_SYNC_SOURCE_MASK 0x0F
+#define AUDIO_SYNC_DISABLE_BIT 0x10
+#define AUDIO_SYNC_TAP_NIBBLE_SHIFT(c) ((c->reg_shift - 24) * 4)
+
+#define PLL_BASE 0x0
+#define PLL_BASE_BYPASS (1<<31)
+#define PLL_BASE_ENABLE (1<<30)
+#define PLL_BASE_REF_ENABLE (1<<29)
+#define PLL_BASE_OVERRIDE (1<<28)
+#define PLL_BASE_LOCK (1<<27)
+#define PLL_BASE_DIVP_MASK (0x7<<20)
+#define PLL_BASE_DIVP_SHIFT 20
+#define PLL_BASE_DIVN_MASK (0x3FF<<8)
+#define PLL_BASE_DIVN_SHIFT 8
+#define PLL_BASE_DIVM_MASK (0x1F)
+#define PLL_BASE_DIVM_SHIFT 0
+
+#define PLL_OUT_RATIO_MASK (0xFF<<8)
+#define PLL_OUT_RATIO_SHIFT 8
+#define PLL_OUT_OVERRIDE (1<<2)
+#define PLL_OUT_CLKEN (1<<1)
+#define PLL_OUT_RESET_DISABLE (1<<0)
+
+#define PLL_MISC(c) \
+ (((c)->flags & PLL_ALT_MISC_REG) ? 0x4 : 0xc)
+#define PLL_MISC_LOCK_ENABLE(c) \
+ (((c)->flags & (PLLU | PLLD)) ? (1<<22) : (1<<18))
+
+#define PLL_MISC_DCCON_SHIFT 20
+#define PLL_MISC_CPCON_SHIFT 8
+#define PLL_MISC_CPCON_MASK (0xF<<PLL_MISC_CPCON_SHIFT)
+#define PLL_MISC_LFCON_SHIFT 4
+#define PLL_MISC_LFCON_MASK (0xF<<PLL_MISC_LFCON_SHIFT)
+#define PLL_MISC_VCOCON_SHIFT 0
+#define PLL_MISC_VCOCON_MASK (0xF<<PLL_MISC_VCOCON_SHIFT)
+#define PLLD_MISC_CLKENABLE (1<<30)
+
+#define PLLU_BASE_POST_DIV (1<<20)
+
+#define PLLD_BASE_DSIB_MUX_SHIFT 25
+#define PLLD_BASE_DSIB_MUX_MASK (1<<PLLD_BASE_DSIB_MUX_SHIFT)
+#define PLLD_BASE_CSI_CLKENABLE (1<<26)
+#define PLLD_MISC_DSI_CLKENABLE (1<<30)
+#define PLLD_MISC_DIV_RST (1<<23)
+#define PLLD_MISC_DCCON_SHIFT 12
+
+#define PLLDU_LFCON_SET_DIVN 600
+
+/* FIXME: OUT_OF_TABLE_CPCON per pll */
+#define OUT_OF_TABLE_CPCON 0x8
+
+#define SUPER_CLK_MUX 0x00
+#define SUPER_STATE_SHIFT 28
+#define SUPER_STATE_MASK (0xF << SUPER_STATE_SHIFT)
+#define SUPER_STATE_STANDBY (0x0 << SUPER_STATE_SHIFT)
+#define SUPER_STATE_IDLE (0x1 << SUPER_STATE_SHIFT)
+#define SUPER_STATE_RUN (0x2 << SUPER_STATE_SHIFT)
+#define SUPER_STATE_IRQ (0x3 << SUPER_STATE_SHIFT)
+#define SUPER_STATE_FIQ (0x4 << SUPER_STATE_SHIFT)
+#define SUPER_LP_DIV2_BYPASS (0x1 << 16)
+#define SUPER_SOURCE_MASK 0xF
+#define SUPER_FIQ_SOURCE_SHIFT 12
+#define SUPER_IRQ_SOURCE_SHIFT 8
+#define SUPER_RUN_SOURCE_SHIFT 4
+#define SUPER_IDLE_SOURCE_SHIFT 0
+
+#define SUPER_CLK_DIVIDER 0x04
+#define SUPER_CLOCK_SKIP_ENABLE (0x1 << 31)
+#define SUPER_CLOCK_DIV_U71_SHIFT 16
+#define SUPER_CLOCK_DIV_U71_MASK (0xff << SUPER_CLOCK_DIV_U71_SHIFT)
+/* guarantees safe cpu backup */
+#define SUPER_CLOCK_DIV_U71_MIN 0x2
+#define SUPER_CLOCK_SKIP_NOMIN_SHIFT 8
+#define SUPER_CLOCK_SKIP_DENOM_SHIFT 0
+#define SUPER_CLOCK_SKIP_MASK (0xffff << SUPER_CLOCK_SKIP_DENOM_SHIFT)
+
+#define BUS_CLK_DISABLE (1<<3)
+#define BUS_CLK_DIV_MASK 0x3
+
+#define PMC_CTRL 0x0
+ #define PMC_CTRL_BLINK_ENB (1 << 7)
+
+#define PMC_DPD_PADS_ORIDE 0x1c
+ #define PMC_DPD_PADS_ORIDE_BLINK_ENB (1 << 20)
+
+#define PMC_BLINK_TIMER_DATA_ON_SHIFT 0
+#define PMC_BLINK_TIMER_DATA_ON_MASK 0x7fff
+#define PMC_BLINK_TIMER_ENB (1 << 15)
+#define PMC_BLINK_TIMER_DATA_OFF_SHIFT 16
+#define PMC_BLINK_TIMER_DATA_OFF_MASK 0xffff
+
+#define PMC_PLLP_WB0_OVERRIDE 0xf8
+#define PMC_PLLP_WB0_OVERRIDE_PLLM_ENABLE (1 << 12)
+
+#define UTMIP_PLL_CFG2 0x488
+#define UTMIP_PLL_CFG2_STABLE_COUNT(x) (((x) & 0xfff) << 6)
+#define UTMIP_PLL_CFG2_ACTIVE_DLY_COUNT(x) (((x) & 0x3f) << 18)
+#define UTMIP_PLL_CFG2_FORCE_PD_SAMP_A_POWERDOWN (1 << 0)
+#define UTMIP_PLL_CFG2_FORCE_PD_SAMP_B_POWERDOWN (1 << 2)
+#define UTMIP_PLL_CFG2_FORCE_PD_SAMP_C_POWERDOWN (1 << 4)
+
+#define UTMIP_PLL_CFG1 0x484
+#define UTMIP_PLL_CFG1_ENABLE_DLY_COUNT(x) (((x) & 0x1f) << 27)
+#define UTMIP_PLL_CFG1_XTAL_FREQ_COUNT(x) (((x) & 0xfff) << 0)
+#define UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERDOWN (1 << 14)
+#define UTMIP_PLL_CFG1_FORCE_PLL_ACTIVE_POWERDOWN (1 << 12)
+#define UTMIP_PLL_CFG1_FORCE_PLLU_POWERDOWN (1 << 16)
+
+#define PLLE_BASE_CML_ENABLE (1<<31)
+#define PLLE_BASE_ENABLE (1<<30)
+#define PLLE_BASE_DIVCML_SHIFT 24
+#define PLLE_BASE_DIVCML_MASK (0xf<<PLLE_BASE_DIVCML_SHIFT)
+#define PLLE_BASE_DIVP_SHIFT 16
+#define PLLE_BASE_DIVP_MASK (0x3f<<PLLE_BASE_DIVP_SHIFT)
+#define PLLE_BASE_DIVN_SHIFT 8
+#define PLLE_BASE_DIVN_MASK (0xFF<<PLLE_BASE_DIVN_SHIFT)
+#define PLLE_BASE_DIVM_SHIFT 0
+#define PLLE_BASE_DIVM_MASK (0xFF<<PLLE_BASE_DIVM_SHIFT)
+#define PLLE_BASE_DIV_MASK \
+ (PLLE_BASE_DIVCML_MASK | PLLE_BASE_DIVP_MASK | \
+ PLLE_BASE_DIVN_MASK | PLLE_BASE_DIVM_MASK)
+#define PLLE_BASE_DIV(m, n, p, cml) \
+ (((cml)<<PLLE_BASE_DIVCML_SHIFT) | ((p)<<PLLE_BASE_DIVP_SHIFT) | \
+ ((n)<<PLLE_BASE_DIVN_SHIFT) | ((m)<<PLLE_BASE_DIVM_SHIFT))
+
+#define PLLE_MISC_SETUP_BASE_SHIFT 16
+#define PLLE_MISC_SETUP_BASE_MASK (0xFFFF<<PLLE_MISC_SETUP_BASE_SHIFT)
+#define PLLE_MISC_READY (1<<15)
+#define PLLE_MISC_LOCK (1<<11)
+#define PLLE_MISC_LOCK_ENABLE (1<<9)
+#define PLLE_MISC_SETUP_EX_SHIFT 2
+#define PLLE_MISC_SETUP_EX_MASK (0x3<<PLLE_MISC_SETUP_EX_SHIFT)
+#define PLLE_MISC_SETUP_MASK \
+ (PLLE_MISC_SETUP_BASE_MASK | PLLE_MISC_SETUP_EX_MASK)
+#define PLLE_MISC_SETUP_VALUE \
+ ((0x7<<PLLE_MISC_SETUP_BASE_SHIFT) | (0x0<<PLLE_MISC_SETUP_EX_SHIFT))
+
+#define PLLE_SS_CTRL 0x68
+#define PLLE_SS_INCINTRV_SHIFT 24
+#define PLLE_SS_INCINTRV_MASK (0x3f<<PLLE_SS_INCINTRV_SHIFT)
+#define PLLE_SS_INC_SHIFT 16
+#define PLLE_SS_INC_MASK (0xff<<PLLE_SS_INC_SHIFT)
+#define PLLE_SS_MAX_SHIFT 0
+#define PLLE_SS_MAX_MASK (0x1ff<<PLLE_SS_MAX_SHIFT)
+#define PLLE_SS_COEFFICIENTS_MASK \
+ (PLLE_SS_INCINTRV_MASK | PLLE_SS_INC_MASK | PLLE_SS_MAX_MASK)
+#define PLLE_SS_COEFFICIENTS_12MHZ \
+ ((0x18<<PLLE_SS_INCINTRV_SHIFT) | (0x1<<PLLE_SS_INC_SHIFT) | \
+ (0x24<<PLLE_SS_MAX_SHIFT))
+#define PLLE_SS_DISABLE ((1<<12) | (1<<11) | (1<<10))
+
+#define PLLE_AUX 0x48c
+#define PLLE_AUX_PLLP_SEL (1<<2)
+#define PLLE_AUX_CML_SATA_ENABLE (1<<1)
+#define PLLE_AUX_CML_PCIE_ENABLE (1<<0)
+
+#define PMC_SATA_PWRGT 0x1ac
+#define PMC_SATA_PWRGT_PLLE_IDDQ_VALUE (1<<5)
+#define PMC_SATA_PWRGT_PLLE_IDDQ_SWCTL (1<<4)
+
+#define ROUND_DIVIDER_UP 0
+#define ROUND_DIVIDER_DOWN 1
+
+/* FIXME: recommended safety delay after lock is detected */
+#define PLL_POST_LOCK_DELAY 100
+
+static bool tegra3_clk_is_parent_allowed(struct clk *c, struct clk *p);
+
+static int tegra3_clk_shared_bus_update(struct clk *bus);
+
+static struct clk *emc_bridge;
+
+static bool detach_shared_bus;
+module_param(detach_shared_bus, bool, 0644);
+
+/**
+* Structure defining the fields for USB UTMI clocks Parameters.
+*/
+struct utmi_clk_param
+{
+ /* Oscillator Frequency in KHz */
+ u32 osc_frequency;
+ /* UTMIP PLL Enable Delay Count */
+ u8 enable_delay_count;
+ /* UTMIP PLL Stable count */
+ u8 stable_count;
+ /* UTMIP PLL Active delay count */
+ u8 active_delay_count;
+ /* UTMIP PLL Xtal frequency count */
+ u8 xtal_freq_count;
+};
+
+static const struct utmi_clk_param utmi_parameters[] =
+{
+/* OSC_FREQUENCY, ENABLE_DLY, STABLE_CNT, ACTIVE_DLY, XTAL_FREQ_CNT */
+ {13000000, 0x02, 0x33, 0x05, 0x7F},
+ {19200000, 0x03, 0x4B, 0x06, 0xBB},
+ {12000000, 0x02, 0x2F, 0x04, 0x76},
+ {26000000, 0x04, 0x66, 0x09, 0xFE},
+ {16800000, 0x03, 0x41, 0x0A, 0xA4},
+};
+
+static void __iomem *reg_clk_base = IO_ADDRESS(TEGRA_CLK_RESET_BASE);
+static void __iomem *reg_pmc_base = IO_ADDRESS(TEGRA_PMC_BASE);
+static void __iomem *misc_gp_hidrev_base = IO_ADDRESS(TEGRA_APB_MISC_BASE);
+
+#define MISC_GP_HIDREV 0x804
+
+/*
+ * Some peripheral clocks share an enable bit, so refcount the enable bits
+ * in registers CLK_ENABLE_L, ... CLK_ENABLE_W
+ */
+static int tegra_periph_clk_enable_refcount[CLK_OUT_ENB_NUM * 32];
+
+#define clk_writel(value, reg) \
+ __raw_writel(value, (u32)reg_clk_base + (reg))
+#define clk_readl(reg) \
+ __raw_readl((u32)reg_clk_base + (reg))
+#define pmc_writel(value, reg) \
+ __raw_writel(value, (u32)reg_pmc_base + (reg))
+#define pmc_readl(reg) \
+ __raw_readl((u32)reg_pmc_base + (reg))
+#define chipid_readl() \
+ __raw_readl((u32)misc_gp_hidrev_base + MISC_GP_HIDREV)
+
+#define clk_writel_delay(value, reg) \
+ do { \
+ __raw_writel((value), (u32)reg_clk_base + (reg)); \
+ udelay(2); \
+ } while (0)
+
+
+static inline int clk_set_div(struct clk *c, u32 n)
+{
+ return clk_set_rate(c, (clk_get_rate(c->parent) + n-1) / n);
+}
+
+static inline u32 periph_clk_to_reg(
+ struct clk *c, u32 reg_L, u32 reg_V, int offs)
+{
+ u32 reg = c->u.periph.clk_num / 32;
+ BUG_ON(reg >= RST_DEVICES_NUM);
+ if (reg < 3) {
+ reg = reg_L + (reg * offs);
+ } else {
+ reg = reg_V + ((reg - 3) * offs);
+ }
+ return reg;
+}
+
+unsigned long clk_measure_input_freq(void)
+{
+ u32 clock_autodetect;
+ clk_writel(OSC_FREQ_DET_TRIG | 1, OSC_FREQ_DET);
+ do {} while (clk_readl(OSC_FREQ_DET_STATUS) & OSC_FREQ_DET_BUSY);
+ clock_autodetect = clk_readl(OSC_FREQ_DET_STATUS);
+ if (clock_autodetect >= 732 - 3 && clock_autodetect <= 732 + 3) {
+ return 12000000;
+ } else if (clock_autodetect >= 794 - 3 && clock_autodetect <= 794 + 3) {
+ return 13000000;
+ } else if (clock_autodetect >= 1172 - 3 && clock_autodetect <= 1172 + 3) {
+ return 19200000;
+ } else if (clock_autodetect >= 1587 - 3 && clock_autodetect <= 1587 + 3) {
+ return 26000000;
+ } else if (clock_autodetect >= 1025 - 3 && clock_autodetect <= 1025 + 3) {
+ return 16800000;
+ } else if (clock_autodetect >= 2344 - 3 && clock_autodetect <= 2344 + 3) {
+ return 38400000;
+ } else if (clock_autodetect >= 2928 - 3 && clock_autodetect <= 2928 + 3) {
+ return 48000000;
+ } else {
+ pr_err("%s: Unexpected clock autodetect value %d", __func__, clock_autodetect);
+ BUG();
+ return 0;
+ }
+}
+
+static int clk_div71_get_divider(unsigned long parent_rate, unsigned long rate,
+ u32 flags, u32 round_mode)
+{
+ s64 divider_u71 = parent_rate;
+ if (!rate)
+ return -EINVAL;
+
+ if (!(flags & DIV_U71_INT))
+ divider_u71 *= 2;
+ if (round_mode == ROUND_DIVIDER_UP)
+ divider_u71 += rate - 1;
+ do_div(divider_u71, rate);
+ if (flags & DIV_U71_INT)
+ divider_u71 *= 2;
+
+ if (divider_u71 - 2 < 0)
+ return 0;
+
+ if (divider_u71 - 2 > 255)
+ return -EINVAL;
+
+ return divider_u71 - 2;
+}
+
+static int clk_div16_get_divider(unsigned long parent_rate, unsigned long rate)
+{
+ s64 divider_u16;
+
+ divider_u16 = parent_rate;
+ if (!rate)
+ return -EINVAL;
+ divider_u16 += rate - 1;
+ do_div(divider_u16, rate);
+
+ if (divider_u16 - 1 < 0)
+ return 0;
+
+ if (divider_u16 - 1 > 0xFFFF)
+ return -EINVAL;
+
+ return divider_u16 - 1;
+}
+
+/* clk_m functions */
+static unsigned long tegra3_clk_m_autodetect_rate(struct clk *c)
+{
+ u32 osc_ctrl = clk_readl(OSC_CTRL);
+ u32 auto_clock_control = osc_ctrl & ~OSC_CTRL_OSC_FREQ_MASK;
+ u32 pll_ref_div = osc_ctrl & OSC_CTRL_PLL_REF_DIV_MASK;
+
+ c->rate = clk_measure_input_freq();
+ switch (c->rate) {
+ case 12000000:
+ auto_clock_control |= OSC_CTRL_OSC_FREQ_12MHZ;
+ BUG_ON(pll_ref_div != OSC_CTRL_PLL_REF_DIV_1);
+ break;
+ case 13000000:
+ auto_clock_control |= OSC_CTRL_OSC_FREQ_13MHZ;
+ BUG_ON(pll_ref_div != OSC_CTRL_PLL_REF_DIV_1);
+ break;
+ case 19200000:
+ auto_clock_control |= OSC_CTRL_OSC_FREQ_19_2MHZ;
+ BUG_ON(pll_ref_div != OSC_CTRL_PLL_REF_DIV_1);
+ break;
+ case 26000000:
+ auto_clock_control |= OSC_CTRL_OSC_FREQ_26MHZ;
+ BUG_ON(pll_ref_div != OSC_CTRL_PLL_REF_DIV_1);
+ break;
+ case 16800000:
+ auto_clock_control |= OSC_CTRL_OSC_FREQ_16_8MHZ;
+ BUG_ON(pll_ref_div != OSC_CTRL_PLL_REF_DIV_1);
+ break;
+ case 38400000:
+ auto_clock_control |= OSC_CTRL_OSC_FREQ_38_4MHZ;
+ BUG_ON(pll_ref_div != OSC_CTRL_PLL_REF_DIV_2);
+ break;
+ case 48000000:
+ auto_clock_control |= OSC_CTRL_OSC_FREQ_48MHZ;
+ BUG_ON(pll_ref_div != OSC_CTRL_PLL_REF_DIV_4);
+ break;
+ default:
+ pr_err("%s: Unexpected clock rate %ld", __func__, c->rate);
+ BUG();
+ }
+ clk_writel(auto_clock_control, OSC_CTRL);
+ return c->rate;
+}
+
+static void tegra3_clk_m_init(struct clk *c)
+{
+ pr_debug("%s on clock %s\n", __func__, c->name);
+ tegra3_clk_m_autodetect_rate(c);
+}
+
+static int tegra3_clk_m_enable(struct clk *c)
+{
+ pr_debug("%s on clock %s\n", __func__, c->name);
+ return 0;
+}
+
+static void tegra3_clk_m_disable(struct clk *c)
+{
+ pr_debug("%s on clock %s\n", __func__, c->name);
+ WARN(1, "Attempting to disable main SoC clock\n");
+}
+
+static struct clk_ops tegra_clk_m_ops = {
+ .init = tegra3_clk_m_init,
+ .enable = tegra3_clk_m_enable,
+ .disable = tegra3_clk_m_disable,
+};
+
+static struct clk_ops tegra_clk_m_div_ops = {
+ .enable = tegra3_clk_m_enable,
+};
+
+/* PLL reference divider functions */
+static void tegra3_pll_ref_init(struct clk *c)
+{
+ u32 pll_ref_div = clk_readl(OSC_CTRL) & OSC_CTRL_PLL_REF_DIV_MASK;
+ pr_debug("%s on clock %s\n", __func__, c->name);
+
+ switch (pll_ref_div) {
+ case OSC_CTRL_PLL_REF_DIV_1:
+ c->div = 1;
+ break;
+ case OSC_CTRL_PLL_REF_DIV_2:
+ c->div = 2;
+ break;
+ case OSC_CTRL_PLL_REF_DIV_4:
+ c->div = 4;
+ break;
+ default:
+ pr_err("%s: Invalid pll ref divider %d", __func__, pll_ref_div);
+ BUG();
+ }
+ c->mul = 1;
+ c->state = ON;
+}
+
+static struct clk_ops tegra_pll_ref_ops = {
+ .init = tegra3_pll_ref_init,
+ .enable = tegra3_clk_m_enable,
+ .disable = tegra3_clk_m_disable,
+};
+
+/* super clock functions */
+/* "super clocks" on tegra3 have two-stage muxes, fractional 7.1 divider and
+ * clock skipping super divider. We will ignore the clock skipping divider,
+ * since we can't lower the voltage when using the clock skip, but we can if
+ * we lower the PLL frequency. We will use 7.1 divider for CPU super-clock
+ * only when its parent is a fixed rate PLL, since we can't change PLL rate
+ * in this case.
+ */
+static void tegra3_super_clk_init(struct clk *c)
+{
+ u32 val;
+ int source;
+ int shift;
+ const struct clk_mux_sel *sel;
+
+ val = clk_readl(c->reg + SUPER_CLK_MUX);
+ c->state = ON;
+ BUG_ON(((val & SUPER_STATE_MASK) != SUPER_STATE_RUN) &&
+ ((val & SUPER_STATE_MASK) != SUPER_STATE_IDLE));
+ shift = ((val & SUPER_STATE_MASK) == SUPER_STATE_IDLE) ?
+ SUPER_IDLE_SOURCE_SHIFT : SUPER_RUN_SOURCE_SHIFT;
+ source = (val >> shift) & SUPER_SOURCE_MASK;
+ if (c->flags & DIV_2)
+ source |= val & SUPER_LP_DIV2_BYPASS;
+ for (sel = c->inputs; sel->input != NULL; sel++) {
+ if (sel->value == source)
+ break;
+ }
+ BUG_ON(sel->input == NULL);
+ c->parent = sel->input;
+
+ if (c->flags & DIV_U71) {
+ /* Init safe 7.1 divider value (does not affect PLLX path).
+ Super skipper is enabled to be ready for emergency throttle,
+ but set 1:1 */
+ val = SUPER_CLOCK_SKIP_ENABLE |
+ (SUPER_CLOCK_DIV_U71_MIN << SUPER_CLOCK_DIV_U71_SHIFT);
+ clk_writel(val, c->reg + SUPER_CLK_DIVIDER);
+ c->mul = 2;
+ c->div = 2;
+ if (!(c->parent->flags & PLLX))
+ c->div += SUPER_CLOCK_DIV_U71_MIN;
+ }
+ else
+ clk_writel(0, c->reg + SUPER_CLK_DIVIDER);
+}
+
+static int tegra3_super_clk_enable(struct clk *c)
+{
+ return 0;
+}
+
+static void tegra3_super_clk_disable(struct clk *c)
+{
+ /* since tegra 3 has 2 CPU super clocks - low power lp-mode clock and
+ geared up g-mode super clock - mode switch may request to disable
+ either of them; accept request with no affect on h/w */
+}
+
+static int tegra3_super_clk_set_parent(struct clk *c, struct clk *p)
+{
+ u32 val;
+ const struct clk_mux_sel *sel;
+ int shift;
+
+ val = clk_readl(c->reg + SUPER_CLK_MUX);;
+ BUG_ON(((val & SUPER_STATE_MASK) != SUPER_STATE_RUN) &&
+ ((val & SUPER_STATE_MASK) != SUPER_STATE_IDLE));
+ shift = ((val & SUPER_STATE_MASK) == SUPER_STATE_IDLE) ?
+ SUPER_IDLE_SOURCE_SHIFT : SUPER_RUN_SOURCE_SHIFT;
+ for (sel = c->inputs; sel->input != NULL; sel++) {
+ if (sel->input == p) {
+ /* For LP mode super-clock switch between PLLX direct
+ and divided-by-2 outputs is allowed only when other
+ than PLLX clock source is current parent */
+ if ((c->flags & DIV_2) && (p->flags & PLLX) &&
+ ((sel->value ^ val) & SUPER_LP_DIV2_BYPASS)) {
+ if (c->parent->flags & PLLX)
+ return -EINVAL;
+ val ^= SUPER_LP_DIV2_BYPASS;
+ clk_writel_delay(val, c->reg);
+ }
+ val &= ~(SUPER_SOURCE_MASK << shift);
+ val |= (sel->value & SUPER_SOURCE_MASK) << shift;
+
+ /* 7.1 divider for CPU super-clock does not affect
+ PLLX path */
+ if (c->flags & DIV_U71) {
+ u32 div = 0;
+ if (!(p->flags & PLLX)) {
+ div = clk_readl(c->reg +
+ SUPER_CLK_DIVIDER);
+ div &= SUPER_CLOCK_DIV_U71_MASK;
+ div >>= SUPER_CLOCK_DIV_U71_SHIFT;
+ }
+ c->div = div + 2;
+ c->mul = 2;
+ }
+
+ if (c->refcnt)
+ clk_enable(p);
+
+ clk_writel_delay(val, c->reg);
+
+ if (c->refcnt && c->parent)
+ clk_disable(c->parent);
+
+ clk_reparent(c, p);
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
+
+static DEFINE_SPINLOCK(super_divider_lock);
+
+static void tegra3_super_clk_divider_update(struct clk *c, u8 div)
+{
+ u32 val;
+ unsigned long flags;
+
+ spin_lock_irqsave(&super_divider_lock, flags);
+ val = clk_readl(c->reg + SUPER_CLK_DIVIDER);
+ val &= ~SUPER_CLOCK_DIV_U71_MASK;
+ val |= div << SUPER_CLOCK_DIV_U71_SHIFT;
+ clk_writel(val, c->reg + SUPER_CLK_DIVIDER);
+ spin_unlock_irqrestore(&super_divider_lock, flags);
+ udelay(2);
+}
+
+static void tegra3_super_clk_skipper_update(struct clk *c, u8 nomin, u8 denom)
+{
+ u32 val;
+ unsigned long flags;
+
+ spin_lock_irqsave(&super_divider_lock, flags);
+ val = clk_readl(c->reg + SUPER_CLK_DIVIDER);
+ val &= ~SUPER_CLOCK_SKIP_MASK;
+ val |= (nomin << SUPER_CLOCK_SKIP_NOMIN_SHIFT) |
+ (denom << SUPER_CLOCK_SKIP_DENOM_SHIFT);
+ clk_writel(val, c->reg + SUPER_CLK_DIVIDER);
+ spin_unlock_irqrestore(&super_divider_lock, flags);
+}
+
+/*
+ * Do not use super clocks "skippers", since dividing using a clock skipper
+ * does not allow the voltage to be scaled down. Instead adjust the rate of
+ * the parent clock. This requires that the parent of a super clock have no
+ * other children, otherwise the rate will change underneath the other
+ * children. Special case: if fixed rate PLL is CPU super clock parent the
+ * rate of this PLL can't be changed, and it has many other children. In
+ * this case use 7.1 fractional divider to adjust the super clock rate.
+ */
+static int tegra3_super_clk_set_rate(struct clk *c, unsigned long rate)
+{
+ if ((c->flags & DIV_U71) && (c->parent->flags & PLL_FIXED)) {
+ int div = clk_div71_get_divider(c->parent->u.pll.fixed_rate,
+ rate, c->flags, ROUND_DIVIDER_DOWN);
+ div = max(div, SUPER_CLOCK_DIV_U71_MIN);
+ tegra3_super_clk_divider_update(c, div);
+ c->div = div + 2;
+ c->mul = 2;
+ return 0;
+ }
+ return clk_set_rate(c->parent, rate);
+}
+
+static struct clk_ops tegra_super_ops = {
+ .init = tegra3_super_clk_init,
+ .enable = tegra3_super_clk_enable,
+ .disable = tegra3_super_clk_disable,
+ .set_parent = tegra3_super_clk_set_parent,
+ .set_rate = tegra3_super_clk_set_rate,
+};
+
+static int tegra3_twd_clk_set_rate(struct clk *c, unsigned long rate)
+{
+ /* The input value 'rate' is the clock rate of the CPU complex. */
+ c->rate = (rate * c->mul) / c->div;
+ return 0;
+}
+
+static struct clk_ops tegra3_twd_ops = {
+ .set_rate = tegra3_twd_clk_set_rate,
+};
+
+static struct clk tegra3_clk_twd = {
+ /* NOTE: The twd clock must have *NO* parent. It's rate is directly
+ updated by tegra3_cpu_cmplx_clk_set_rate() because the
+ frequency change notifer for the twd is called in an
+ atomic context which cannot take a mutex. */
+ .name = "twd",
+ .ops = &tegra3_twd_ops,
+ .max_rate = 1400000000, /* Same as tegra_clk_cpu_cmplx.max_rate */
+ .mul = 1,
+ .div = 2,
+};
+
+/* virtual cpu clock functions */
+/* some clocks can not be stopped (cpu, memory bus) while the SoC is running.
+ To change the frequency of these clocks, the parent pll may need to be
+ reprogrammed, so the clock must be moved off the pll, the pll reprogrammed,
+ and then the clock moved back to the pll. To hide this sequence, a virtual
+ clock handles it.
+ */
+static void tegra3_cpu_clk_init(struct clk *c)
+{
+ c->state = (!is_lp_cluster() == (c->u.cpu.mode == MODE_G))? ON : OFF;
+}
+
+static int tegra3_cpu_clk_enable(struct clk *c)
+{
+ return 0;
+}
+
+static void tegra3_cpu_clk_disable(struct clk *c)
+{
+ /* since tegra 3 has 2 virtual CPU clocks - low power lp-mode clock
+ and geared up g-mode clock - mode switch may request to disable
+ either of them; accept request with no affect on h/w */
+}
+
+static int tegra3_cpu_clk_set_rate(struct clk *c, unsigned long rate)
+{
+ int ret = 0;
+
+ /* Hardware clock control is not possible on FPGA platforms.
+ Report success so that upper level layers don't complain
+ needlessly. */
+#ifndef CONFIG_TEGRA_FPGA_PLATFORM
+ if (c->dvfs) {
+ if (!c->dvfs->dvfs_rail)
+ return -ENOSYS;
+ else if ((!c->dvfs->dvfs_rail->reg) &&
+ (clk_get_rate_locked(c) < rate)) {
+ WARN(1, "Increasing CPU rate while regulator is not"
+ " ready may overclock CPU\n");
+ return -ENOSYS;
+ }
+ }
+
+ /*
+ * Take an extra reference to the main pll so it doesn't turn
+ * off when we move the cpu off of it
+ */
+ clk_enable(c->u.cpu.main);
+
+ ret = clk_set_parent(c->parent, c->u.cpu.backup);
+ if (ret) {
+ pr_err("Failed to switch cpu to clock %s\n", c->u.cpu.backup->name);
+ goto out;
+ }
+
+ ret = clk_set_rate(c->parent, rate);
+ if (!ret && (rate <= clk_get_rate(c->parent)))
+ goto out;
+
+ if (rate != clk_get_rate(c->u.cpu.main)) {
+ ret = clk_set_rate(c->u.cpu.main, rate);
+ if (ret) {
+ pr_err("Failed to change cpu pll to %lu\n", rate);
+ goto out;
+ }
+ }
+
+ ret = clk_set_parent(c->parent, c->u.cpu.main);
+ if (ret) {
+ pr_err("Failed to switch cpu to clock %s\n", c->u.cpu.main->name);
+ goto out;
+ }
+
+out:
+ clk_disable(c->u.cpu.main);
+#endif
+ return ret;
+}
+
+static struct clk_ops tegra_cpu_ops = {
+ .init = tegra3_cpu_clk_init,
+ .enable = tegra3_cpu_clk_enable,
+ .disable = tegra3_cpu_clk_disable,
+ .set_rate = tegra3_cpu_clk_set_rate,
+};
+
+
+static void tegra3_cpu_cmplx_clk_init(struct clk *c)
+{
+ int i = !!is_lp_cluster();
+
+ BUG_ON(c->inputs[0].input->u.cpu.mode != MODE_G);
+ BUG_ON(c->inputs[1].input->u.cpu.mode != MODE_LP);
+ c->parent = c->inputs[i].input;
+}
+
+/* cpu complex clock provides second level vitualization (on top of
+ cpu virtual cpu rate control) in order to hide the CPU mode switch
+ sequence */
+#if PARAMETERIZE_CLUSTER_SWITCH
+static unsigned int switch_delay;
+static unsigned int switch_flags;
+static DEFINE_SPINLOCK(parameters_lock);
+
+void tegra_cluster_switch_set_parameters(unsigned int us, unsigned int flags)
+{
+ spin_lock(&parameters_lock);
+ switch_delay = us;
+ switch_flags = flags;
+ spin_unlock(&parameters_lock);
+}
+#endif
+
+static int tegra3_cpu_cmplx_clk_enable(struct clk *c)
+{
+ return 0;
+}
+
+static void tegra3_cpu_cmplx_clk_disable(struct clk *c)
+{
+ pr_debug("%s on clock %s\n", __func__, c->name);
+
+ /* oops - don't disable the CPU complex clock! */
+ BUG();
+}
+
+static int tegra3_cpu_cmplx_clk_set_rate(struct clk *c, unsigned long rate)
+{
+ unsigned long flags;
+ int ret;
+ struct clk *parent = c->parent;
+
+ if (!parent->ops || !parent->ops->set_rate)
+ return -ENOSYS;
+
+ clk_lock_save(parent, &flags);
+
+ ret = clk_set_rate_locked(parent, rate);
+
+ /* We can't parent the twd to directly to the CPU complex because
+ the TWD frequency update notifier is called in an atomic context
+ and the CPU frequency update requires a mutex. Update the twd
+ clock rate with the new CPU complex rate. */
+ clk_set_rate(&tegra3_clk_twd, clk_get_rate_locked(parent));
+
+ clk_unlock_restore(parent, &flags);
+
+ return ret;
+}
+
+static int tegra3_cpu_cmplx_clk_set_parent(struct clk *c, struct clk *p)
+{
+ int ret;
+ unsigned int flags, delay;
+ const struct clk_mux_sel *sel;
+ unsigned long rate = clk_get_rate(c->parent);
+
+ pr_debug("%s: %s %s\n", __func__, c->name, p->name);
+ BUG_ON(c->parent->u.cpu.mode != (is_lp_cluster() ? MODE_LP : MODE_G));
+
+ for (sel = c->inputs; sel->input != NULL; sel++) {
+ if (sel->input == p)
+ break;
+ }
+ if (!sel->input)
+ return -EINVAL;
+
+#if PARAMETERIZE_CLUSTER_SWITCH
+ spin_lock(&parameters_lock);
+ flags = switch_flags;
+ delay = switch_delay;
+ switch_flags = 0;
+ spin_unlock(&parameters_lock);
+
+ if (flags) {
+ /* over-clocking after the switch - allow, but lower rate */
+ if (rate > p->max_rate) {
+ rate = p->max_rate;
+ ret = clk_set_rate(c->parent, rate);
+ if (ret) {
+ pr_err("%s: Failed to set rate %lu for %s\n",
+ __func__, rate, p->name);
+ return ret;
+ }
+ }
+ } else
+#endif
+ {
+ if (p == c->parent) /* already switched - exit*/
+ return 0;
+
+ if (rate > p->max_rate) { /* over-clocking - no switch */
+ pr_warn("%s: No %s mode switch to %s at rate %lu\n",
+ __func__, c->name, p->name, rate);
+ return -ECANCELED;
+ }
+ flags = TEGRA_POWER_CLUSTER_IMMEDIATE;
+ delay = 0;
+ }
+ flags |= (p->u.cpu.mode == MODE_LP) ? TEGRA_POWER_CLUSTER_LP :
+ TEGRA_POWER_CLUSTER_G;
+
+ /* Since in both LP and G mode CPU main and backup sources are the
+ same, set rate on the new parent just synchronizes super-clock
+ muxes before mode switch with no PLL re-locking */
+ ret = clk_set_rate(p, rate);
+ if (ret) {
+ pr_err("%s: Failed to set rate %lu for %s\n",
+ __func__, rate, p->name);
+ return ret;
+ }
+
+ /* Enabling new parent scales new mode voltage rail in advanvce
+ before the switch happens*/
+ if (c->refcnt)
+ clk_enable(p);
+
+ /* switch CPU mode */
+ ret = tegra_cluster_control(delay, flags);
+ if (ret) {
+ if (c->refcnt)
+ clk_disable(p);
+ pr_err("%s: Failed to switch %s mode to %s\n",
+ __func__, c->name, p->name);
+ return ret;
+ }
+
+ /* Disabling old parent scales old mode voltage rail */
+ if (c->refcnt && c->parent)
+ clk_disable(c->parent);
+
+ clk_reparent(c, p);
+ return 0;
+}
+
+static long tegra3_cpu_cmplx_round_rate(struct clk *c,
+ unsigned long rate)
+{
+ if (rate > c->parent->max_rate)
+ rate = c->parent->max_rate;
+ else if (rate < c->parent->min_rate)
+ rate = c->parent->min_rate;
+ return rate;
+}
+
+static struct clk_ops tegra_cpu_cmplx_ops = {
+ .init = tegra3_cpu_cmplx_clk_init,
+ .enable = tegra3_cpu_cmplx_clk_enable,
+ .disable = tegra3_cpu_cmplx_clk_disable,
+ .set_rate = tegra3_cpu_cmplx_clk_set_rate,
+ .set_parent = tegra3_cpu_cmplx_clk_set_parent,
+ .round_rate = tegra3_cpu_cmplx_round_rate,
+};
+
+/* virtual cop clock functions. Used to acquire the fake 'cop' clock to
+ * reset the COP block (i.e. AVP) */
+static void tegra3_cop_clk_reset(struct clk *c, bool assert)
+{
+ unsigned long reg = assert ? RST_DEVICES_SET_L : RST_DEVICES_CLR_L;
+
+ pr_debug("%s %s\n", __func__, assert ? "assert" : "deassert");
+ clk_writel(1 << 1, reg);
+}
+
+static struct clk_ops tegra_cop_ops = {
+ .reset = tegra3_cop_clk_reset,
+};
+
+/* bus clock functions */
+static void tegra3_bus_clk_init(struct clk *c)
+{
+ u32 val = clk_readl(c->reg);
+ c->state = ((val >> c->reg_shift) & BUS_CLK_DISABLE) ? OFF : ON;
+ c->div = ((val >> c->reg_shift) & BUS_CLK_DIV_MASK) + 1;
+ c->mul = 1;
+}
+
+static int tegra3_bus_clk_enable(struct clk *c)
+{
+ u32 val = clk_readl(c->reg);
+ val &= ~(BUS_CLK_DISABLE << c->reg_shift);
+ clk_writel(val, c->reg);
+ return 0;
+}
+
+static void tegra3_bus_clk_disable(struct clk *c)
+{
+ u32 val = clk_readl(c->reg);
+ val |= BUS_CLK_DISABLE << c->reg_shift;
+ clk_writel(val, c->reg);
+}
+
+static int tegra3_bus_clk_set_rate(struct clk *c, unsigned long rate)
+{
+ u32 val = clk_readl(c->reg);
+ unsigned long parent_rate = clk_get_rate(c->parent);
+ int i;
+ for (i = 1; i <= 4; i++) {
+ if (rate >= parent_rate / i) {
+ val &= ~(BUS_CLK_DIV_MASK << c->reg_shift);
+ val |= (i - 1) << c->reg_shift;
+ clk_writel(val, c->reg);
+ c->div = i;
+ c->mul = 1;
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
+
+static struct clk_ops tegra_bus_ops = {
+ .init = tegra3_bus_clk_init,
+ .enable = tegra3_bus_clk_enable,
+ .disable = tegra3_bus_clk_disable,
+ .set_rate = tegra3_bus_clk_set_rate,
+};
+
+/* Virtual system bus complex clock is used to hide the sequence of
+ changing sclk/hclk/pclk parents and dividers to configure requested
+ sclk target rate. */
+static void tegra3_sbus_cmplx_init(struct clk *c)
+{
+ unsigned long rate;
+
+ c->max_rate = c->parent->max_rate;
+ c->min_rate = c->parent->min_rate;
+
+ /* Threshold must be an exact proper factor of low range parent,
+ and both low/high range parents have 7.1 fractional dividers */
+ rate = clk_get_rate(c->u.system.sclk_low->parent);
+ if (c->u.system.threshold) {
+ BUG_ON(c->u.system.threshold > rate) ;
+ BUG_ON((rate % c->u.system.threshold) != 0);
+ }
+ BUG_ON(!(c->u.system.sclk_low->flags & DIV_U71));
+ BUG_ON(!(c->u.system.sclk_high->flags & DIV_U71));
+}
+
+/* This special sbus round function is implemented because:
+ *
+ * (a) fractional dividers can not be used to derive system bus clock with one
+ * exception: 1 : 2.5 divider is allowed at 1.2V and above (and we do need this
+ * divider to reach top sbus frequencies from high frequency source).
+ *
+ * (b) since sbus is a shared bus, and its frequency is set to the highest
+ * enabled shared_bus_user clock, the target rate should be rounded up divider
+ * ladder (if max limit allows it) - for pll_div and peripheral_div common is
+ * rounding down - special case again.
+ *
+ * Note that final rate is trimmed (not rounded up) to avoid spiraling up in
+ * recursive calls. Lost 1Hz is added in tegra3_sbus_cmplx_set_rate before
+ * actually setting divider rate.
+ */
+static unsigned long sclk_high_2_5_rate;
+static bool sclk_high_2_5_valid;
+
+static long tegra3_sbus_cmplx_round_rate(struct clk *c, unsigned long rate)
+{
+ int i, divider;
+ unsigned long source_rate, round_rate;
+ struct clk *new_parent;
+
+ rate = max(rate, c->min_rate);
+
+ if (!sclk_high_2_5_rate) {
+ source_rate = clk_get_rate(c->u.system.sclk_high->parent);
+ sclk_high_2_5_rate = 2 * source_rate / 5;
+ i = tegra_dvfs_predict_millivolts(c, sclk_high_2_5_rate);
+ if (!IS_ERR_VALUE(i) && (i >= 1200) &&
+ (sclk_high_2_5_rate <= c->max_rate))
+ sclk_high_2_5_valid = true;
+ }
+
+ new_parent = (rate <= c->u.system.threshold) ?
+ c->u.system.sclk_low : c->u.system.sclk_high;
+ source_rate = clk_get_rate(new_parent->parent);
+
+ divider = clk_div71_get_divider(source_rate, rate,
+ new_parent->flags | DIV_U71_INT, ROUND_DIVIDER_DOWN);
+ if (divider < 0)
+ return divider;
+
+ round_rate = source_rate * 2 / (divider + 2);
+ if (round_rate > c->max_rate) {
+ divider += 2;
+ round_rate = source_rate * 2 / (divider + 2);
+ }
+
+ if (new_parent == c->u.system.sclk_high) {
+ /* Check if 1 : 2.5 ratio provides better approximation */
+ if (sclk_high_2_5_valid) {
+ if (((sclk_high_2_5_rate < round_rate) &&
+ (sclk_high_2_5_rate >= rate)) ||
+ ((round_rate < sclk_high_2_5_rate) &&
+ (round_rate < rate)))
+ round_rate = sclk_high_2_5_rate;
+ }
+
+ if (round_rate <= c->u.system.threshold)
+ round_rate = c->u.system.threshold;
+ }
+ return round_rate;
+}
+
+static int tegra3_sbus_cmplx_set_rate(struct clk *c, unsigned long rate)
+{
+ int ret;
+ struct clk *new_parent;
+
+ /* - select the appropriate sclk parent
+ - keep hclk at the same rate as sclk
+ - set pclk at 1:2 rate of hclk unless pclk minimum is violated,
+ in the latter case switch to 1:1 ratio */
+
+ if (rate >= c->u.system.pclk->min_rate * 2) {
+ ret = clk_set_div(c->u.system.pclk, 2);
+ if (ret) {
+ pr_err("Failed to set 1 : 2 pclk divider\n");
+ return ret;
+ }
+ }
+
+ new_parent = (rate <= c->u.system.threshold) ?
+ c->u.system.sclk_low : c->u.system.sclk_high;
+
+ ret = clk_set_rate(new_parent, rate + 1);
+ if (ret) {
+ pr_err("Failed to set sclk source %s to %lu\n",
+ new_parent->name, rate);
+ return ret;
+ }
+
+ if (new_parent != clk_get_parent(c->parent)) {
+ ret = clk_set_parent(c->parent, new_parent);
+ if (ret) {
+ pr_err("Failed to switch sclk source to %s\n",
+ new_parent->name);
+ return ret;
+ }
+ }
+
+ if (rate < c->u.system.pclk->min_rate * 2) {
+ ret = clk_set_div(c->u.system.pclk, 1);
+ if (ret) {
+ pr_err("Failed to set 1 : 1 pclk divider\n");
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static struct clk_ops tegra_sbus_cmplx_ops = {
+ .init = tegra3_sbus_cmplx_init,
+ .set_rate = tegra3_sbus_cmplx_set_rate,
+ .round_rate = tegra3_sbus_cmplx_round_rate,
+ .shared_bus_update = tegra3_clk_shared_bus_update,
+};
+
+/* Blink output functions */
+
+static void tegra3_blink_clk_init(struct clk *c)
+{
+ u32 val;
+
+ val = pmc_readl(PMC_CTRL);
+ c->state = (val & PMC_CTRL_BLINK_ENB) ? ON : OFF;
+ c->mul = 1;
+ val = pmc_readl(c->reg);
+
+ if (val & PMC_BLINK_TIMER_ENB) {
+ unsigned int on_off;
+
+ on_off = (val >> PMC_BLINK_TIMER_DATA_ON_SHIFT) &
+ PMC_BLINK_TIMER_DATA_ON_MASK;
+ val >>= PMC_BLINK_TIMER_DATA_OFF_SHIFT;
+ val &= PMC_BLINK_TIMER_DATA_OFF_MASK;
+ on_off += val;
+ /* each tick in the blink timer is 4 32KHz clocks */
+ c->div = on_off * 4;
+ } else {
+ c->div = 1;
+ }
+}
+
+static int tegra3_blink_clk_enable(struct clk *c)
+{
+ u32 val;
+
+ val = pmc_readl(PMC_DPD_PADS_ORIDE);
+ pmc_writel(val | PMC_DPD_PADS_ORIDE_BLINK_ENB, PMC_DPD_PADS_ORIDE);
+
+ val = pmc_readl(PMC_CTRL);
+ pmc_writel(val | PMC_CTRL_BLINK_ENB, PMC_CTRL);
+
+ return 0;
+}
+
+static void tegra3_blink_clk_disable(struct clk *c)
+{
+ u32 val;
+
+ val = pmc_readl(PMC_CTRL);
+ pmc_writel(val & ~PMC_CTRL_BLINK_ENB, PMC_CTRL);
+
+ val = pmc_readl(PMC_DPD_PADS_ORIDE);
+ pmc_writel(val & ~PMC_DPD_PADS_ORIDE_BLINK_ENB, PMC_DPD_PADS_ORIDE);
+}
+
+static int tegra3_blink_clk_set_rate(struct clk *c, unsigned long rate)
+{
+ unsigned long parent_rate = clk_get_rate(c->parent);
+ if (rate >= parent_rate) {
+ c->div = 1;
+ pmc_writel(0, c->reg);
+ } else {
+ unsigned int on_off;
+ u32 val;
+
+ on_off = DIV_ROUND_UP(parent_rate / 8, rate);
+ c->div = on_off * 8;
+
+ val = (on_off & PMC_BLINK_TIMER_DATA_ON_MASK) <<
+ PMC_BLINK_TIMER_DATA_ON_SHIFT;
+ on_off &= PMC_BLINK_TIMER_DATA_OFF_MASK;
+ on_off <<= PMC_BLINK_TIMER_DATA_OFF_SHIFT;
+ val |= on_off;
+ val |= PMC_BLINK_TIMER_ENB;
+ pmc_writel(val, c->reg);
+ }
+
+ return 0;
+}
+
+static struct clk_ops tegra_blink_clk_ops = {
+ .init = &tegra3_blink_clk_init,
+ .enable = &tegra3_blink_clk_enable,
+ .disable = &tegra3_blink_clk_disable,
+ .set_rate = &tegra3_blink_clk_set_rate,
+};
+
+/* PLL Functions */
+static int tegra3_pll_clk_wait_for_lock(struct clk *c, u32 lock_reg, u32 lock_bit)
+{
+#if USE_PLL_LOCK_BITS
+ int i;
+ for (i = 0; i < c->u.pll.lock_delay; i++) {
+ if (clk_readl(lock_reg) & lock_bit) {
+ udelay(PLL_POST_LOCK_DELAY);
+ return 0;
+ }
+ udelay(2); /* timeout = 2 * lock time */
+ }
+ pr_err("Timed out waiting for lock bit on pll %s", c->name);
+ return -1;
+#endif
+ udelay(c->u.pll.lock_delay);
+
+ return 0;
+}
+
+
+static void tegra3_utmi_param_configure(struct clk *c)
+{
+ u32 reg;
+ int i;
+ unsigned long main_rate =
+ clk_get_rate(c->parent->parent);
+
+ for (i = 0; i < ARRAY_SIZE(utmi_parameters); i++) {
+ if (main_rate == utmi_parameters[i].osc_frequency) {
+ break;
+ }
+ }
+
+ if (i >= ARRAY_SIZE(utmi_parameters)) {
+ pr_err("%s: Unexpected main rate %lu\n", __func__, main_rate);
+ return;
+ }
+
+ reg = clk_readl(UTMIP_PLL_CFG2);
+
+ /* Program UTMIP PLL stable and active counts */
+ /* [FIXME] arclk_rst.h says WRONG! This should be 1ms -> 0x50 Check! */
+ reg &= ~UTMIP_PLL_CFG2_STABLE_COUNT(~0);
+ reg |= UTMIP_PLL_CFG2_STABLE_COUNT(
+ utmi_parameters[i].stable_count);
+
+ reg &= ~UTMIP_PLL_CFG2_ACTIVE_DLY_COUNT(~0);
+
+ reg |= UTMIP_PLL_CFG2_ACTIVE_DLY_COUNT(
+ utmi_parameters[i].active_delay_count);
+
+ /* Remove power downs from UTMIP PLL control bits */
+ reg &= ~UTMIP_PLL_CFG2_FORCE_PD_SAMP_A_POWERDOWN;
+ reg &= ~UTMIP_PLL_CFG2_FORCE_PD_SAMP_B_POWERDOWN;
+ reg &= ~UTMIP_PLL_CFG2_FORCE_PD_SAMP_C_POWERDOWN;
+
+ clk_writel(reg, UTMIP_PLL_CFG2);
+
+ /* Program UTMIP PLL delay and oscillator frequency counts */
+ reg = clk_readl(UTMIP_PLL_CFG1);
+ reg &= ~UTMIP_PLL_CFG1_ENABLE_DLY_COUNT(~0);
+
+ reg |= UTMIP_PLL_CFG1_ENABLE_DLY_COUNT(
+ utmi_parameters[i].enable_delay_count);
+
+ reg &= ~UTMIP_PLL_CFG1_XTAL_FREQ_COUNT(~0);
+ reg |= UTMIP_PLL_CFG1_XTAL_FREQ_COUNT(
+ utmi_parameters[i].xtal_freq_count);
+
+ /* Remove power downs from UTMIP PLL control bits */
+ reg &= ~UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERDOWN;
+ reg &= ~UTMIP_PLL_CFG1_FORCE_PLL_ACTIVE_POWERDOWN;
+ reg &= ~UTMIP_PLL_CFG1_FORCE_PLLU_POWERDOWN;
+
+ clk_writel(reg, UTMIP_PLL_CFG1);
+}
+
+static void tegra3_pll_clk_init(struct clk *c)
+{
+ u32 val = clk_readl(c->reg + PLL_BASE);
+
+ c->state = (val & PLL_BASE_ENABLE) ? ON : OFF;
+
+ if (c->flags & PLL_FIXED && !(val & PLL_BASE_OVERRIDE)) {
+ const struct clk_pll_freq_table *sel;
+ unsigned long input_rate = clk_get_rate(c->parent);
+ for (sel = c->u.pll.freq_table; sel->input_rate != 0; sel++) {
+ if (sel->input_rate == input_rate &&
+ sel->output_rate == c->u.pll.fixed_rate) {
+ c->mul = sel->n;
+ c->div = sel->m * sel->p;
+ return;
+ }
+ }
+ pr_err("Clock %s has unknown fixed frequency\n", c->name);
+ BUG();
+ } else if (val & PLL_BASE_BYPASS) {
+ c->mul = 1;
+ c->div = 1;
+ } else {
+ c->mul = (val & PLL_BASE_DIVN_MASK) >> PLL_BASE_DIVN_SHIFT;
+ c->div = (val & PLL_BASE_DIVM_MASK) >> PLL_BASE_DIVM_SHIFT;
+ if (c->flags & PLLU)
+ c->div *= (val & PLLU_BASE_POST_DIV) ? 1 : 2;
+ else
+ c->div *= (0x1 << ((val & PLL_BASE_DIVP_MASK) >>
+ PLL_BASE_DIVP_SHIFT));
+ if (c->flags & PLL_FIXED) {
+ unsigned long rate = clk_get_rate_locked(c);
+ BUG_ON(rate != c->u.pll.fixed_rate);
+ }
+ }
+
+ if (c->flags & PLLU) {
+ tegra3_utmi_param_configure(c);
+ }
+}
+
+static int tegra3_pll_clk_enable(struct clk *c)
+{
+ u32 val;
+ pr_debug("%s on clock %s\n", __func__, c->name);
+
+#if USE_PLL_LOCK_BITS
+ val = clk_readl(c->reg + PLL_MISC(c));
+ val |= PLL_MISC_LOCK_ENABLE(c);
+ clk_writel(val, c->reg + PLL_MISC(c));
+#endif
+ val = clk_readl(c->reg + PLL_BASE);
+ val &= ~PLL_BASE_BYPASS;
+ val |= PLL_BASE_ENABLE;
+ clk_writel(val, c->reg + PLL_BASE);
+
+ if (c->flags & PLLM) {
+ val = pmc_readl(PMC_PLLP_WB0_OVERRIDE);
+ val |= PMC_PLLP_WB0_OVERRIDE_PLLM_ENABLE;
+ pmc_writel(val, PMC_PLLP_WB0_OVERRIDE);
+ }
+
+ tegra3_pll_clk_wait_for_lock(c, c->reg + PLL_BASE, PLL_BASE_LOCK);
+
+ return 0;
+}
+
+static void tegra3_pll_clk_disable(struct clk *c)
+{
+ u32 val;
+ pr_debug("%s on clock %s\n", __func__, c->name);
+
+ val = clk_readl(c->reg);
+ val &= ~(PLL_BASE_BYPASS | PLL_BASE_ENABLE);
+ clk_writel(val, c->reg);
+
+ if (c->flags & PLLM) {
+ val = pmc_readl(PMC_PLLP_WB0_OVERRIDE);
+ val &= ~PMC_PLLP_WB0_OVERRIDE_PLLM_ENABLE;
+ pmc_writel(val, PMC_PLLP_WB0_OVERRIDE);
+ }
+}
+
+static int tegra3_pll_clk_set_rate(struct clk *c, unsigned long rate)
+{
+ u32 val, p_div, old_base;
+ unsigned long input_rate;
+ const struct clk_pll_freq_table *sel;
+ struct clk_pll_freq_table cfg;
+
+ pr_debug("%s: %s %lu\n", __func__, c->name, rate);
+
+ if (c->flags & PLL_FIXED) {
+ int ret = 0;
+ if (rate != c->u.pll.fixed_rate) {
+ pr_err("%s: Can not change %s fixed rate %lu to %lu\n",
+ __func__, c->name, c->u.pll.fixed_rate, rate);
+ ret = -EINVAL;
+ }
+ return ret;
+ }
+
+ if (c->flags & PLLM) {
+ if (rate != clk_get_rate_locked(c)) {
+ pr_err("%s: Can not change memory %s rate in flight\n",
+ __func__, c->name);
+ return -EINVAL;
+ }
+ return 0;
+ }
+
+ p_div = 0;
+ input_rate = clk_get_rate(c->parent);
+
+ /* Check if the target rate is tabulated */
+ for (sel = c->u.pll.freq_table; sel->input_rate != 0; sel++) {
+ if (sel->input_rate == input_rate && sel->output_rate == rate) {
+ if (c->flags & PLLU) {
+ BUG_ON(sel->p < 1 || sel->p > 2);
+ if (sel->p == 1)
+ p_div = PLLU_BASE_POST_DIV;
+ } else {
+ BUG_ON(sel->p < 1);
+ for (val = sel->p; val > 1; val >>= 1, p_div++);
+ p_div <<= PLL_BASE_DIVP_SHIFT;
+ }
+ break;
+ }
+ }
+
+ /* Configure out-of-table rate */
+ if (sel->input_rate == 0) {
+ unsigned long cfreq;
+ BUG_ON(c->flags & PLLU);
+ sel = &cfg;
+
+ switch (input_rate) {
+ case 12000000:
+ case 26000000:
+ cfreq = (rate <= 1000000 * 1000) ? 1000000 : 2000000;
+ break;
+ case 13000000:
+ cfreq = (rate <= 1000000 * 1000) ? 1000000 : 2600000;
+ break;
+ case 16800000:
+ case 19200000:
+ cfreq = (rate <= 1200000 * 1000) ? 1200000 : 2400000;
+ break;
+ default:
+ pr_err("%s: Unexpected reference rate %lu\n",
+ __func__, input_rate);
+ BUG();
+ }
+
+ /* Raise VCO to guarantee 0.5% accuracy */
+ for (cfg.output_rate = rate; cfg.output_rate < 200 * cfreq;
+ cfg.output_rate <<= 1, p_div++);
+
+ cfg.p = 0x1 << p_div;
+ cfg.m = input_rate / cfreq;
+ cfg.n = cfg.output_rate / cfreq;
+ cfg.cpcon = OUT_OF_TABLE_CPCON;
+
+ if ((cfg.m > (PLL_BASE_DIVM_MASK >> PLL_BASE_DIVM_SHIFT)) ||
+ (cfg.n > (PLL_BASE_DIVN_MASK >> PLL_BASE_DIVN_SHIFT)) ||
+ (p_div > (PLL_BASE_DIVP_MASK >> PLL_BASE_DIVP_SHIFT)) ||
+ (cfg.output_rate > c->u.pll.vco_max)) {
+ pr_err("%s: Failed to set %s out-of-table rate %lu\n",
+ __func__, c->name, rate);
+ return -EINVAL;
+ }
+ p_div <<= PLL_BASE_DIVP_SHIFT;
+ }
+
+ c->mul = sel->n;
+ c->div = sel->m * sel->p;
+
+ old_base = val = clk_readl(c->reg + PLL_BASE);
+ val &= ~(PLL_BASE_DIVM_MASK | PLL_BASE_DIVN_MASK |
+ ((c->flags & PLLU) ? PLLU_BASE_POST_DIV : PLL_BASE_DIVP_MASK));
+ val |= (sel->m << PLL_BASE_DIVM_SHIFT) |
+ (sel->n << PLL_BASE_DIVN_SHIFT) | p_div;
+ if (val == old_base)
+ return 0;
+
+ if (c->state == ON) {
+ tegra3_pll_clk_disable(c);
+ val &= ~(PLL_BASE_BYPASS | PLL_BASE_ENABLE);
+ }
+ clk_writel(val, c->reg + PLL_BASE);
+
+ if (c->flags & PLL_HAS_CPCON) {
+ val = clk_readl(c->reg + PLL_MISC(c));
+ val &= ~PLL_MISC_CPCON_MASK;
+ val |= sel->cpcon << PLL_MISC_CPCON_SHIFT;
+ if (c->flags & (PLLU | PLLD)) {
+ val &= ~PLL_MISC_LFCON_MASK;
+ if (sel->n >= PLLDU_LFCON_SET_DIVN)
+ val |= 0x1 << PLL_MISC_LFCON_SHIFT;
+ } else if (c->flags & (PLLX | PLLM)) {
+ val &= ~(0x1 << PLL_MISC_DCCON_SHIFT);
+ if (rate >= (c->u.pll.vco_max >> 1))
+ val |= 0x1 << PLL_MISC_DCCON_SHIFT;
+ }
+ clk_writel(val, c->reg + PLL_MISC(c));
+ }
+
+ if (c->state == ON)
+ tegra3_pll_clk_enable(c);
+
+ return 0;
+}
+
+static struct clk_ops tegra_pll_ops = {
+ .init = tegra3_pll_clk_init,
+ .enable = tegra3_pll_clk_enable,
+ .disable = tegra3_pll_clk_disable,
+ .set_rate = tegra3_pll_clk_set_rate,
+};
+
+static int
+tegra3_plld_clk_cfg_ex(struct clk *c, enum tegra_clk_ex_param p, u32 setting)
+{
+ u32 val, mask, reg;
+
+ switch (p) {
+ case TEGRA_CLK_PLLD_CSI_OUT_ENB:
+ mask = PLLD_BASE_CSI_CLKENABLE;
+ reg = c->reg + PLL_BASE;
+ break;
+ case TEGRA_CLK_PLLD_DSI_OUT_ENB:
+ mask = PLLD_MISC_DSI_CLKENABLE;
+ reg = c->reg + PLL_MISC(c);
+ break;
+ case TEGRA_CLK_PLLD_MIPI_MUX_SEL:
+ if (!(c->flags & PLL_ALT_MISC_REG)) {
+ mask = PLLD_BASE_DSIB_MUX_MASK;
+ reg = c->reg + PLL_BASE;
+ break;
+ }
+ /* fall through - error since PLLD2 does not have MUX_SEL control */
+ default:
+ return -EINVAL;
+ }
+
+ val = clk_readl(reg);
+ if (setting)
+ val |= mask;
+ else
+ val &= ~mask;
+ clk_writel(val, reg);
+ return 0;
+}
+
+static struct clk_ops tegra_plld_ops = {
+ .init = tegra3_pll_clk_init,
+ .enable = tegra3_pll_clk_enable,
+ .disable = tegra3_pll_clk_disable,
+ .set_rate = tegra3_pll_clk_set_rate,
+ .clk_cfg_ex = tegra3_plld_clk_cfg_ex,
+};
+
+static void tegra3_plle_clk_init(struct clk *c)
+{
+ u32 val;
+
+ val = clk_readl(PLLE_AUX);
+ c->parent = (val & PLLE_AUX_PLLP_SEL) ?
+ tegra_get_clock_by_name("pll_p") :
+ tegra_get_clock_by_name("pll_ref");
+
+ val = clk_readl(c->reg + PLL_BASE);
+ c->state = (val & PLLE_BASE_ENABLE) ? ON : OFF;
+ c->mul = (val & PLLE_BASE_DIVN_MASK) >> PLLE_BASE_DIVN_SHIFT;
+ c->div = (val & PLLE_BASE_DIVM_MASK) >> PLLE_BASE_DIVM_SHIFT;
+ c->div *= (val & PLLE_BASE_DIVP_MASK) >> PLLE_BASE_DIVP_SHIFT;
+}
+
+static void tegra3_plle_clk_disable(struct clk *c)
+{
+ u32 val;
+ pr_debug("%s on clock %s\n", __func__, c->name);
+
+ val = clk_readl(c->reg + PLL_BASE);
+ val &= ~(PLLE_BASE_CML_ENABLE | PLLE_BASE_ENABLE);
+ clk_writel(val, c->reg + PLL_BASE);
+}
+
+static void tegra3_plle_training(struct clk *c)
+{
+ u32 val;
+
+ /* PLLE is already disabled, and setup cleared;
+ * create falling edge on PLLE IDDQ input */
+ val = pmc_readl(PMC_SATA_PWRGT);
+ val |= PMC_SATA_PWRGT_PLLE_IDDQ_VALUE;
+ pmc_writel(val, PMC_SATA_PWRGT);
+
+ val = pmc_readl(PMC_SATA_PWRGT);
+ val |= PMC_SATA_PWRGT_PLLE_IDDQ_SWCTL;
+ pmc_writel(val, PMC_SATA_PWRGT);
+
+ val = pmc_readl(PMC_SATA_PWRGT);
+ val &= ~PMC_SATA_PWRGT_PLLE_IDDQ_VALUE;
+ pmc_writel(val, PMC_SATA_PWRGT);
+
+ do {
+ val = clk_readl(c->reg + PLL_MISC(c));
+ } while (!(val & PLLE_MISC_READY));
+}
+
+static int tegra3_plle_configure(struct clk *c, bool force_training)
+{
+ u32 val;
+ const struct clk_pll_freq_table *sel;
+ unsigned long rate = c->u.pll.fixed_rate;
+ unsigned long input_rate = clk_get_rate(c->parent);
+
+ for (sel = c->u.pll.freq_table; sel->input_rate != 0; sel++) {
+ if (sel->input_rate == input_rate && sel->output_rate == rate)
+ break;
+ }
+
+ if (sel->input_rate == 0)
+ return -ENOSYS;
+
+ /* disable PLLE, clear setup fiels */
+ tegra3_plle_clk_disable(c);
+
+ val = clk_readl(c->reg + PLL_MISC(c));
+ val &= ~(PLLE_MISC_LOCK_ENABLE | PLLE_MISC_SETUP_MASK);
+ clk_writel(val, c->reg + PLL_MISC(c));
+
+ /* training */
+ val = clk_readl(c->reg + PLL_MISC(c));
+ if (force_training || (!(val & PLLE_MISC_READY)))
+ tegra3_plle_training(c);
+
+ /* configure dividers, setup, disable SS */
+ val = clk_readl(c->reg + PLL_BASE);
+ val &= ~PLLE_BASE_DIV_MASK;
+ val |= PLLE_BASE_DIV(sel->m, sel->n, sel->p, sel->cpcon);
+ clk_writel(val, c->reg + PLL_BASE);
+ c->mul = sel->n;
+ c->div = sel->m * sel->p;
+
+ val = clk_readl(c->reg + PLL_MISC(c));
+ val |= PLLE_MISC_SETUP_VALUE;
+ val |= PLLE_MISC_LOCK_ENABLE;
+ clk_writel(val, c->reg + PLL_MISC(c));
+
+ val = clk_readl(PLLE_SS_CTRL);
+ val |= PLLE_SS_DISABLE;
+ clk_writel(val, PLLE_SS_CTRL);
+
+ /* enable and lock PLLE*/
+ val = clk_readl(c->reg + PLL_BASE);
+ val |= (PLLE_BASE_CML_ENABLE | PLLE_BASE_ENABLE);
+ clk_writel(val, c->reg + PLL_BASE);
+
+ tegra3_pll_clk_wait_for_lock(c, c->reg + PLL_MISC(c), PLLE_MISC_LOCK);
+
+#if USE_PLLE_SS
+ /* configure spread spectrum coefficients */
+ /* FIXME: coefficients for 216MHZ input? */
+#ifdef CONFIG_TEGRA_SILICON_PLATFORM
+ if (input_rate == 12000000)
+#endif
+ {
+ val = clk_readl(PLLE_SS_CTRL);
+ val &= ~(PLLE_SS_COEFFICIENTS_MASK | PLLE_SS_DISABLE);
+ val |= PLLE_SS_COEFFICIENTS_12MHZ;
+ clk_writel(val, PLLE_SS_CTRL);
+ }
+#endif
+ return 0;
+}
+
+static int tegra3_plle_clk_enable(struct clk *c)
+{
+ pr_debug("%s on clock %s\n", __func__, c->name);
+ return tegra3_plle_configure(c, !c->set);
+}
+
+static struct clk_ops tegra_plle_ops = {
+ .init = tegra3_plle_clk_init,
+ .enable = tegra3_plle_clk_enable,
+ .disable = tegra3_plle_clk_disable,
+};
+
+/* Clock divider ops */
+static void tegra3_pll_div_clk_init(struct clk *c)
+{
+ if (c->flags & DIV_U71) {
+ u32 divu71;
+ u32 val = clk_readl(c->reg);
+ val >>= c->reg_shift;
+ c->state = (val & PLL_OUT_CLKEN) ? ON : OFF;
+ if (!(val & PLL_OUT_RESET_DISABLE))
+ c->state = OFF;
+
+ divu71 = (val & PLL_OUT_RATIO_MASK) >> PLL_OUT_RATIO_SHIFT;
+ c->div = (divu71 + 2);
+ c->mul = 2;
+ } else if (c->flags & DIV_2) {
+ c->state = ON;
+ if (c->flags & (PLLD | PLLX)) {
+ c->div = 2;
+ c->mul = 1;
+ }
+ else
+ BUG();
+ } else {
+ c->state = ON;
+ c->div = 1;
+ c->mul = 1;
+ }
+}
+
+static int tegra3_pll_div_clk_enable(struct clk *c)
+{
+ u32 val;
+ u32 new_val;
+
+ pr_debug("%s: %s\n", __func__, c->name);
+ if (c->flags & DIV_U71) {
+ val = clk_readl(c->reg);
+ new_val = val >> c->reg_shift;
+ new_val &= 0xFFFF;
+
+ new_val |= PLL_OUT_CLKEN | PLL_OUT_RESET_DISABLE;
+
+ val &= ~(0xFFFF << c->reg_shift);
+ val |= new_val << c->reg_shift;
+ clk_writel_delay(val, c->reg);
+ return 0;
+ } else if (c->flags & DIV_2) {
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static void tegra3_pll_div_clk_disable(struct clk *c)
+{
+ u32 val;
+ u32 new_val;
+
+ pr_debug("%s: %s\n", __func__, c->name);
+ if (c->flags & DIV_U71) {
+ val = clk_readl(c->reg);
+ new_val = val >> c->reg_shift;
+ new_val &= 0xFFFF;
+
+ new_val &= ~(PLL_OUT_CLKEN | PLL_OUT_RESET_DISABLE);
+
+ val &= ~(0xFFFF << c->reg_shift);
+ val |= new_val << c->reg_shift;
+ clk_writel_delay(val, c->reg);
+ }
+}
+
+static int tegra3_pll_div_clk_set_rate(struct clk *c, unsigned long rate)
+{
+ u32 val;
+ u32 new_val;
+ int divider_u71;
+ unsigned long parent_rate = clk_get_rate(c->parent);
+
+ pr_debug("%s: %s %lu\n", __func__, c->name, rate);
+ if (c->flags & DIV_U71) {
+ divider_u71 = clk_div71_get_divider(
+ parent_rate, rate, c->flags, ROUND_DIVIDER_UP);
+ if (divider_u71 >= 0) {
+ val = clk_readl(c->reg);
+ new_val = val >> c->reg_shift;
+ new_val &= 0xFFFF;
+ if (c->flags & DIV_U71_FIXED)
+ new_val |= PLL_OUT_OVERRIDE;
+ new_val &= ~PLL_OUT_RATIO_MASK;
+ new_val |= divider_u71 << PLL_OUT_RATIO_SHIFT;
+
+ val &= ~(0xFFFF << c->reg_shift);
+ val |= new_val << c->reg_shift;
+ clk_writel_delay(val, c->reg);
+ c->div = divider_u71 + 2;
+ c->mul = 2;
+ return 0;
+ }
+ } else if (c->flags & DIV_2)
+ return clk_set_rate(c->parent, rate * 2);
+
+ return -EINVAL;
+}
+
+static long tegra3_pll_div_clk_round_rate(struct clk *c, unsigned long rate)
+{
+ int divider;
+ unsigned long parent_rate = clk_get_rate(c->parent);
+ pr_debug("%s: %s %lu\n", __func__, c->name, rate);
+
+ if (c->flags & DIV_U71) {
+ divider = clk_div71_get_divider(
+ parent_rate, rate, c->flags, ROUND_DIVIDER_UP);
+ if (divider < 0)
+ return divider;
+ return DIV_ROUND_UP(parent_rate * 2, divider + 2);
+ } else if (c->flags & DIV_2)
+ /* no rounding - fixed DIV_2 dividers pass rate to parent PLL */
+ return rate;
+
+ return -EINVAL;
+}
+
+static struct clk_ops tegra_pll_div_ops = {
+ .init = tegra3_pll_div_clk_init,
+ .enable = tegra3_pll_div_clk_enable,
+ .disable = tegra3_pll_div_clk_disable,
+ .set_rate = tegra3_pll_div_clk_set_rate,
+ .round_rate = tegra3_pll_div_clk_round_rate,
+};
+
+/* Periph clk ops */
+static inline u32 periph_clk_source_mask(struct clk *c)
+{
+ if (c->flags & MUX8)
+ return 7 << 29;
+ else if (c->flags & MUX_PWM)
+ return 3 << 28;
+ else if (c->flags & MUX_CLK_OUT)
+ return 3 << (c->u.periph.clk_num + 4);
+ else if (c->flags & PLLD)
+ return PLLD_BASE_DSIB_MUX_MASK;
+ else
+ return 3 << 30;
+}
+
+static inline u32 periph_clk_source_shift(struct clk *c)
+{
+ if (c->flags & MUX8)
+ return 29;
+ else if (c->flags & MUX_PWM)
+ return 28;
+ else if (c->flags & MUX_CLK_OUT)
+ return c->u.periph.clk_num + 4;
+ else if (c->flags & PLLD)
+ return PLLD_BASE_DSIB_MUX_SHIFT;
+ else
+ return 30;
+}
+
+static void tegra3_periph_clk_init(struct clk *c)
+{
+ u32 val = clk_readl(c->reg);
+ const struct clk_mux_sel *mux = 0;
+ const struct clk_mux_sel *sel;
+ if (c->flags & MUX) {
+ for (sel = c->inputs; sel->input != NULL; sel++) {
+ if (((val & periph_clk_source_mask(c)) >>
+ periph_clk_source_shift(c)) == sel->value)
+ mux = sel;
+ }
+ BUG_ON(!mux);
+
+ c->parent = mux->input;
+ } else {
+ c->parent = c->inputs[0].input;
+ }
+
+ if (c->flags & DIV_U71) {
+ u32 divu71 = val & PERIPH_CLK_SOURCE_DIVU71_MASK;
+ if ((c->flags & DIV_U71_UART) &&
+ (!(val & PERIPH_CLK_UART_DIV_ENB))) {
+ divu71 = 0;
+ }
+ if (c->flags & DIV_U71_IDLE) {
+ val &= ~(PERIPH_CLK_SOURCE_DIVU71_MASK <<
+ PERIPH_CLK_SOURCE_DIVIDLE_SHIFT);
+ val |= (PERIPH_CLK_SOURCE_DIVIDLE_VAL <<
+ PERIPH_CLK_SOURCE_DIVIDLE_SHIFT);
+ clk_writel(val, c->reg);
+ }
+ c->div = divu71 + 2;
+ c->mul = 2;
+ } else if (c->flags & DIV_U16) {
+ u32 divu16 = val & PERIPH_CLK_SOURCE_DIVU16_MASK;
+ c->div = divu16 + 1;
+ c->mul = 1;
+ } else {
+ c->div = 1;
+ c->mul = 1;
+ }
+
+ c->state = ON;
+ if (!(clk_readl(PERIPH_CLK_TO_ENB_REG(c)) & PERIPH_CLK_TO_BIT(c)))
+ c->state = OFF;
+ if (!(c->flags & PERIPH_NO_RESET))
+ if (clk_readl(PERIPH_CLK_TO_RST_REG(c)) & PERIPH_CLK_TO_BIT(c))
+ c->state = OFF;
+}
+
+static int tegra3_periph_clk_enable(struct clk *c)
+{
+ pr_debug("%s on clock %s\n", __func__, c->name);
+
+ tegra_periph_clk_enable_refcount[c->u.periph.clk_num]++;
+ if (tegra_periph_clk_enable_refcount[c->u.periph.clk_num] > 1)
+ return 0;
+
+ clk_writel_delay(PERIPH_CLK_TO_BIT(c), PERIPH_CLK_TO_ENB_SET_REG(c));
+ if (!(c->flags & PERIPH_NO_RESET) && !(c->flags & PERIPH_MANUAL_RESET)) {
+ if (clk_readl(PERIPH_CLK_TO_RST_REG(c)) & PERIPH_CLK_TO_BIT(c)) {
+ udelay(5); /* reset propagation delay */
+ clk_writel(PERIPH_CLK_TO_BIT(c), PERIPH_CLK_TO_RST_CLR_REG(c));
+ }
+ }
+ return 0;
+}
+
+static void tegra3_periph_clk_disable(struct clk *c)
+{
+ unsigned long val;
+ pr_debug("%s on clock %s\n", __func__, c->name);
+
+ if (c->refcnt)
+ tegra_periph_clk_enable_refcount[c->u.periph.clk_num]--;
+
+ if (tegra_periph_clk_enable_refcount[c->u.periph.clk_num] == 0) {
+ /* If peripheral is in the APB bus then read the APB bus to
+ * flush the write operation in apb bus. This will avoid the
+ * peripheral access after disabling clock*/
+ if (c->flags & PERIPH_ON_APB)
+ val = chipid_readl();
+
+ clk_writel_delay(
+ PERIPH_CLK_TO_BIT(c), PERIPH_CLK_TO_ENB_CLR_REG(c));
+ }
+}
+
+static void tegra3_periph_clk_reset(struct clk *c, bool assert)
+{
+ unsigned long val;
+ pr_debug("%s %s on clock %s\n", __func__,
+ assert ? "assert" : "deassert", c->name);
+
+ if (!(c->flags & PERIPH_NO_RESET)) {
+ if (assert) {
+ /* If peripheral is in the APB bus then read the APB
+ * bus to flush the write operation in apb bus. This
+ * will avoid the peripheral access after disabling
+ * clock */
+ if (c->flags & PERIPH_ON_APB)
+ val = chipid_readl();
+
+ clk_writel(PERIPH_CLK_TO_BIT(c),
+ PERIPH_CLK_TO_RST_SET_REG(c));
+ } else
+ clk_writel(PERIPH_CLK_TO_BIT(c),
+ PERIPH_CLK_TO_RST_CLR_REG(c));
+ }
+}
+
+static int tegra3_periph_clk_set_parent(struct clk *c, struct clk *p)
+{
+ u32 val;
+ const struct clk_mux_sel *sel;
+ pr_debug("%s: %s %s\n", __func__, c->name, p->name);
+
+ if (!(c->flags & MUX))
+ return (p == c->parent) ? 0 : (-EINVAL);
+
+ if (!tegra3_clk_is_parent_allowed(c, p))
+ return -EINVAL;
+
+ for (sel = c->inputs; sel->input != NULL; sel++) {
+ if (sel->input == p) {
+ val = clk_readl(c->reg);
+ val &= ~periph_clk_source_mask(c);
+ val |= (sel->value << periph_clk_source_shift(c));
+
+ if (c->refcnt)
+ clk_enable(p);
+
+ clk_writel_delay(val, c->reg);
+
+ if (c->refcnt && c->parent)
+ clk_disable(c->parent);
+
+ clk_reparent(c, p);
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int tegra3_periph_clk_set_rate(struct clk *c, unsigned long rate)
+{
+ u32 val;
+ int divider;
+ unsigned long parent_rate = clk_get_rate(c->parent);
+
+ if (c->flags & DIV_U71) {
+ divider = clk_div71_get_divider(
+ parent_rate, rate, c->flags, ROUND_DIVIDER_UP);
+ if (divider >= 0) {
+ val = clk_readl(c->reg);
+ val &= ~PERIPH_CLK_SOURCE_DIVU71_MASK;
+ val |= divider;
+ if (c->flags & DIV_U71_UART) {
+ if (divider)
+ val |= PERIPH_CLK_UART_DIV_ENB;
+ else
+ val &= ~PERIPH_CLK_UART_DIV_ENB;
+ }
+ clk_writel_delay(val, c->reg);
+ c->div = divider + 2;
+ c->mul = 2;
+ return 0;
+ }
+ } else if (c->flags & DIV_U16) {
+ divider = clk_div16_get_divider(parent_rate, rate);
+ if (divider >= 0) {
+ val = clk_readl(c->reg);
+ val &= ~PERIPH_CLK_SOURCE_DIVU16_MASK;
+ val |= divider;
+ clk_writel_delay(val, c->reg);
+ c->div = divider + 1;
+ c->mul = 1;
+ return 0;
+ }
+ } else if (parent_rate <= rate) {
+ c->div = 1;
+ c->mul = 1;
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static long tegra3_periph_clk_round_rate(struct clk *c,
+ unsigned long rate)
+{
+ int divider;
+ unsigned long parent_rate = clk_get_rate(c->parent);
+ pr_debug("%s: %s %lu\n", __func__, c->name, rate);
+
+ if (c->flags & DIV_U71) {
+ divider = clk_div71_get_divider(
+ parent_rate, rate, c->flags, ROUND_DIVIDER_UP);
+ if (divider < 0)
+ return divider;
+
+ return DIV_ROUND_UP(parent_rate * 2, divider + 2);
+ } else if (c->flags & DIV_U16) {
+ divider = clk_div16_get_divider(parent_rate, rate);
+ if (divider < 0)
+ return divider;
+ return DIV_ROUND_UP(parent_rate, divider + 1);
+ }
+ return -EINVAL;
+}
+
+static struct clk_ops tegra_periph_clk_ops = {
+ .init = &tegra3_periph_clk_init,
+ .enable = &tegra3_periph_clk_enable,
+ .disable = &tegra3_periph_clk_disable,
+ .set_parent = &tegra3_periph_clk_set_parent,
+ .set_rate = &tegra3_periph_clk_set_rate,
+ .round_rate = &tegra3_periph_clk_round_rate,
+ .reset = &tegra3_periph_clk_reset,
+};
+
+
+/* Periph extended clock configuration ops */
+static int
+tegra3_vi_clk_cfg_ex(struct clk *c, enum tegra_clk_ex_param p, u32 setting)
+{
+ if (p == TEGRA_CLK_VI_INP_SEL) {
+ u32 val = clk_readl(c->reg);
+ val &= ~PERIPH_CLK_VI_SEL_EX_MASK;
+ val |= (setting << PERIPH_CLK_VI_SEL_EX_SHIFT) &
+ PERIPH_CLK_VI_SEL_EX_MASK;
+ clk_writel(val, c->reg);
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static struct clk_ops tegra_vi_clk_ops = {
+ .init = &tegra3_periph_clk_init,
+ .enable = &tegra3_periph_clk_enable,
+ .disable = &tegra3_periph_clk_disable,
+ .set_parent = &tegra3_periph_clk_set_parent,
+ .set_rate = &tegra3_periph_clk_set_rate,
+ .round_rate = &tegra3_periph_clk_round_rate,
+ .clk_cfg_ex = &tegra3_vi_clk_cfg_ex,
+ .reset = &tegra3_periph_clk_reset,
+};
+
+static int
+tegra3_nand_clk_cfg_ex(struct clk *c, enum tegra_clk_ex_param p, u32 setting)
+{
+ if (p == TEGRA_CLK_NAND_PAD_DIV2_ENB) {
+ u32 val = clk_readl(c->reg);
+ if (setting)
+ val |= PERIPH_CLK_NAND_DIV_EX_ENB;
+ else
+ val &= ~PERIPH_CLK_NAND_DIV_EX_ENB;
+ clk_writel(val, c->reg);
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static struct clk_ops tegra_nand_clk_ops = {
+ .init = &tegra3_periph_clk_init,
+ .enable = &tegra3_periph_clk_enable,
+ .disable = &tegra3_periph_clk_disable,
+ .set_parent = &tegra3_periph_clk_set_parent,
+ .set_rate = &tegra3_periph_clk_set_rate,
+ .round_rate = &tegra3_periph_clk_round_rate,
+ .clk_cfg_ex = &tegra3_nand_clk_cfg_ex,
+ .reset = &tegra3_periph_clk_reset,
+};
+
+
+static int
+tegra3_dtv_clk_cfg_ex(struct clk *c, enum tegra_clk_ex_param p, u32 setting)
+{
+ if (p == TEGRA_CLK_DTV_INVERT) {
+ u32 val = clk_readl(c->reg);
+ if (setting)
+ val |= PERIPH_CLK_DTV_POLARITY_INV;
+ else
+ val &= ~PERIPH_CLK_DTV_POLARITY_INV;
+ clk_writel(val, c->reg);
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static struct clk_ops tegra_dtv_clk_ops = {
+ .init = &tegra3_periph_clk_init,
+ .enable = &tegra3_periph_clk_enable,
+ .disable = &tegra3_periph_clk_disable,
+ .set_parent = &tegra3_periph_clk_set_parent,
+ .set_rate = &tegra3_periph_clk_set_rate,
+ .round_rate = &tegra3_periph_clk_round_rate,
+ .clk_cfg_ex = &tegra3_dtv_clk_cfg_ex,
+ .reset = &tegra3_periph_clk_reset,
+};
+
+static int tegra3_dsib_clk_set_parent(struct clk *c, struct clk *p)
+{
+ const struct clk_mux_sel *sel;
+ struct clk *d = tegra_get_clock_by_name("pll_d");
+
+ pr_debug("%s: %s %s\n", __func__, c->name, p->name);
+
+ for (sel = c->inputs; sel->input != NULL; sel++) {
+ if (sel->input == p) {
+ if (c->refcnt)
+ clk_enable(p);
+
+ /* The DSIB parent selection bit is in PLLD base
+ register - can not do direct r-m-w, must be
+ protected by PLLD lock */
+ tegra_clk_cfg_ex(
+ d, TEGRA_CLK_PLLD_MIPI_MUX_SEL, sel->value);
+
+ if (c->refcnt && c->parent)
+ clk_disable(c->parent);
+
+ clk_reparent(c, p);
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static struct clk_ops tegra_dsib_clk_ops = {
+ .init = &tegra3_periph_clk_init,
+ .enable = &tegra3_periph_clk_enable,
+ .disable = &tegra3_periph_clk_disable,
+ .set_parent = &tegra3_dsib_clk_set_parent,
+ .set_rate = &tegra3_periph_clk_set_rate,
+ .round_rate = &tegra3_periph_clk_round_rate,
+ .reset = &tegra3_periph_clk_reset,
+};
+
+/* pciex clock support only reset function */
+static struct clk_ops tegra_pciex_clk_ops = {
+ .reset = tegra3_periph_clk_reset,
+};
+
+/* Output clock ops */
+
+static DEFINE_SPINLOCK(clk_out_lock);
+
+static void tegra3_clk_out_init(struct clk *c)
+{
+ const struct clk_mux_sel *mux = 0;
+ const struct clk_mux_sel *sel;
+ u32 val = pmc_readl(c->reg);
+
+ c->state = (val & (0x1 << c->u.periph.clk_num)) ? ON : OFF;
+ c->mul = 1;
+ c->div = 1;
+
+ for (sel = c->inputs; sel->input != NULL; sel++) {
+ if (((val & periph_clk_source_mask(c)) >>
+ periph_clk_source_shift(c)) == sel->value)
+ mux = sel;
+ }
+ BUG_ON(!mux);
+ c->parent = mux->input;
+}
+
+static int tegra3_clk_out_enable(struct clk *c)
+{
+ u32 val;
+ unsigned long flags;
+
+ pr_debug("%s on clock %s\n", __func__, c->name);
+
+ spin_lock_irqsave(&clk_out_lock, flags);
+ val = pmc_readl(c->reg);
+ val |= (0x1 << c->u.periph.clk_num);
+ pmc_writel(val, c->reg);
+ spin_unlock_irqrestore(&clk_out_lock, flags);
+
+ return 0;
+}
+
+static void tegra3_clk_out_disable(struct clk *c)
+{
+ u32 val;
+ unsigned long flags;
+
+ pr_debug("%s on clock %s\n", __func__, c->name);
+
+ spin_lock_irqsave(&clk_out_lock, flags);
+ val = pmc_readl(c->reg);
+ val &= ~(0x1 << c->u.periph.clk_num);
+ pmc_writel(val, c->reg);
+ spin_unlock_irqrestore(&clk_out_lock, flags);
+}
+
+static int tegra3_clk_out_set_parent(struct clk *c, struct clk *p)
+{
+ u32 val;
+ unsigned long flags;
+ const struct clk_mux_sel *sel;
+
+ pr_debug("%s: %s %s\n", __func__, c->name, p->name);
+
+ for (sel = c->inputs; sel->input != NULL; sel++) {
+ if (sel->input == p) {
+ if (c->refcnt)
+ clk_enable(p);
+
+ spin_lock_irqsave(&clk_out_lock, flags);
+ val = pmc_readl(c->reg);
+ val &= ~periph_clk_source_mask(c);
+ val |= (sel->value << periph_clk_source_shift(c));
+ pmc_writel(val, c->reg);
+ spin_unlock_irqrestore(&clk_out_lock, flags);
+
+ if (c->refcnt && c->parent)
+ clk_disable(c->parent);
+
+ clk_reparent(c, p);
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
+
+static struct clk_ops tegra_clk_out_ops = {
+ .init = &tegra3_clk_out_init,
+ .enable = &tegra3_clk_out_enable,
+ .disable = &tegra3_clk_out_disable,
+ .set_parent = &tegra3_clk_out_set_parent,
+};
+
+
+/* External memory controller clock ops */
+static void tegra3_emc_clk_init(struct clk *c)
+{
+ tegra3_periph_clk_init(c);
+ tegra_emc_dram_type_init(c);
+
+ /* On A01 limit EMC maximum rate to boot frequency;
+ starting with A02 full PLLM range should be supported */
+ if (tegra_get_revision() == TEGRA_REVISION_A01)
+ c->max_rate = clk_get_rate_locked(c);
+ else
+ c->max_rate = clk_get_rate(c->parent);
+}
+
+static long tegra3_emc_clk_round_rate(struct clk *c, unsigned long rate)
+{
+ long new_rate = max(rate, c->min_rate);
+
+ new_rate = tegra_emc_round_rate(new_rate);
+ if (new_rate < 0)
+ new_rate = c->max_rate;
+
+ return new_rate;
+}
+
+static int tegra3_emc_clk_set_rate(struct clk *c, unsigned long rate)
+{
+ int ret;
+ u32 div_value;
+ struct clk *p;
+
+ /* The tegra3 memory controller has an interlock with the clock
+ * block that allows memory shadowed registers to be updated,
+ * and then transfer them to the main registers at the same
+ * time as the clock update without glitches. During clock change
+ * operation both clock parent and divider may change simultaneously
+ * to achieve requested rate. */
+ p = tegra_emc_predict_parent(rate, &div_value);
+ div_value += 2; /* emc has fractional DIV_U71 divider */
+ if (!p)
+ return -EINVAL;
+
+ if (p == c->parent) {
+ if (div_value == c->div)
+ return 0;
+ } else if (c->refcnt)
+ clk_enable(p);
+
+ ret = tegra_emc_set_rate(rate);
+ if (ret < 0)
+ return ret;
+
+ if (p != c->parent) {
+ if(c->refcnt && c->parent)
+ clk_disable(c->parent);
+ clk_reparent(c, p);
+ }
+ c->div = div_value;
+ c->mul = 2;
+ return 0;
+}
+
+static struct clk_ops tegra_emc_clk_ops = {
+ .init = &tegra3_emc_clk_init,
+ .enable = &tegra3_periph_clk_enable,
+ .disable = &tegra3_periph_clk_disable,
+ .set_rate = &tegra3_emc_clk_set_rate,
+ .round_rate = &tegra3_emc_clk_round_rate,
+ .reset = &tegra3_periph_clk_reset,
+ .shared_bus_update = &tegra3_clk_shared_bus_update,
+};
+
+/* Clock doubler ops */
+static void tegra3_clk_double_init(struct clk *c)
+{
+ u32 val = clk_readl(c->reg);
+ c->mul = val & (0x1 << c->reg_shift) ? 1 : 2;
+ c->div = 1;
+ c->state = ON;
+ if (!(clk_readl(PERIPH_CLK_TO_ENB_REG(c)) & PERIPH_CLK_TO_BIT(c)))
+ c->state = OFF;
+};
+
+static int tegra3_clk_double_set_rate(struct clk *c, unsigned long rate)
+{
+ u32 val;
+ unsigned long parent_rate = clk_get_rate(c->parent);
+ if (rate == parent_rate) {
+ val = clk_readl(c->reg) | (0x1 << c->reg_shift);
+ clk_writel(val, c->reg);
+ c->mul = 1;
+ c->div = 1;
+ return 0;
+ } else if (rate == 2 * parent_rate) {
+ val = clk_readl(c->reg) & (~(0x1 << c->reg_shift));
+ clk_writel(val, c->reg);
+ c->mul = 2;
+ c->div = 1;
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static struct clk_ops tegra_clk_double_ops = {
+ .init = &tegra3_clk_double_init,
+ .enable = &tegra3_periph_clk_enable,
+ .disable = &tegra3_periph_clk_disable,
+ .set_rate = &tegra3_clk_double_set_rate,
+};
+
+/* Audio sync clock ops */
+static int tegra3_sync_source_set_rate(struct clk *c, unsigned long rate)
+{
+ c->rate = rate;
+ return 0;
+}
+
+static struct clk_ops tegra_sync_source_ops = {
+ .set_rate = &tegra3_sync_source_set_rate,
+};
+
+static void tegra3_audio_sync_clk_init(struct clk *c)
+{
+ int source;
+ const struct clk_mux_sel *sel;
+ u32 val = clk_readl(c->reg);
+ c->state = (val & AUDIO_SYNC_DISABLE_BIT) ? OFF : ON;
+ source = val & AUDIO_SYNC_SOURCE_MASK;
+ for (sel = c->inputs; sel->input != NULL; sel++)
+ if (sel->value == source)
+ break;
+ BUG_ON(sel->input == NULL);
+ c->parent = sel->input;
+}
+
+static int tegra3_audio_sync_clk_enable(struct clk *c)
+{
+ u32 val = clk_readl(c->reg);
+ clk_writel((val & (~AUDIO_SYNC_DISABLE_BIT)), c->reg);
+ return 0;
+}
+
+static void tegra3_audio_sync_clk_disable(struct clk *c)
+{
+ u32 val = clk_readl(c->reg);
+ clk_writel((val | AUDIO_SYNC_DISABLE_BIT), c->reg);
+}
+
+static int tegra3_audio_sync_clk_set_parent(struct clk *c, struct clk *p)
+{
+ u32 val;
+ const struct clk_mux_sel *sel;
+ for (sel = c->inputs; sel->input != NULL; sel++) {
+ if (sel->input == p) {
+ val = clk_readl(c->reg);
+ val &= ~AUDIO_SYNC_SOURCE_MASK;
+ val |= sel->value;
+
+ if (c->refcnt)
+ clk_enable(p);
+
+ clk_writel(val, c->reg);
+
+ if (c->refcnt && c->parent)
+ clk_disable(c->parent);
+
+ clk_reparent(c, p);
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static struct clk_ops tegra_audio_sync_clk_ops = {
+ .init = tegra3_audio_sync_clk_init,
+ .enable = tegra3_audio_sync_clk_enable,
+ .disable = tegra3_audio_sync_clk_disable,
+ .set_parent = tegra3_audio_sync_clk_set_parent,
+};
+
+/* cml0 (pcie), and cml1 (sata) clock ops */
+static void tegra3_cml_clk_init(struct clk *c)
+{
+ u32 val = clk_readl(c->reg);
+ c->state = val & (0x1 << c->u.periph.clk_num) ? ON : OFF;
+}
+
+static int tegra3_cml_clk_enable(struct clk *c)
+{
+ u32 val = clk_readl(c->reg);
+ val |= (0x1 << c->u.periph.clk_num);
+ clk_writel(val, c->reg);
+ return 0;
+}
+
+static void tegra3_cml_clk_disable(struct clk *c)
+{
+ u32 val = clk_readl(c->reg);
+ val &= ~(0x1 << c->u.periph.clk_num);
+ clk_writel(val, c->reg);
+}
+
+static struct clk_ops tegra_cml_clk_ops = {
+ .init = &tegra3_cml_clk_init,
+ .enable = &tegra3_cml_clk_enable,
+ .disable = &tegra3_cml_clk_disable,
+};
+
+
+/* cbus ops */
+/*
+ * Some clocks require dynamic re-locking of source PLL in order to
+ * achieve frequency scaling granularity that matches characterized
+ * core voltage steps. The cbus clock creates a shared bus that
+ * provides a virtual root for such clocks to hide and synchronize
+ * parent PLL re-locking as well as backup operations.
+*/
+
+static void tegra3_clk_cbus_init(struct clk *c)
+{
+ c->state = OFF;
+ c->set = true;
+}
+
+static int tegra3_clk_cbus_enable(struct clk *c)
+{
+ return 0;
+}
+
+static long tegra3_clk_cbus_round_rate(struct clk *c, unsigned long rate)
+{
+ int i;
+
+ if (!c->dvfs)
+ return rate;
+
+ /* update min now, since no dvfs table was available during init */
+ if (!c->min_rate)
+ c->min_rate = c->dvfs->freqs[0];
+
+ for (i = 0; i < (c->dvfs->num_freqs - 1); i++) {
+ unsigned long f = c->dvfs->freqs[i];
+ if (f >= rate)
+ break;
+ }
+ return c->dvfs->freqs[i];
+}
+
+static int cbus_switch_one(struct clk *c, struct clk *p, u32 div, bool abort)
+{
+ int ret = 0;
+
+ /* set new divider if it is bigger than the current one */
+ if (c->div < c->mul * div) {
+ ret = clk_set_div(c, div);
+ if (ret) {
+ pr_err("%s: failed to set %s clock divider %u: %d\n",
+ __func__, c->name, div, ret);
+ if (abort)
+ return ret;
+ }
+ }
+
+ ret = clk_set_parent(c, p);
+ if (ret) {
+ pr_err("%s: failed to set %s clock parent %s: %d\n",
+ __func__, c->name, p->name, ret);
+ if (abort)
+ return ret;
+ }
+
+ /* set new divider if it is smaller than the current one */
+ if (c->div > c->mul * div) {
+ ret = clk_set_div(c, div);
+ if (ret)
+ pr_err("%s: failed to set %s clock divider %u: %d\n",
+ __func__, c->name, div, ret);
+ }
+
+ return ret;
+}
+
+static int cbus_backup(struct clk *c)
+{
+ int ret;
+ struct clk *user;
+
+ list_for_each_entry(user, &c->shared_bus_list,
+ u.shared_bus_user.node) {
+ bool enabled = user->u.shared_bus_user.client &&
+ (user->u.shared_bus_user.enabled ||
+ user->u.shared_bus_user.client->refcnt);
+ if (enabled) {
+ ret = cbus_switch_one(user->u.shared_bus_user.client,
+ c->shared_bus_backup.input,
+ c->shared_bus_backup.value *
+ user->div, true);
+ if (ret)
+ return ret;
+ }
+ }
+ return 0;
+}
+
+static void cbus_restore(struct clk *c)
+{
+ struct clk *user;
+
+ list_for_each_entry(user, &c->shared_bus_list,
+ u.shared_bus_user.node) {
+ bool back = user->u.shared_bus_user.client && (c->parent !=
+ user->u.shared_bus_user.client->parent);
+ if (back)
+ cbus_switch_one(user->u.shared_bus_user.client,
+ c->parent, c->div * user->div, false);
+ }
+}
+
+static int tegra3_clk_cbus_set_rate(struct clk *c, unsigned long rate)
+{
+ int ret;
+
+ if (rate == 0)
+ return 0;
+
+ ret = clk_enable(c->parent);
+ if (ret) {
+ pr_err("%s: failed to enable %s clock: %d\n",
+ __func__, c->name, ret);
+ return ret;
+ }
+
+ ret = cbus_backup(c);
+ if (ret)
+ goto out;
+
+ ret = clk_set_rate(c->parent, rate * c->div);
+ if (ret) {
+ pr_err("%s: failed to set %s clock rate %lu: %d\n",
+ __func__, c->name, rate, ret);
+ goto out;
+ }
+
+ cbus_restore(c);
+
+out:
+ clk_disable(c->parent);
+ return ret;
+}
+
+static struct clk_ops tegra_clk_cbus_ops = {
+ .init = tegra3_clk_cbus_init,
+ .enable = tegra3_clk_cbus_enable,
+ .set_rate = tegra3_clk_cbus_set_rate,
+ .round_rate = tegra3_clk_cbus_round_rate,
+ .shared_bus_update = tegra3_clk_shared_bus_update,
+};
+
+/* shared bus ops */
+/*
+ * Some clocks may have multiple downstream users that need to request a
+ * higher clock rate. Shared bus clocks provide a unique shared_bus_user
+ * clock to each user. The frequency of the bus is set to the highest
+ * enabled shared_bus_user clock, with a minimum value set by the
+ * shared bus.
+ */
+
+static noinline int shared_bus_set_rate(struct clk *bus, unsigned long rate,
+ unsigned long old_rate)
+{
+ int ret, mv, old_mv;
+ unsigned long bridge_rate = emc_bridge->u.shared_bus_user.rate;
+
+ /* If bridge is not needed (LPDDR2) just set bus rate */
+ if (tegra_emc_get_dram_type() == DRAM_TYPE_LPDDR2)
+ return clk_set_rate_locked(bus, rate);
+
+ mv = tegra_dvfs_predict_millivolts(bus, rate);
+ old_mv = tegra_dvfs_predict_millivolts(bus, old_rate);
+ if (IS_ERR_VALUE(mv) || IS_ERR_VALUE(old_mv)) {
+ pr_err("%s: Failed to predict %s voltage for %lu => %lu\n",
+ __func__, bus->name, old_rate, rate);
+ return -EINVAL;
+ }
+
+ /* emc bus: set bridge rate as intermediate step when crossing
+ * bridge threshold in any direction
+ */
+ if (bus->flags & PERIPH_EMC_ENB) {
+ if (((mv > TEGRA_EMC_BRIDGE_MVOLTS_MIN) &&
+ (old_rate < bridge_rate)) ||
+ ((old_mv > TEGRA_EMC_BRIDGE_MVOLTS_MIN) &&
+ (rate < bridge_rate))) {
+ ret = clk_set_rate_locked(bus, bridge_rate);
+ if (ret) {
+ pr_err("%s: Failed to set emc bridge rate %lu\n",
+ __func__, bridge_rate);
+ return ret;
+ }
+ }
+ return clk_set_rate_locked(bus, rate);
+ }
+
+ /* sbus and cbus: enable/disable emc bridge user when crossing voltage
+ * threshold up/down respectively; hence, emc rate is kept above the
+ * bridge rate as long as any sbus or cbus user requires high voltage
+ */
+ if ((mv > TEGRA_EMC_BRIDGE_MVOLTS_MIN) &&
+ (old_mv <= TEGRA_EMC_BRIDGE_MVOLTS_MIN)) {
+ ret = clk_enable(emc_bridge);
+ if (ret) {
+ pr_err("%s: Failed to enable emc bridge\n", __func__);
+ return ret;
+ }
+ }
+
+ ret = clk_set_rate_locked(bus, rate);
+ if (ret)
+ return ret;
+
+ if ((mv <= TEGRA_EMC_BRIDGE_MVOLTS_MIN) &&
+ (old_mv > TEGRA_EMC_BRIDGE_MVOLTS_MIN))
+ clk_disable(emc_bridge);
+
+ return 0;
+}
+
+static int tegra3_clk_shared_bus_update(struct clk *bus)
+{
+ struct clk *c;
+ unsigned long old_rate;
+ unsigned long rate = bus->min_rate;
+ unsigned long bw = 0;
+ unsigned long ceiling = bus->max_rate;
+
+ if (detach_shared_bus)
+ return 0;
+
+ list_for_each_entry(c, &bus->shared_bus_list,
+ u.shared_bus_user.node) {
+ /* Ignore requests from disabled users and from users with
+ fixed bus-to-client ratio */
+ if (c->u.shared_bus_user.enabled) {
+ switch (c->u.shared_bus_user.mode) {
+ case SHARED_BW:
+ bw += c->u.shared_bus_user.rate;
+ break;
+ case SHARED_CEILING:
+ ceiling = min(c->u.shared_bus_user.rate,
+ ceiling);
+ break;
+ case SHARED_AUTO:
+ case SHARED_FLOOR:
+ default:
+ rate = max(c->u.shared_bus_user.rate, rate);
+ }
+ }
+ }
+ rate = min(max(rate, bw), ceiling);
+
+ old_rate = clk_get_rate_locked(bus);
+ if (rate == old_rate)
+ return 0;
+
+ return shared_bus_set_rate(bus, rate, old_rate);
+};
+
+static void tegra_clk_shared_bus_init(struct clk *c)
+{
+ c->max_rate = c->parent->max_rate;
+ c->u.shared_bus_user.rate = c->parent->max_rate;
+ c->state = OFF;
+ c->set = true;
+
+ if (c->u.shared_bus_user.client_id) {
+ c->u.shared_bus_user.client =
+ tegra_get_clock_by_name(c->u.shared_bus_user.client_id);
+ if (!c->u.shared_bus_user.client) {
+ pr_err("%s: could not find clk %s\n", __func__,
+ c->u.shared_bus_user.client_id);
+ return;
+ }
+ c->u.shared_bus_user.client->flags |=
+ c->parent->flags & PERIPH_ON_CBUS;
+ c->flags |= c->parent->flags & PERIPH_ON_CBUS;
+ c->div = c->u.shared_bus_user.client_div ? : 1;
+ c->mul = 1;
+ }
+
+ list_add_tail(&c->u.shared_bus_user.node,
+ &c->parent->shared_bus_list);
+}
+
+static int tegra_clk_shared_bus_set_rate(struct clk *c, unsigned long rate)
+{
+ c->u.shared_bus_user.rate = rate;
+ tegra_clk_shared_bus_update(c->parent);
+ return 0;
+}
+
+static long tegra_clk_shared_bus_round_rate(struct clk *c, unsigned long rate)
+{
+ /* auto user follow others, by itself it run at minimum bus rate */
+ if (c->u.shared_bus_user.mode == SHARED_AUTO)
+ rate = 0;
+
+ return clk_round_rate(c->parent, rate);
+}
+
+static int tegra_clk_shared_bus_enable(struct clk *c)
+{
+ c->u.shared_bus_user.enabled = true;
+ tegra_clk_shared_bus_update(c->parent);
+ if (c->u.shared_bus_user.client) {
+ return clk_enable(c->u.shared_bus_user.client);
+ }
+ return 0;
+}
+
+static void tegra_clk_shared_bus_disable(struct clk *c)
+{
+ if (c->u.shared_bus_user.client)
+ clk_disable(c->u.shared_bus_user.client);
+ c->u.shared_bus_user.enabled = false;
+ tegra_clk_shared_bus_update(c->parent);
+}
+
+static void tegra_clk_shared_bus_reset(struct clk *c, bool assert)
+{
+ if (c->u.shared_bus_user.client) {
+ if (c->u.shared_bus_user.client->ops &&
+ c->u.shared_bus_user.client->ops->reset)
+ c->u.shared_bus_user.client->ops->reset(
+ c->u.shared_bus_user.client, assert);
+ }
+}
+
+static struct clk_ops tegra_clk_shared_bus_ops = {
+ .init = tegra_clk_shared_bus_init,
+ .enable = tegra_clk_shared_bus_enable,
+ .disable = tegra_clk_shared_bus_disable,
+ .set_rate = tegra_clk_shared_bus_set_rate,
+ .round_rate = tegra_clk_shared_bus_round_rate,
+ .reset = tegra_clk_shared_bus_reset,
+};
+
+/* emc bridge ops */
+/* On Tegra3 platforms emc configurations for DDR3 low rates can not work
+ * at high core voltage; the intermediate step (bridge) is mandatory whenever
+ * core voltage is crossing the threshold: TEGRA_EMC_BRIDGE_MVOLTS_MIN (fixed
+ * for the entire Tegra3 arch); also emc must run above the bridge rate if any
+ * other than emc clock requires high voltage. LP CPU, memory, sbus and cbus
+ * together include all clocks that may require core voltage above threshold
+ * (other peripherals can reach their maximum rates below threshold). LP CPU
+ * dependency is taken care of via tegra_emc_to_cpu_ratio() api. Memory clock
+ * transitions are forced to step through bridge rate; sbus and cbus control
+ * emc bridge to set emc clock floor as necessary.
+ *
+ * EMC bridge is implemented as a special emc shared bus user: initialized at
+ * minimum rate until updated once by emc dvfs setup; then it is only enabled
+ * or disabled when sbus and/or cbus voltage is crossing the threshold.
+ */
+static void tegra3_clk_emc_bridge_init(struct clk *c)
+{
+ tegra_clk_shared_bus_init(c);
+ c->u.shared_bus_user.rate = 0;
+}
+
+static int tegra3_clk_emc_bridge_set_rate(struct clk *c, unsigned long rate)
+{
+ if (c->u.shared_bus_user.rate == 0)
+ c->u.shared_bus_user.rate = rate;
+ return 0;
+}
+
+static struct clk_ops tegra_clk_emc_bridge_ops = {
+ .init = tegra3_clk_emc_bridge_init,
+ .enable = tegra_clk_shared_bus_enable,
+ .disable = tegra_clk_shared_bus_disable,
+ .set_rate = tegra3_clk_emc_bridge_set_rate,
+ .round_rate = tegra_clk_shared_bus_round_rate,
+};
+
+/* Clock definitions */
+static struct clk tegra_clk_32k = {
+ .name = "clk_32k",
+ .rate = 32768,
+ .ops = NULL,
+ .max_rate = 32768,
+};
+
+static struct clk tegra_clk_m = {
+ .name = "clk_m",
+ .flags = ENABLE_ON_INIT,
+ .ops = &tegra_clk_m_ops,
+ .reg = 0x1fc,
+ .reg_shift = 28,
+ .max_rate = 48000000,
+};
+
+static struct clk tegra_clk_m_div2 = {
+ .name = "clk_m_div2",
+ .ops = &tegra_clk_m_div_ops,
+ .parent = &tegra_clk_m,
+ .mul = 1,
+ .div = 2,
+ .state = ON,
+ .max_rate = 24000000,
+};
+
+static struct clk tegra_clk_m_div4 = {
+ .name = "clk_m_div4",
+ .ops = &tegra_clk_m_div_ops,
+ .parent = &tegra_clk_m,
+ .mul = 1,
+ .div = 4,
+ .state = ON,
+ .max_rate = 12000000,
+};
+
+static struct clk tegra_pll_ref = {
+ .name = "pll_ref",
+ .flags = ENABLE_ON_INIT,
+ .ops = &tegra_pll_ref_ops,
+ .parent = &tegra_clk_m,
+ .max_rate = 26000000,
+};
+
+static struct clk_pll_freq_table tegra_pll_c_freq_table[] = {
+ { 12000000, 1040000000, 520, 6, 1, 8},
+ { 13000000, 1040000000, 480, 6, 1, 8},
+ { 16800000, 1040000000, 495, 8, 1, 8}, /* actual: 1039.5 MHz */
+ { 19200000, 1040000000, 325, 6, 1, 6},
+ { 26000000, 1040000000, 520, 13, 1, 8},
+
+ { 12000000, 832000000, 416, 6, 1, 8},
+ { 13000000, 832000000, 832, 13, 1, 8},
+ { 16800000, 832000000, 396, 8, 1, 8}, /* actual: 831.6 MHz */
+ { 19200000, 832000000, 260, 6, 1, 8},
+ { 26000000, 832000000, 416, 13, 1, 8},
+
+ { 12000000, 624000000, 624, 12, 1, 8},
+ { 13000000, 624000000, 624, 13, 1, 8},
+ { 16800000, 600000000, 520, 14, 1, 8},
+ { 19200000, 624000000, 520, 16, 1, 8},
+ { 26000000, 624000000, 624, 26, 1, 8},
+
+ { 12000000, 600000000, 600, 12, 1, 8},
+ { 13000000, 600000000, 600, 13, 1, 8},
+ { 16800000, 600000000, 500, 14, 1, 8},
+ { 19200000, 600000000, 375, 12, 1, 6},
+ { 26000000, 600000000, 600, 26, 1, 8},
+
+ { 12000000, 520000000, 520, 12, 1, 8},
+ { 13000000, 520000000, 520, 13, 1, 8},
+ { 16800000, 520000000, 495, 16, 1, 8}, /* actual: 519.75 MHz */
+ { 19200000, 520000000, 325, 12, 1, 6},
+ { 26000000, 520000000, 520, 26, 1, 8},
+
+ { 12000000, 416000000, 416, 12, 1, 8},
+ { 13000000, 416000000, 416, 13, 1, 8},
+ { 16800000, 416000000, 396, 16, 1, 8}, /* actual: 415.8 MHz */
+ { 19200000, 416000000, 260, 12, 1, 6},
+ { 26000000, 416000000, 416, 26, 1, 8},
+ { 0, 0, 0, 0, 0, 0 },
+};
+
+static struct clk tegra_pll_c = {
+ .name = "pll_c",
+ .flags = PLL_HAS_CPCON,
+ .ops = &tegra_pll_ops,
+ .reg = 0x80,
+ .parent = &tegra_pll_ref,
+ .max_rate = 1400000000,
+ .u.pll = {
+ .input_min = 2000000,
+ .input_max = 31000000,
+ .cf_min = 1000000,
+ .cf_max = 6000000,
+ .vco_min = 20000000,
+ .vco_max = 1400000000,
+ .freq_table = tegra_pll_c_freq_table,
+ .lock_delay = 300,
+ },
+};
+
+static struct clk tegra_pll_c_out1 = {
+ .name = "pll_c_out1",
+ .ops = &tegra_pll_div_ops,
+ .flags = DIV_U71,
+ .parent = &tegra_pll_c,
+ .reg = 0x84,
+ .reg_shift = 0,
+ .max_rate = 700000000,
+};
+
+static struct clk_pll_freq_table tegra_pll_m_freq_table[] = {
+ { 12000000, 666000000, 666, 12, 1, 8},
+ { 13000000, 666000000, 666, 13, 1, 8},
+ { 16800000, 666000000, 555, 14, 1, 8},
+ { 19200000, 666000000, 555, 16, 1, 8},
+ { 26000000, 666000000, 666, 26, 1, 8},
+ { 12000000, 600000000, 600, 12, 1, 8},
+ { 13000000, 600000000, 600, 13, 1, 8},
+ { 16800000, 600000000, 500, 14, 1, 8},
+ { 19200000, 600000000, 375, 12, 1, 6},
+ { 26000000, 600000000, 600, 26, 1, 8},
+ { 0, 0, 0, 0, 0, 0 },
+};
+
+static struct clk tegra_pll_m = {
+ .name = "pll_m",
+ .flags = PLL_HAS_CPCON | PLLM,
+ .ops = &tegra_pll_ops,
+ .reg = 0x90,
+ .parent = &tegra_pll_ref,
+ .max_rate = 800000000,
+ .u.pll = {
+ .input_min = 2000000,
+ .input_max = 31000000,
+ .cf_min = 1000000,
+ .cf_max = 6000000,
+ .vco_min = 20000000,
+ .vco_max = 1200000000,
+ .freq_table = tegra_pll_m_freq_table,
+ .lock_delay = 300,
+ },
+};
+
+static struct clk tegra_pll_m_out1 = {
+ .name = "pll_m_out1",
+ .ops = &tegra_pll_div_ops,
+ .flags = DIV_U71,
+ .parent = &tegra_pll_m,
+ .reg = 0x94,
+ .reg_shift = 0,
+ .max_rate = 600000000,
+};
+
+static struct clk_pll_freq_table tegra_pll_p_freq_table[] = {
+ { 12000000, 216000000, 432, 12, 2, 8},
+ { 13000000, 216000000, 432, 13, 2, 8},
+ { 16800000, 216000000, 360, 14, 2, 8},
+ { 19200000, 216000000, 360, 16, 2, 8},
+ { 26000000, 216000000, 432, 26, 2, 8},
+ { 0, 0, 0, 0, 0, 0 },
+};
+
+static struct clk tegra_pll_p = {
+ .name = "pll_p",
+ .flags = ENABLE_ON_INIT | PLL_FIXED | PLL_HAS_CPCON,
+ .ops = &tegra_pll_ops,
+ .reg = 0xa0,
+ .parent = &tegra_pll_ref,
+ .max_rate = 432000000,
+ .u.pll = {
+ .input_min = 2000000,
+ .input_max = 31000000,
+ .cf_min = 1000000,
+ .cf_max = 6000000,
+ .vco_min = 20000000,
+ .vco_max = 1400000000,
+ .freq_table = tegra_pll_p_freq_table,
+ .lock_delay = 300,
+#ifdef CONFIG_TEGRA_SILICON_PLATFORM
+ .fixed_rate = 408000000,
+#else
+ .fixed_rate = 216000000,
+#endif
+ },
+};
+
+static struct clk tegra_pll_p_out1 = {
+ .name = "pll_p_out1",
+ .ops = &tegra_pll_div_ops,
+ .flags = ENABLE_ON_INIT | DIV_U71 | DIV_U71_FIXED,
+ .parent = &tegra_pll_p,
+ .reg = 0xa4,
+ .reg_shift = 0,
+ .max_rate = 432000000,
+};
+
+static struct clk tegra_pll_p_out2 = {
+ .name = "pll_p_out2",
+ .ops = &tegra_pll_div_ops,
+ .flags = ENABLE_ON_INIT | DIV_U71 | DIV_U71_FIXED,
+ .parent = &tegra_pll_p,
+ .reg = 0xa4,
+ .reg_shift = 16,
+ .max_rate = 432000000,
+};
+
+static struct clk tegra_pll_p_out3 = {
+ .name = "pll_p_out3",
+ .ops = &tegra_pll_div_ops,
+ .flags = ENABLE_ON_INIT | DIV_U71 | DIV_U71_FIXED,
+ .parent = &tegra_pll_p,
+ .reg = 0xa8,
+ .reg_shift = 0,
+ .max_rate = 432000000,
+};
+
+static struct clk tegra_pll_p_out4 = {
+ .name = "pll_p_out4",
+ .ops = &tegra_pll_div_ops,
+ .flags = ENABLE_ON_INIT | DIV_U71 | DIV_U71_FIXED,
+ .parent = &tegra_pll_p,
+ .reg = 0xa8,
+ .reg_shift = 16,
+ .max_rate = 432000000,
+};
+
+static struct clk_pll_freq_table tegra_pll_a_freq_table[] = {
+ { 9600000, 564480000, 294, 5, 1, 4},
+ { 9600000, 552960000, 288, 5, 1, 4},
+ { 9600000, 24000000, 5, 2, 1, 1},
+
+ { 28800000, 56448000, 49, 25, 1, 1},
+ { 28800000, 73728000, 64, 25, 1, 1},
+ { 28800000, 24000000, 5, 6, 1, 1},
+ { 0, 0, 0, 0, 0, 0 },
+};
+
+static struct clk tegra_pll_a = {
+ .name = "pll_a",
+ .flags = PLL_HAS_CPCON,
+ .ops = &tegra_pll_ops,
+ .reg = 0xb0,
+ .parent = &tegra_pll_p_out1,
+ .max_rate = 700000000,
+ .u.pll = {
+ .input_min = 2000000,
+ .input_max = 31000000,
+ .cf_min = 1000000,
+ .cf_max = 6000000,
+ .vco_min = 20000000,
+ .vco_max = 1400000000,
+ .freq_table = tegra_pll_a_freq_table,
+ .lock_delay = 300,
+ },
+};
+
+static struct clk tegra_pll_a_out0 = {
+ .name = "pll_a_out0",
+ .ops = &tegra_pll_div_ops,
+ .flags = DIV_U71,
+ .parent = &tegra_pll_a,
+ .reg = 0xb4,
+ .reg_shift = 0,
+ .max_rate = 100000000,
+};
+
+static struct clk_pll_freq_table tegra_pll_d_freq_table[] = {
+ { 12000000, 216000000, 216, 12, 1, 4},
+ { 13000000, 216000000, 216, 13, 1, 4},
+ { 16800000, 216000000, 180, 14, 1, 4},
+ { 19200000, 216000000, 180, 16, 1, 4},
+ { 26000000, 216000000, 216, 26, 1, 4},
+
+ { 12000000, 594000000, 594, 12, 1, 8},
+ { 13000000, 594000000, 594, 13, 1, 8},
+ { 16800000, 594000000, 495, 14, 1, 8},
+ { 19200000, 594000000, 495, 16, 1, 8},
+ { 26000000, 594000000, 594, 26, 1, 8},
+
+ { 12000000, 1000000000, 1000, 12, 1, 12},
+ { 13000000, 1000000000, 1000, 13, 1, 12},
+ { 19200000, 1000000000, 625, 12, 1, 8},
+ { 26000000, 1000000000, 1000, 26, 1, 12},
+
+ { 0, 0, 0, 0, 0, 0 },
+};
+
+static struct clk tegra_pll_d = {
+ .name = "pll_d",
+ .flags = PLL_HAS_CPCON | PLLD,
+ .ops = &tegra_plld_ops,
+ .reg = 0xd0,
+ .parent = &tegra_pll_ref,
+ .max_rate = 1000000000,
+ .u.pll = {
+ .input_min = 2000000,
+ .input_max = 40000000,
+ .cf_min = 1000000,
+ .cf_max = 6000000,
+ .vco_min = 40000000,
+ .vco_max = 1000000000,
+ .freq_table = tegra_pll_d_freq_table,
+ .lock_delay = 1000,
+ },
+};
+
+static struct clk tegra_pll_d_out0 = {
+ .name = "pll_d_out0",
+ .ops = &tegra_pll_div_ops,
+ .flags = DIV_2 | PLLD,
+ .parent = &tegra_pll_d,
+ .max_rate = 500000000,
+};
+
+static struct clk tegra_pll_d2 = {
+ .name = "pll_d2",
+ .flags = PLL_HAS_CPCON | PLL_ALT_MISC_REG | PLLD,
+ .ops = &tegra_plld_ops,
+ .reg = 0x4b8,
+ .parent = &tegra_pll_ref,
+ .max_rate = 1000000000,
+ .u.pll = {
+ .input_min = 2000000,
+ .input_max = 40000000,
+ .cf_min = 1000000,
+ .cf_max = 6000000,
+ .vco_min = 40000000,
+ .vco_max = 1000000000,
+ .freq_table = tegra_pll_d_freq_table,
+ .lock_delay = 1000,
+ },
+};
+
+static struct clk tegra_pll_d2_out0 = {
+ .name = "pll_d2_out0",
+ .ops = &tegra_pll_div_ops,
+ .flags = DIV_2 | PLLD,
+ .parent = &tegra_pll_d2,
+ .max_rate = 500000000,
+};
+
+static struct clk_pll_freq_table tegra_pll_u_freq_table[] = {
+ { 12000000, 480000000, 960, 12, 2, 12},
+ { 13000000, 480000000, 960, 13, 2, 12},
+ { 16800000, 480000000, 400, 7, 2, 5},
+ { 19200000, 480000000, 200, 4, 2, 3},
+ { 26000000, 480000000, 960, 26, 2, 12},
+ { 0, 0, 0, 0, 0, 0 },
+};
+
+static struct clk tegra_pll_u = {
+ .name = "pll_u",
+ .flags = PLL_HAS_CPCON | PLLU,
+ .ops = &tegra_pll_ops,
+ .reg = 0xc0,
+ .parent = &tegra_pll_ref,
+ .max_rate = 480000000,
+ .u.pll = {
+ .input_min = 2000000,
+ .input_max = 40000000,
+ .cf_min = 1000000,
+ .cf_max = 6000000,
+ .vco_min = 480000000,
+ .vco_max = 960000000,
+ .freq_table = tegra_pll_u_freq_table,
+ .lock_delay = 1000,
+ },
+};
+
+static struct clk_pll_freq_table tegra_pll_x_freq_table[] = {
+ /* 1.7 GHz */
+ { 12000000, 1700000000, 850, 6, 1, 8},
+ { 13000000, 1700000000, 915, 7, 1, 8}, /* actual: 1699.2 MHz */
+ { 16800000, 1700000000, 708, 7, 1, 8}, /* actual: 1699.2 MHz */
+ { 19200000, 1700000000, 885, 10, 1, 8}, /* actual: 1699.2 MHz */
+ { 26000000, 1700000000, 850, 13, 1, 8},
+
+ /* 1.6 GHz */
+ { 12000000, 1600000000, 800, 6, 1, 8},
+ { 13000000, 1600000000, 738, 6, 1, 8}, /* actual: 1599.0 MHz */
+ { 16800000, 1600000000, 857, 9, 1, 8}, /* actual: 1599.7 MHz */
+ { 19200000, 1600000000, 500, 6, 1, 8},
+ { 26000000, 1600000000, 800, 13, 1, 8},
+
+ /* 1.5 GHz */
+ { 12000000, 1500000000, 750, 6, 1, 8},
+ { 13000000, 1500000000, 923, 8, 1, 8}, /* actual: 1499.8 MHz */
+ { 16800000, 1500000000, 625, 7, 1, 8},
+ { 19200000, 1500000000, 625, 8, 1, 8},
+ { 26000000, 1500000000, 750, 13, 1, 8},
+
+ /* 1.4 GHz */
+ { 12000000, 1400000000, 700, 6, 1, 8},
+ { 13000000, 1400000000, 969, 9, 1, 8}, /* actual: 1399.7 MHz */
+ { 16800000, 1400000000, 1000, 12, 1, 8},
+ { 19200000, 1400000000, 875, 12, 1, 8},
+ { 26000000, 1400000000, 700, 13, 1, 8},
+
+ /* 1.3 GHz */
+ { 12000000, 1300000000, 975, 9, 1, 8},
+ { 13000000, 1300000000, 1000, 10, 1, 8},
+ { 16800000, 1300000000, 928, 12, 1, 8}, /* actual: 1299.2 MHz */
+ { 19200000, 1300000000, 812, 12, 1, 8}, /* actual: 1299.2 MHz */
+ { 26000000, 1300000000, 650, 13, 1, 8},
+
+ /* 1.2 GHz */
+ { 12000000, 1200000000, 1000, 10, 1, 8},
+ { 13000000, 1200000000, 923, 10, 1, 8}, /* actual: 1199.9 MHz */
+ { 16800000, 1200000000, 1000, 14, 1, 8},
+ { 19200000, 1200000000, 1000, 16, 1, 8},
+ { 26000000, 1200000000, 600, 13, 1, 8},
+
+ /* 1.1 GHz */
+ { 12000000, 1100000000, 825, 9, 1, 8},
+ { 13000000, 1100000000, 846, 10, 1, 8}, /* actual: 1099.8 MHz */
+ { 16800000, 1100000000, 982, 15, 1, 8}, /* actual: 1099.8 MHz */
+ { 19200000, 1100000000, 859, 15, 1, 8}, /* actual: 1099.5 MHz */
+ { 26000000, 1100000000, 550, 13, 1, 8},
+
+ /* 1 GHz */
+ { 12000000, 1000000000, 1000, 12, 1, 8},
+ { 13000000, 1000000000, 1000, 13, 1, 8},
+ { 16800000, 1000000000, 833, 14, 1, 8}, /* actual: 999.6 MHz */
+ { 19200000, 1000000000, 625, 12, 1, 8},
+ { 26000000, 1000000000, 1000, 26, 1, 8},
+
+ { 0, 0, 0, 0, 0, 0 },
+};
+
+static struct clk tegra_pll_x = {
+ .name = "pll_x",
+ .flags = PLL_HAS_CPCON | PLL_ALT_MISC_REG | PLLX,
+ .ops = &tegra_pll_ops,
+ .reg = 0xe0,
+ .parent = &tegra_pll_ref,
+ .max_rate = 1700000000,
+ .u.pll = {
+ .input_min = 2000000,
+ .input_max = 31000000,
+ .cf_min = 1000000,
+ .cf_max = 6000000,
+ .vco_min = 20000000,
+ .vco_max = 1700000000,
+ .freq_table = tegra_pll_x_freq_table,
+ .lock_delay = 300,
+ },
+};
+
+static struct clk tegra_pll_x_out0 = {
+ .name = "pll_x_out0",
+ .ops = &tegra_pll_div_ops,
+ .flags = DIV_2 | PLLX,
+ .parent = &tegra_pll_x,
+ .max_rate = 850000000,
+};
+
+
+static struct clk_pll_freq_table tegra_pll_e_freq_table[] = {
+ /* PLLE special case: use cpcon field to store cml divider value */
+ { 12000000, 100000000, 150, 1, 18, 11},
+ { 216000000, 100000000, 200, 18, 24, 13},
+#ifndef CONFIG_TEGRA_SILICON_PLATFORM
+ { 13000000, 100000000, 200, 1, 26, 13},
+#endif
+ { 0, 0, 0, 0, 0, 0 },
+};
+
+static struct clk tegra_pll_e = {
+ .name = "pll_e",
+ .flags = PLL_ALT_MISC_REG,
+ .ops = &tegra_plle_ops,
+ .reg = 0xe8,
+ .max_rate = 100000000,
+ .u.pll = {
+ .input_min = 12000000,
+ .input_max = 216000000,
+ .cf_min = 12000000,
+ .cf_max = 12000000,
+ .vco_min = 1200000000,
+ .vco_max = 2400000000U,
+ .freq_table = tegra_pll_e_freq_table,
+ .lock_delay = 300,
+ .fixed_rate = 100000000,
+ },
+};
+
+static struct clk tegra_cml0_clk = {
+ .name = "cml0",
+ .parent = &tegra_pll_e,
+ .ops = &tegra_cml_clk_ops,
+ .reg = PLLE_AUX,
+ .max_rate = 100000000,
+ .u.periph = {
+ .clk_num = 0,
+ },
+};
+
+static struct clk tegra_cml1_clk = {
+ .name = "cml1",
+ .parent = &tegra_pll_e,
+ .ops = &tegra_cml_clk_ops,
+ .reg = PLLE_AUX,
+ .max_rate = 100000000,
+ .u.periph = {
+ .clk_num = 1,
+ },
+};
+
+static struct clk tegra_pciex_clk = {
+ .name = "pciex",
+ .parent = &tegra_pll_e,
+ .ops = &tegra_pciex_clk_ops,
+ .max_rate = 100000000,
+ .u.periph = {
+ .clk_num = 74,
+ },
+};
+
+/* Audio sync clocks */
+#define SYNC_SOURCE(_id) \
+ { \
+ .name = #_id "_sync", \
+ .rate = 24000000, \
+ .max_rate = 24000000, \
+ .ops = &tegra_sync_source_ops \
+ }
+static struct clk tegra_sync_source_list[] = {
+ SYNC_SOURCE(spdif_in),
+ SYNC_SOURCE(i2s0),
+ SYNC_SOURCE(i2s1),
+ SYNC_SOURCE(i2s2),
+ SYNC_SOURCE(i2s3),
+ SYNC_SOURCE(i2s4),
+ SYNC_SOURCE(vimclk),
+};
+
+static struct clk_mux_sel mux_audio_sync_clk[] =
+{
+ { .input = &tegra_sync_source_list[0], .value = 0},
+ { .input = &tegra_sync_source_list[1], .value = 1},
+ { .input = &tegra_sync_source_list[2], .value = 2},
+ { .input = &tegra_sync_source_list[3], .value = 3},
+ { .input = &tegra_sync_source_list[4], .value = 4},
+ { .input = &tegra_sync_source_list[5], .value = 5},
+ { .input = &tegra_pll_a_out0, .value = 6},
+ { .input = &tegra_sync_source_list[6], .value = 7},
+ { 0, 0 }
+};
+
+#define AUDIO_SYNC_CLK(_id, _index) \
+ { \
+ .name = #_id, \
+ .inputs = mux_audio_sync_clk, \
+ .reg = 0x4A0 + (_index) * 4, \
+ .max_rate = 24000000, \
+ .ops = &tegra_audio_sync_clk_ops \
+ }
+static struct clk tegra_clk_audio_list[] = {
+ AUDIO_SYNC_CLK(audio0, 0),
+ AUDIO_SYNC_CLK(audio1, 1),
+ AUDIO_SYNC_CLK(audio2, 2),
+ AUDIO_SYNC_CLK(audio3, 3),
+ AUDIO_SYNC_CLK(audio4, 4),
+ AUDIO_SYNC_CLK(audio, 5), /* SPDIF */
+};
+
+#define AUDIO_SYNC_2X_CLK(_id, _index) \
+ { \
+ .name = #_id "_2x", \
+ .flags = PERIPH_NO_RESET, \
+ .max_rate = 48000000, \
+ .ops = &tegra_clk_double_ops, \
+ .reg = 0x49C, \
+ .reg_shift = 24 + (_index), \
+ .parent = &tegra_clk_audio_list[(_index)], \
+ .u.periph = { \
+ .clk_num = 113 + (_index), \
+ }, \
+ }
+static struct clk tegra_clk_audio_2x_list[] = {
+ AUDIO_SYNC_2X_CLK(audio0, 0),
+ AUDIO_SYNC_2X_CLK(audio1, 1),
+ AUDIO_SYNC_2X_CLK(audio2, 2),
+ AUDIO_SYNC_2X_CLK(audio3, 3),
+ AUDIO_SYNC_2X_CLK(audio4, 4),
+ AUDIO_SYNC_2X_CLK(audio, 5), /* SPDIF */
+};
+
+#define MUX_I2S_SPDIF(_id, _index) \
+static struct clk_mux_sel mux_pllaout0_##_id##_2x_pllp_clkm[] = { \
+ {.input = &tegra_pll_a_out0, .value = 0}, \
+ {.input = &tegra_clk_audio_2x_list[(_index)], .value = 1}, \
+ {.input = &tegra_pll_p, .value = 2}, \
+ {.input = &tegra_clk_m, .value = 3}, \
+ { 0, 0}, \
+}
+MUX_I2S_SPDIF(audio0, 0);
+MUX_I2S_SPDIF(audio1, 1);
+MUX_I2S_SPDIF(audio2, 2);
+MUX_I2S_SPDIF(audio3, 3);
+MUX_I2S_SPDIF(audio4, 4);
+MUX_I2S_SPDIF(audio, 5); /* SPDIF */
+
+/* External clock outputs (through PMC) */
+#define MUX_EXTERN_OUT(_id) \
+static struct clk_mux_sel mux_clkm_clkm2_clkm4_extern##_id[] = { \
+ {.input = &tegra_clk_m, .value = 0}, \
+ {.input = &tegra_clk_m_div2, .value = 1}, \
+ {.input = &tegra_clk_m_div4, .value = 2}, \
+ {.input = NULL, .value = 3}, /* placeholder */ \
+ { 0, 0}, \
+}
+MUX_EXTERN_OUT(1);
+MUX_EXTERN_OUT(2);
+MUX_EXTERN_OUT(3);
+
+static struct clk_mux_sel *mux_extern_out_list[] = {
+ mux_clkm_clkm2_clkm4_extern1,
+ mux_clkm_clkm2_clkm4_extern2,
+ mux_clkm_clkm2_clkm4_extern3,
+};
+
+#define CLK_OUT_CLK(_id) \
+ { \
+ .name = "clk_out_" #_id, \
+ .lookup = { \
+ .dev_id = "clk_out_" #_id, \
+ .con_id = "extern" #_id, \
+ }, \
+ .ops = &tegra_clk_out_ops, \
+ .reg = 0x1a8, \
+ .inputs = mux_clkm_clkm2_clkm4_extern##_id, \
+ .flags = MUX_CLK_OUT, \
+ .max_rate = 216000000, \
+ .u.periph = { \
+ .clk_num = (_id - 1) * 8 + 2, \
+ }, \
+ }
+static struct clk tegra_clk_out_list[] = {
+ CLK_OUT_CLK(1),
+ CLK_OUT_CLK(2),
+ CLK_OUT_CLK(3),
+};
+
+/* called after peripheral external clocks are initialized */
+static void init_clk_out_mux(void)
+{
+ int i;
+ struct clk *c;
+
+ /* output clock con_id is the name of peripheral
+ external clock connected to input 3 of the output mux */
+ for (i = 0; i < ARRAY_SIZE(tegra_clk_out_list); i++) {
+ c = tegra_get_clock_by_name(
+ tegra_clk_out_list[i].lookup.con_id);
+ if (!c)
+ pr_err("%s: could not find clk %s\n", __func__,
+ tegra_clk_out_list[i].lookup.con_id);
+ mux_extern_out_list[i][3].input = c;
+ }
+}
+
+/* Peripheral muxes */
+static struct clk_mux_sel mux_cclk_g[] = {
+ { .input = &tegra_clk_m, .value = 0},
+ { .input = &tegra_pll_c, .value = 1},
+ { .input = &tegra_clk_32k, .value = 2},
+ { .input = &tegra_pll_m, .value = 3},
+ { .input = &tegra_pll_p, .value = 4},
+ { .input = &tegra_pll_p_out4, .value = 5},
+ { .input = &tegra_pll_p_out3, .value = 6},
+ /* { .input = &tegra_clk_d, .value = 7}, - no use on tegra3 */
+ { .input = &tegra_pll_x, .value = 8},
+ { 0, 0},
+};
+
+static struct clk_mux_sel mux_cclk_lp[] = {
+ { .input = &tegra_clk_m, .value = 0},
+ { .input = &tegra_pll_c, .value = 1},
+ { .input = &tegra_clk_32k, .value = 2},
+ { .input = &tegra_pll_m, .value = 3},
+ { .input = &tegra_pll_p, .value = 4},
+ { .input = &tegra_pll_p_out4, .value = 5},
+ { .input = &tegra_pll_p_out3, .value = 6},
+ /* { .input = &tegra_clk_d, .value = 7}, - no use on tegra3 */
+ { .input = &tegra_pll_x_out0, .value = 8},
+ { .input = &tegra_pll_x, .value = 8 | SUPER_LP_DIV2_BYPASS},
+ { 0, 0},
+};
+
+static struct clk_mux_sel mux_sclk[] = {
+ { .input = &tegra_clk_m, .value = 0},
+ { .input = &tegra_pll_c_out1, .value = 1},
+ { .input = &tegra_pll_p_out4, .value = 2},
+ { .input = &tegra_pll_p_out3, .value = 3},
+ { .input = &tegra_pll_p_out2, .value = 4},
+ /* { .input = &tegra_clk_d, .value = 5}, - no use on tegra3 */
+ { .input = &tegra_clk_32k, .value = 6},
+ { .input = &tegra_pll_m_out1, .value = 7},
+ { 0, 0},
+};
+
+static struct clk tegra_clk_cclk_g = {
+ .name = "cclk_g",
+ .flags = DIV_U71 | DIV_U71_INT,
+ .inputs = mux_cclk_g,
+ .reg = 0x368,
+ .ops = &tegra_super_ops,
+ .max_rate = 1700000000,
+};
+
+static struct clk tegra_clk_cclk_lp = {
+ .name = "cclk_lp",
+ .flags = DIV_2 | DIV_U71 | DIV_U71_INT,
+ .inputs = mux_cclk_lp,
+ .reg = 0x370,
+ .ops = &tegra_super_ops,
+ .max_rate = 620000000,
+};
+
+static struct clk tegra_clk_sclk = {
+ .name = "sclk",
+ .inputs = mux_sclk,
+ .reg = 0x28,
+ .ops = &tegra_super_ops,
+ .max_rate = 334000000,
+ .min_rate = 40000000,
+};
+
+static struct clk tegra_clk_virtual_cpu_g = {
+ .name = "cpu_g",
+ .parent = &tegra_clk_cclk_g,
+ .ops = &tegra_cpu_ops,
+ .max_rate = 1700000000,
+ .u.cpu = {
+ .main = &tegra_pll_x,
+ .backup = &tegra_pll_p,
+ .mode = MODE_G,
+ },
+};
+
+static struct clk tegra_clk_virtual_cpu_lp = {
+ .name = "cpu_lp",
+ .parent = &tegra_clk_cclk_lp,
+ .ops = &tegra_cpu_ops,
+ .max_rate = 620000000,
+ .u.cpu = {
+ .main = &tegra_pll_x,
+ .backup = &tegra_pll_p,
+ .mode = MODE_LP,
+ },
+};
+
+static struct clk_mux_sel mux_cpu_cmplx[] = {
+ { .input = &tegra_clk_virtual_cpu_g, .value = 0},
+ { .input = &tegra_clk_virtual_cpu_lp, .value = 1},
+ { 0, 0},
+};
+
+static struct clk tegra_clk_cpu_cmplx = {
+ .name = "cpu",
+ .inputs = mux_cpu_cmplx,
+ .ops = &tegra_cpu_cmplx_ops,
+ .max_rate = 1700000000,
+};
+
+static struct clk tegra_clk_cop = {
+ .name = "cop",
+ .parent = &tegra_clk_sclk,
+ .ops = &tegra_cop_ops,
+ .max_rate = 334000000,
+};
+
+static struct clk tegra_clk_hclk = {
+ .name = "hclk",
+ .flags = DIV_BUS,
+ .parent = &tegra_clk_sclk,
+ .reg = 0x30,
+ .reg_shift = 4,
+ .ops = &tegra_bus_ops,
+ .max_rate = 334000000,
+ .min_rate = 40000000,
+};
+
+static struct clk tegra_clk_pclk = {
+ .name = "pclk",
+ .flags = DIV_BUS,
+ .parent = &tegra_clk_hclk,
+ .reg = 0x30,
+ .reg_shift = 0,
+ .ops = &tegra_bus_ops,
+ .max_rate = 167000000,
+ .min_rate = 40000000,
+};
+
+static struct raw_notifier_head sbus_rate_change_nh;
+
+static struct clk tegra_clk_sbus_cmplx = {
+ .name = "sbus",
+ .parent = &tegra_clk_sclk,
+ .ops = &tegra_sbus_cmplx_ops,
+ .u.system = {
+ .pclk = &tegra_clk_pclk,
+ .hclk = &tegra_clk_hclk,
+ .sclk_low = &tegra_pll_p_out4,
+ .sclk_high = &tegra_pll_m_out1,
+#ifdef CONFIG_TEGRA_SILICON_PLATFORM
+ .threshold = 204000000, /* exact factor of low range pll_p */
+#else
+ .threshold = 108000000, /* exact factor of low range pll_p */
+#endif
+ },
+ .rate_change_nh = &sbus_rate_change_nh,
+};
+
+static struct clk tegra_clk_blink = {
+ .name = "blink",
+ .parent = &tegra_clk_32k,
+ .reg = 0x40,
+ .ops = &tegra_blink_clk_ops,
+ .max_rate = 32768,
+};
+
+static struct clk_mux_sel mux_pllm_pllc_pllp_plla[] = {
+ { .input = &tegra_pll_m, .value = 0},
+ { .input = &tegra_pll_c, .value = 1},
+ { .input = &tegra_pll_p, .value = 2},
+ { .input = &tegra_pll_a_out0, .value = 3},
+ { 0, 0},
+};
+
+static struct clk_mux_sel mux_pllm_pllc_pllp_clkm[] = {
+ { .input = &tegra_pll_m, .value = 0},
+ /* { .input = &tegra_pll_c, .value = 1}, not used on tegra3 */
+ { .input = &tegra_pll_p, .value = 2},
+ { .input = &tegra_clk_m, .value = 3},
+ { 0, 0},
+};
+
+static struct clk_mux_sel mux_pllp_pllc_pllm_clkm[] = {
+ { .input = &tegra_pll_p, .value = 0},
+ { .input = &tegra_pll_c, .value = 1},
+#ifndef CONFIG_TEGRA_PLLM_RESTRICTED
+ { .input = &tegra_pll_m, .value = 2},
+#endif
+ { .input = &tegra_clk_m, .value = 3},
+ { 0, 0},
+};
+
+static struct clk_mux_sel mux_pllp_clkm[] = {
+ { .input = &tegra_pll_p, .value = 0},
+ { .input = &tegra_clk_m, .value = 3},
+ { 0, 0},
+};
+
+static struct clk_mux_sel mux_pllp_plld_pllc_clkm[] = {
+ {.input = &tegra_pll_p, .value = 0},
+ {.input = &tegra_pll_d_out0, .value = 1},
+ {.input = &tegra_pll_c, .value = 2},
+ {.input = &tegra_clk_m, .value = 3},
+ { 0, 0},
+};
+
+static struct clk_mux_sel mux_pllp_pllm_plld_plla_pllc_plld2_clkm[] = {
+ {.input = &tegra_pll_p, .value = 0},
+#ifndef CONFIG_TEGRA_PLLM_RESTRICTED
+ {.input = &tegra_pll_m, .value = 1},
+#endif
+ {.input = &tegra_pll_d_out0, .value = 2},
+ {.input = &tegra_pll_a_out0, .value = 3},
+ {.input = &tegra_pll_c, .value = 4},
+ {.input = &tegra_pll_d2_out0, .value = 5},
+ {.input = &tegra_clk_m, .value = 6},
+ { 0, 0},
+};
+
+static struct clk_mux_sel mux_plla_pllc_pllp_clkm[] = {
+ { .input = &tegra_pll_a_out0, .value = 0},
+ /* { .input = &tegra_pll_c, .value = 1}, no use on tegra3 */
+ { .input = &tegra_pll_p, .value = 2},
+ { .input = &tegra_clk_m, .value = 3},
+ { 0, 0},
+};
+
+static struct clk_mux_sel mux_pllp_pllc_clk32_clkm[] = {
+ {.input = &tegra_pll_p, .value = 0},
+ {.input = &tegra_pll_c, .value = 1},
+ {.input = &tegra_clk_32k, .value = 2},
+ {.input = &tegra_clk_m, .value = 3},
+ { 0, 0},
+};
+
+static struct clk_mux_sel mux_pllp_pllc_clkm_clk32[] = {
+ {.input = &tegra_pll_p, .value = 0},
+ {.input = &tegra_pll_c, .value = 1},
+ {.input = &tegra_clk_m, .value = 2},
+ {.input = &tegra_clk_32k, .value = 3},
+ { 0, 0},
+};
+
+static struct clk_mux_sel mux_pllp_pllc_pllm[] = {
+ {.input = &tegra_pll_p, .value = 0},
+ {.input = &tegra_pll_c, .value = 1},
+#ifndef CONFIG_TEGRA_PLLM_RESTRICTED
+ {.input = &tegra_pll_m, .value = 2},
+#endif
+ { 0, 0},
+};
+
+static struct clk_mux_sel mux_clk_m[] = {
+ { .input = &tegra_clk_m, .value = 0},
+ { 0, 0},
+};
+
+static struct clk_mux_sel mux_pllp_out3[] = {
+ { .input = &tegra_pll_p_out3, .value = 0},
+ { 0, 0},
+};
+
+static struct clk_mux_sel mux_plld_out0[] = {
+ { .input = &tegra_pll_d_out0, .value = 0},
+ { 0, 0},
+};
+
+static struct clk_mux_sel mux_plld_out0_plld2_out0[] = {
+ { .input = &tegra_pll_d_out0, .value = 0},
+ { .input = &tegra_pll_d2_out0, .value = 1},
+ { 0, 0},
+};
+
+static struct clk_mux_sel mux_clk_32k[] = {
+ { .input = &tegra_clk_32k, .value = 0},
+ { 0, 0},
+};
+
+static struct clk_mux_sel mux_plla_clk32_pllp_clkm_plle[] = {
+ { .input = &tegra_pll_a_out0, .value = 0},
+ { .input = &tegra_clk_32k, .value = 1},
+ { .input = &tegra_pll_p, .value = 2},
+ { .input = &tegra_clk_m, .value = 3},
+ { .input = &tegra_pll_e, .value = 4},
+ { 0, 0},
+};
+
+static struct raw_notifier_head emc_rate_change_nh;
+
+static struct clk tegra_clk_emc = {
+ .name = "emc",
+ .ops = &tegra_emc_clk_ops,
+ .reg = 0x19c,
+ .max_rate = 800000000,
+ .min_rate = 25000000,
+ .inputs = mux_pllm_pllc_pllp_clkm,
+ .flags = MUX | DIV_U71 | PERIPH_EMC_ENB,
+ .u.periph = {
+ .clk_num = 57,
+ },
+ .rate_change_nh = &emc_rate_change_nh,
+};
+
+static struct clk tegra_clk_emc_bridge = {
+ .name = "bridge.emc",
+ .ops = &tegra_clk_emc_bridge_ops,
+ .parent = &tegra_clk_emc,
+};
+
+static struct clk tegra_clk_cbus = {
+ .name = "cbus",
+ .parent = &tegra_pll_c,
+ .ops = &tegra_clk_cbus_ops,
+ .max_rate = 700000000,
+ .mul = 1,
+ .div = 2,
+ .flags = PERIPH_ON_CBUS,
+ .shared_bus_backup = {
+ .input = &tegra_pll_p,
+ .value = 2,
+ }
+};
+
+#define PERIPH_CLK(_name, _dev, _con, _clk_num, _reg, _max, _inputs, _flags) \
+ { \
+ .name = _name, \
+ .lookup = { \
+ .dev_id = _dev, \
+ .con_id = _con, \
+ }, \
+ .ops = &tegra_periph_clk_ops, \
+ .reg = _reg, \
+ .inputs = _inputs, \
+ .flags = _flags, \
+ .max_rate = _max, \
+ .u.periph = { \
+ .clk_num = _clk_num, \
+ }, \
+ }
+
+#define PERIPH_CLK_EX(_name, _dev, _con, _clk_num, _reg, _max, _inputs, \
+ _flags, _ops) \
+ { \
+ .name = _name, \
+ .lookup = { \
+ .dev_id = _dev, \
+ .con_id = _con, \
+ }, \
+ .ops = _ops, \
+ .reg = _reg, \
+ .inputs = _inputs, \
+ .flags = _flags, \
+ .max_rate = _max, \
+ .u.periph = { \
+ .clk_num = _clk_num, \
+ }, \
+ }
+
+#define SHARED_CLK(_name, _dev, _con, _parent, _id, _div, _mode)\
+ { \
+ .name = _name, \
+ .lookup = { \
+ .dev_id = _dev, \
+ .con_id = _con, \
+ }, \
+ .ops = &tegra_clk_shared_bus_ops, \
+ .parent = _parent, \
+ .u.shared_bus_user = { \
+ .client_id = _id, \
+ .client_div = _div, \
+ .mode = _mode, \
+ }, \
+ }
+struct clk tegra_list_clks[] = {
+ PERIPH_CLK("apbdma", "tegra-dma", NULL, 34, 0, 26000000, mux_clk_m, 0),
+ PERIPH_CLK("rtc", "rtc-tegra", NULL, 4, 0, 32768, mux_clk_32k, PERIPH_NO_RESET | PERIPH_ON_APB),
+ PERIPH_CLK("kbc", "tegra-kbc", NULL, 36, 0, 32768, mux_clk_32k, PERIPH_NO_RESET | PERIPH_ON_APB),
+ PERIPH_CLK("timer", "timer", NULL, 5, 0, 26000000, mux_clk_m, 0),
+ PERIPH_CLK("kfuse", "kfuse-tegra", NULL, 40, 0, 26000000, mux_clk_m, 0),
+ PERIPH_CLK("fuse", "fuse-tegra", "fuse", 39, 0, 26000000, mux_clk_m, PERIPH_ON_APB),
+ PERIPH_CLK("fuse_burn", "fuse-tegra", "fuse_burn", 39, 0, 26000000, mux_clk_m, PERIPH_ON_APB),
+ PERIPH_CLK("apbif", "tegra30-ahub", "apbif", 107, 0, 26000000, mux_clk_m, 0),
+ PERIPH_CLK("i2s0", "tegra30-i2s.0", NULL, 30, 0x1d8, 26000000, mux_pllaout0_audio0_2x_pllp_clkm, MUX | DIV_U71 | PERIPH_ON_APB),
+ PERIPH_CLK("i2s1", "tegra30-i2s.1", NULL, 11, 0x100, 26000000, mux_pllaout0_audio1_2x_pllp_clkm, MUX | DIV_U71 | PERIPH_ON_APB),
+ PERIPH_CLK("i2s2", "tegra30-i2s.2", NULL, 18, 0x104, 26000000, mux_pllaout0_audio2_2x_pllp_clkm, MUX | DIV_U71 | PERIPH_ON_APB),
+ PERIPH_CLK("i2s3", "tegra30-i2s.3", NULL, 101, 0x3bc, 26000000, mux_pllaout0_audio3_2x_pllp_clkm, MUX | DIV_U71 | PERIPH_ON_APB),
+ PERIPH_CLK("i2s4", "tegra30-i2s.4", NULL, 102, 0x3c0, 26000000, mux_pllaout0_audio4_2x_pllp_clkm, MUX | DIV_U71 | PERIPH_ON_APB),
+ PERIPH_CLK("spdif_out", "tegra30-spdif", "spdif_out", 10, 0x108, 100000000, mux_pllaout0_audio_2x_pllp_clkm, MUX | DIV_U71 | PERIPH_ON_APB),
+ PERIPH_CLK("spdif_in", "tegra30-spdif", "spdif_in", 10, 0x10c, 100000000, mux_pllp_pllc_pllm, MUX | DIV_U71 | PERIPH_ON_APB),
+ PERIPH_CLK("pwm", "pwm", NULL, 17, 0x110, 432000000, mux_pllp_pllc_clk32_clkm, MUX | MUX_PWM | DIV_U71 | PERIPH_ON_APB),
+ PERIPH_CLK("d_audio", "tegra30-ahub", "d_audio", 106, 0x3d0, 48000000, mux_plla_pllc_pllp_clkm, MUX | DIV_U71),
+ PERIPH_CLK("dam0", "tegra30-dam.0", NULL, 108, 0x3d8, 48000000, mux_plla_pllc_pllp_clkm, MUX | DIV_U71),
+ PERIPH_CLK("dam1", "tegra30-dam.1", NULL, 109, 0x3dc, 48000000, mux_plla_pllc_pllp_clkm, MUX | DIV_U71),
+ PERIPH_CLK("dam2", "tegra30-dam.2", NULL, 110, 0x3e0, 48000000, mux_plla_pllc_pllp_clkm, MUX | DIV_U71),
+ PERIPH_CLK("hda", "tegra30-hda", "hda", 125, 0x428, 108000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71),
+ PERIPH_CLK("hda2codec_2x", "tegra30-hda", "hda2codec", 111, 0x3e4, 48000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71),
+ PERIPH_CLK("hda2hdmi", "tegra30-hda", "hda2hdmi", 128, 0, 48000000, mux_clk_m, 0),
+ PERIPH_CLK("sbc1", "spi_tegra.0", NULL, 41, 0x134, 160000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71 | PERIPH_ON_APB),
+ PERIPH_CLK("sbc2", "spi_tegra.1", NULL, 44, 0x118, 160000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71 | PERIPH_ON_APB),
+ PERIPH_CLK("sbc3", "spi_tegra.2", NULL, 46, 0x11c, 160000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71 | PERIPH_ON_APB),
+ PERIPH_CLK("sbc4", "spi_tegra.3", NULL, 68, 0x1b4, 160000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71 | PERIPH_ON_APB),
+ PERIPH_CLK("sbc5", "spi_tegra.4", NULL, 104, 0x3c8, 160000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71 | PERIPH_ON_APB),
+ PERIPH_CLK("sbc6", "spi_tegra.5", NULL, 105, 0x3cc, 160000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71 | PERIPH_ON_APB),
+ PERIPH_CLK("sata_oob", "tegra_sata_oob", NULL, 123, 0x420, 216000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71),
+ PERIPH_CLK("sata", "tegra_sata", NULL, 124, 0x424, 216000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71),
+ PERIPH_CLK("sata_cold", "tegra_sata_cold", NULL, 129, 0, 48000000, mux_clk_m, 0),
+ PERIPH_CLK_EX("ndflash","tegra_nand", NULL, 13, 0x160, 240000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71, &tegra_nand_clk_ops),
+ PERIPH_CLK("ndspeed", "tegra_nand_speed", NULL, 80, 0x3f8, 240000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71),
+ PERIPH_CLK("vfir", "vfir", NULL, 7, 0x168, 72000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71 | PERIPH_ON_APB),
+ PERIPH_CLK("sdmmc1", "sdhci-tegra.0", NULL, 14, 0x150, 208000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), /* scales with voltage */
+ PERIPH_CLK("sdmmc2", "sdhci-tegra.1", NULL, 9, 0x154, 104000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), /* scales with voltage */
+ PERIPH_CLK("sdmmc3", "sdhci-tegra.2", NULL, 69, 0x1bc, 208000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), /* scales with voltage */
+ PERIPH_CLK("sdmmc4", "sdhci-tegra.3", NULL, 15, 0x164, 104000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), /* scales with voltage */
+ PERIPH_CLK("vcp", "tegra-avp", "vcp", 29, 0, 250000000, mux_clk_m, 0),
+ PERIPH_CLK("bsea", "tegra-avp", "bsea", 62, 0, 250000000, mux_clk_m, 0),
+ PERIPH_CLK("bsev", "tegra-aes", "bsev", 63, 0, 250000000, mux_clk_m, 0),
+ PERIPH_CLK("vde", "vde", NULL, 61, 0x1c8, 520000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71 | DIV_U71_INT),
+ PERIPH_CLK("csite", "csite", NULL, 73, 0x1d4, 144000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), /* max rate ??? */
+ PERIPH_CLK("la", "la", NULL, 76, 0x1f8, 26000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71),
+ PERIPH_CLK("owr", "tegra_w1", NULL, 71, 0x1cc, 26000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71 | PERIPH_ON_APB),
+ PERIPH_CLK("nor", "tegra-nor", NULL, 42, 0x1d0, 127000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), /* requires min voltage */
+ PERIPH_CLK("mipi", "mipi", NULL, 50, 0x174, 60000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71 | PERIPH_ON_APB), /* scales with voltage */
+ PERIPH_CLK("i2c1", "tegra-i2c.0", NULL, 12, 0x124, 26000000, mux_pllp_clkm, MUX | DIV_U16 | PERIPH_ON_APB),
+ PERIPH_CLK("i2c2", "tegra-i2c.1", NULL, 54, 0x198, 26000000, mux_pllp_clkm, MUX | DIV_U16 | PERIPH_ON_APB),
+ PERIPH_CLK("i2c3", "tegra-i2c.2", NULL, 67, 0x1b8, 26000000, mux_pllp_clkm, MUX | DIV_U16 | PERIPH_ON_APB),
+ PERIPH_CLK("i2c4", "tegra-i2c.3", NULL, 103, 0x3c4, 26000000, mux_pllp_clkm, MUX | DIV_U16 | PERIPH_ON_APB),
+ PERIPH_CLK("i2c5", "tegra-i2c.4", NULL, 47, 0x128, 26000000, mux_pllp_clkm, MUX | DIV_U16 | PERIPH_ON_APB),
+ PERIPH_CLK("uarta", "tegra_uart.0", NULL, 6, 0x178, 800000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71 | DIV_U71_UART | PERIPH_ON_APB),
+ PERIPH_CLK("uartb", "tegra_uart.1", NULL, 7, 0x17c, 800000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71 | DIV_U71_UART | PERIPH_ON_APB),
+ PERIPH_CLK("uartc", "tegra_uart.2", NULL, 55, 0x1a0, 800000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71 | DIV_U71_UART | PERIPH_ON_APB),
+ PERIPH_CLK("uartd", "tegra_uart.3", NULL, 65, 0x1c0, 800000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71 | DIV_U71_UART | PERIPH_ON_APB),
+ PERIPH_CLK("uarte", "tegra_uart.4", NULL, 66, 0x1c4, 800000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71 | DIV_U71_UART | PERIPH_ON_APB),
+ PERIPH_CLK("uarta_dbg", "serial8250.0", "uarta",6, 0x178, 800000000, mux_pllp_clkm, MUX | DIV_U71 | DIV_U71_UART | PERIPH_ON_APB),
+ PERIPH_CLK("uartb_dbg", "serial8250.0", "uartb",7, 0x17c, 800000000, mux_pllp_clkm, MUX | DIV_U71 | DIV_U71_UART | PERIPH_ON_APB),
+ PERIPH_CLK("uartc_dbg", "serial8250.0", "uartc",55, 0x1a0, 800000000, mux_pllp_clkm, MUX | DIV_U71 | DIV_U71_UART | PERIPH_ON_APB),
+ PERIPH_CLK("uartd_dbg", "serial8250.0", "uartd",65, 0x1c0, 800000000, mux_pllp_clkm, MUX | DIV_U71 | DIV_U71_UART | PERIPH_ON_APB),
+ PERIPH_CLK("uarte_dbg", "serial8250.0", "uarte",66, 0x1c4, 800000000, mux_pllp_clkm, MUX | DIV_U71 | DIV_U71_UART | PERIPH_ON_APB),
+ PERIPH_CLK_EX("vi", "tegra_camera", "vi", 20, 0x148, 425000000, mux_pllm_pllc_pllp_plla, MUX | DIV_U71 | DIV_U71_INT, &tegra_vi_clk_ops),
+ PERIPH_CLK("3d", "3d", NULL, 24, 0x158, 520000000, mux_pllm_pllc_pllp_plla, MUX | DIV_U71 | DIV_U71_INT | DIV_U71_IDLE | PERIPH_MANUAL_RESET),
+ PERIPH_CLK("3d2", "3d2", NULL, 98, 0x3b0, 520000000, mux_pllm_pllc_pllp_plla, MUX | DIV_U71 | DIV_U71_INT | DIV_U71_IDLE | PERIPH_MANUAL_RESET),
+ PERIPH_CLK("2d", "2d", NULL, 21, 0x15c, 520000000, mux_pllm_pllc_pllp_plla, MUX | DIV_U71 | DIV_U71_INT | DIV_U71_IDLE),
+ PERIPH_CLK("vi_sensor", "tegra_camera", "vi_sensor", 20, 0x1a8, 150000000, mux_pllm_pllc_pllp_plla, MUX | DIV_U71 | PERIPH_NO_RESET),
+ PERIPH_CLK("epp", "epp", NULL, 19, 0x16c, 520000000, mux_pllm_pllc_pllp_plla, MUX | DIV_U71 | DIV_U71_INT),
+ PERIPH_CLK("mpe", "mpe", NULL, 60, 0x170, 520000000, mux_pllm_pllc_pllp_plla, MUX | DIV_U71 | DIV_U71_INT),
+ PERIPH_CLK("host1x", "host1x", NULL, 28, 0x180, 260000000, mux_pllm_pllc_pllp_plla, MUX | DIV_U71 | DIV_U71_INT),
+ PERIPH_CLK("cve", "cve", NULL, 49, 0x140, 250000000, mux_pllp_plld_pllc_clkm, MUX | DIV_U71), /* requires min voltage */
+ PERIPH_CLK("tvo", "tvo", NULL, 49, 0x188, 250000000, mux_pllp_plld_pllc_clkm, MUX | DIV_U71), /* requires min voltage */
+ PERIPH_CLK_EX("dtv", "dtv", NULL, 79, 0x1dc, 250000000, mux_clk_m, 0, &tegra_dtv_clk_ops),
+ PERIPH_CLK("hdmi", "hdmi", NULL, 51, 0x18c, 148500000, mux_pllp_pllm_plld_plla_pllc_plld2_clkm, MUX | MUX8 | DIV_U71),
+ PERIPH_CLK("tvdac", "tvdac", NULL, 53, 0x194, 220000000, mux_pllp_plld_pllc_clkm, MUX | DIV_U71), /* requires min voltage */
+ PERIPH_CLK("disp1", "tegradc.0", NULL, 27, 0x138, 600000000, mux_pllp_pllm_plld_plla_pllc_plld2_clkm, MUX | MUX8),
+ PERIPH_CLK("disp2", "tegradc.1", NULL, 26, 0x13c, 600000000, mux_pllp_pllm_plld_plla_pllc_plld2_clkm, MUX | MUX8),
+ PERIPH_CLK("usbd", "fsl-tegra-udc", NULL, 22, 0, 480000000, mux_clk_m, 0), /* requires min voltage */
+ PERIPH_CLK("usb2", "tegra-ehci.1", NULL, 58, 0, 480000000, mux_clk_m, 0), /* requires min voltage */
+ PERIPH_CLK("usb3", "tegra-ehci.2", NULL, 59, 0, 480000000, mux_clk_m, 0), /* requires min voltage */
+ PERIPH_CLK("dsia", "tegradc.0", "dsia", 48, 0, 500000000, mux_plld_out0, 0),
+ PERIPH_CLK_EX("dsib", "tegradc.1", "dsib", 82, 0xd0, 500000000, mux_plld_out0_plld2_out0, MUX | PLLD, &tegra_dsib_clk_ops),
+ PERIPH_CLK("csi", "tegra_camera", "csi", 52, 0, 102000000, mux_pllp_out3, 0),
+ PERIPH_CLK("isp", "tegra_camera", "isp", 23, 0, 150000000, mux_clk_m, 0), /* same frequency as VI */
+ PERIPH_CLK("csus", "tegra_camera", "csus", 92, 0, 150000000, mux_clk_m, PERIPH_NO_RESET),
+
+ PERIPH_CLK("tsensor", "tegra-tsensor", NULL, 100, 0x3b8, 216000000, mux_pllp_pllc_clkm_clk32, MUX | DIV_U71),
+ PERIPH_CLK("actmon", "actmon", NULL, 119, 0x3e8, 216000000, mux_pllp_pllc_clk32_clkm, MUX | DIV_U71),
+ PERIPH_CLK("extern1", "extern1", NULL, 120, 0x3ec, 216000000, mux_plla_clk32_pllp_clkm_plle, MUX | MUX8 | DIV_U71),
+ PERIPH_CLK("extern2", "extern2", NULL, 121, 0x3f0, 216000000, mux_plla_clk32_pllp_clkm_plle, MUX | MUX8 | DIV_U71),
+ PERIPH_CLK("extern3", "extern3", NULL, 122, 0x3f4, 216000000, mux_plla_clk32_pllp_clkm_plle, MUX | MUX8 | DIV_U71),
+ PERIPH_CLK("i2cslow", "i2cslow", NULL, 81, 0x3fc, 26000000, mux_pllp_pllc_clk32_clkm, MUX | DIV_U71 | PERIPH_ON_APB),
+ PERIPH_CLK("pcie", "tegra-pcie", "pcie", 70, 0, 250000000, mux_clk_m, 0),
+ PERIPH_CLK("afi", "tegra-pcie", "afi", 72, 0, 250000000, mux_clk_m, 0),
+ PERIPH_CLK("se", "se", NULL, 127, 0x42c, 520000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71| DIV_U71_INT),
+
+ SHARED_CLK("avp.sclk", "tegra-avp", "sclk", &tegra_clk_sbus_cmplx, NULL, 0, 0),
+ SHARED_CLK("bsea.sclk", "tegra-aes", "sclk", &tegra_clk_sbus_cmplx, NULL, 0, 0),
+ SHARED_CLK("usbd.sclk", "fsl-tegra-udc", "sclk", &tegra_clk_sbus_cmplx, NULL, 0, 0),
+ SHARED_CLK("usb1.sclk", "tegra-ehci.0", "sclk", &tegra_clk_sbus_cmplx, NULL, 0, 0),
+ SHARED_CLK("usb2.sclk", "tegra-ehci.1", "sclk", &tegra_clk_sbus_cmplx, NULL, 0, 0),
+ SHARED_CLK("usb3.sclk", "tegra-ehci.2", "sclk", &tegra_clk_sbus_cmplx, NULL, 0, 0),
+ SHARED_CLK("mon.avp", "tegra_actmon", "avp", &tegra_clk_sbus_cmplx, NULL, 0, 0),
+ SHARED_CLK("cap.sclk", "cap_sclk", NULL, &tegra_clk_sbus_cmplx, NULL, 0, SHARED_CEILING),
+ SHARED_CLK("floor.sclk", "floor_sclk", NULL, &tegra_clk_sbus_cmplx, NULL, 0, 0),
+
+ SHARED_CLK("avp.emc", "tegra-avp", "emc", &tegra_clk_emc, NULL, 0, 0),
+ SHARED_CLK("cpu.emc", "cpu", "emc", &tegra_clk_emc, NULL, 0, 0),
+ SHARED_CLK("disp1.emc", "tegradc.0", "emc", &tegra_clk_emc, NULL, 0, SHARED_BW),
+ SHARED_CLK("disp2.emc", "tegradc.1", "emc", &tegra_clk_emc, NULL, 0, SHARED_BW),
+ SHARED_CLK("hdmi.emc", "hdmi", "emc", &tegra_clk_emc, NULL, 0, 0),
+ SHARED_CLK("usbd.emc", "fsl-tegra-udc", "emc", &tegra_clk_emc, NULL, 0, 0),
+ SHARED_CLK("usb1.emc", "tegra-ehci.0", "emc", &tegra_clk_emc, NULL, 0, 0),
+ SHARED_CLK("usb2.emc", "tegra-ehci.1", "emc", &tegra_clk_emc, NULL, 0, 0),
+ SHARED_CLK("usb3.emc", "tegra-ehci.2", "emc", &tegra_clk_emc, NULL, 0, 0),
+ SHARED_CLK("mon.emc", "tegra_actmon", "emc", &tegra_clk_emc, NULL, 0, 0),
+ SHARED_CLK("cap.emc", "cap.emc", NULL, &tegra_clk_emc, NULL, 0, SHARED_CEILING),
+ SHARED_CLK("3d.emc", "tegra_gr3d", "emc", &tegra_clk_emc, NULL, 0, 0),
+ SHARED_CLK("2d.emc", "tegra_gr2d", "emc", &tegra_clk_emc, NULL, 0, 0),
+ SHARED_CLK("mpe.emc", "tegra_mpe", "emc", &tegra_clk_emc, NULL, 0, 0),
+ SHARED_CLK("floor.emc", "floor.emc", NULL, &tegra_clk_emc, NULL, 0, 0),
+
+ SHARED_CLK("host1x.cbus", "tegra_host1x", "host1x", &tegra_clk_cbus, "host1x", 2, SHARED_AUTO),
+ SHARED_CLK("3d.cbus", "tegra_gr3d", "gr3d", &tegra_clk_cbus, "3d", 0, 0),
+ SHARED_CLK("3d2.cbus", "tegra_gr3d", "gr3d2", &tegra_clk_cbus, "3d2", 0, 0),
+ SHARED_CLK("2d.cbus", "tegra_gr2d", "gr2d", &tegra_clk_cbus, "2d", 0, 0),
+ SHARED_CLK("epp.cbus", "tegra_gr2d", "epp", &tegra_clk_cbus, "epp", 0, 0),
+ SHARED_CLK("mpe.cbus", "tegra_mpe", "mpe", &tegra_clk_cbus, "mpe", 0, 0),
+ SHARED_CLK("vde.cbus", "tegra-avp", "vde", &tegra_clk_cbus, "vde", 0, 0),
+ SHARED_CLK("se.cbus", "tegra-se", NULL, &tegra_clk_cbus, "se", 0, 0),
+ SHARED_CLK("cap.cbus", "cap.cbus", NULL, &tegra_clk_cbus, NULL, 0, SHARED_CEILING),
+ SHARED_CLK("floor.cbus", "floor.cbus", NULL, &tegra_clk_cbus, NULL, 0, 0),
+};
+
+#define CLK_DUPLICATE(_name, _dev, _con) \
+ { \
+ .name = _name, \
+ .lookup = { \
+ .dev_id = _dev, \
+ .con_id = _con, \
+ }, \
+ }
+
+/* Some clocks may be used by different drivers depending on the board
+ * configuration. List those here to register them twice in the clock lookup
+ * table under two names.
+ */
+struct clk_duplicate tegra_clk_duplicates[] = {
+ CLK_DUPLICATE("usbd", "utmip-pad", NULL),
+ CLK_DUPLICATE("usbd", "tegra-ehci.0", NULL),
+ CLK_DUPLICATE("usbd", "tegra-otg", NULL),
+ CLK_DUPLICATE("hdmi", "tegradc.0", "hdmi"),
+ CLK_DUPLICATE("hdmi", "tegradc.1", "hdmi"),
+ CLK_DUPLICATE("dsib", "tegradc.0", "dsib"),
+ CLK_DUPLICATE("dsia", "tegradc.1", "dsia"),
+ CLK_DUPLICATE("pwm", "tegra_pwm.0", NULL),
+ CLK_DUPLICATE("pwm", "tegra_pwm.1", NULL),
+ CLK_DUPLICATE("pwm", "tegra_pwm.2", NULL),
+ CLK_DUPLICATE("pwm", "tegra_pwm.3", NULL),
+ CLK_DUPLICATE("cop", "tegra-avp", "cop"),
+ CLK_DUPLICATE("bsev", "tegra-avp", "bsev"),
+ CLK_DUPLICATE("cop", "nvavp", "cop"),
+ CLK_DUPLICATE("bsev", "nvavp", "bsev"),
+ CLK_DUPLICATE("vde", "tegra-aes", "vde"),
+ CLK_DUPLICATE("bsea", "tegra-aes", "bsea"),
+ CLK_DUPLICATE("bsea", "nvavp", "bsea"),
+ CLK_DUPLICATE("cml1", "tegra_sata_cml", NULL),
+ CLK_DUPLICATE("cml0", "tegra_pcie", "cml"),
+ CLK_DUPLICATE("pciex", "tegra_pcie", "pciex"),
+ CLK_DUPLICATE("i2c1", "tegra-i2c-slave.0", NULL),
+ CLK_DUPLICATE("i2c2", "tegra-i2c-slave.1", NULL),
+ CLK_DUPLICATE("i2c3", "tegra-i2c-slave.2", NULL),
+ CLK_DUPLICATE("i2c4", "tegra-i2c-slave.3", NULL),
+ CLK_DUPLICATE("i2c5", "tegra-i2c-slave.4", NULL),
+ CLK_DUPLICATE("sbc1", "spi_slave_tegra.0", NULL),
+ CLK_DUPLICATE("sbc2", "spi_slave_tegra.1", NULL),
+ CLK_DUPLICATE("sbc3", "spi_slave_tegra.2", NULL),
+ CLK_DUPLICATE("sbc4", "spi_slave_tegra.3", NULL),
+ CLK_DUPLICATE("sbc5", "spi_slave_tegra.4", NULL),
+ CLK_DUPLICATE("sbc6", "spi_slave_tegra.5", NULL),
+ CLK_DUPLICATE("twd", "smp_twd", NULL),
+ CLK_DUPLICATE("vcp", "nvavp", "vcp"),
+ CLK_DUPLICATE("avp.sclk", "nvavp", "sclk"),
+ CLK_DUPLICATE("avp.emc", "nvavp", "emc"),
+ CLK_DUPLICATE("vde.cbus", "nvavp", "vde"),
+};
+
+struct clk *tegra_ptr_clks[] = {
+ &tegra_clk_32k,
+ &tegra_clk_m,
+ &tegra_clk_m_div2,
+ &tegra_clk_m_div4,
+ &tegra_pll_ref,
+ &tegra_pll_m,
+ &tegra_pll_m_out1,
+ &tegra_pll_c,
+ &tegra_pll_c_out1,
+ &tegra_pll_p,
+ &tegra_pll_p_out1,
+ &tegra_pll_p_out2,
+ &tegra_pll_p_out3,
+ &tegra_pll_p_out4,
+ &tegra_pll_a,
+ &tegra_pll_a_out0,
+ &tegra_pll_d,
+ &tegra_pll_d_out0,
+ &tegra_pll_d2,
+ &tegra_pll_d2_out0,
+ &tegra_pll_u,
+ &tegra_pll_x,
+ &tegra_pll_x_out0,
+ &tegra_pll_e,
+ &tegra_cml0_clk,
+ &tegra_cml1_clk,
+ &tegra_pciex_clk,
+ &tegra_clk_cclk_g,
+ &tegra_clk_cclk_lp,
+ &tegra_clk_sclk,
+ &tegra_clk_hclk,
+ &tegra_clk_pclk,
+ &tegra_clk_virtual_cpu_g,
+ &tegra_clk_virtual_cpu_lp,
+ &tegra_clk_cpu_cmplx,
+ &tegra_clk_blink,
+ &tegra_clk_cop,
+ &tegra_clk_sbus_cmplx,
+ &tegra_clk_emc,
+ &tegra3_clk_twd,
+ &tegra_clk_emc_bridge,
+ &tegra_clk_cbus,
+};
+
+static bool tegra3_clk_is_parent_allowed(struct clk *c, struct clk *p)
+{
+ if (c->flags & PERIPH_ON_CBUS)
+ return p != &tegra_pll_m;
+ return true;
+}
+
+static void tegra3_init_one_clock(struct clk *c)
+{
+ clk_init(c);
+ INIT_LIST_HEAD(&c->shared_bus_list);
+ if (!c->lookup.dev_id && !c->lookup.con_id)
+ c->lookup.con_id = c->name;
+ c->lookup.clk = c;
+ clkdev_add(&c->lookup);
+}
+
+/*
+ * Emergency throttle of G-CPU by setting G-super clock skipper underneath
+ * clock framework, dvfs, and cpufreq driver s/w layers. Can be called in
+ * ISR context for EDP events. When releasing throttle, LP-divider is cleared
+ * just in case it was set as a result of save/restore operations across
+ * cluster switch (should not happen)
+ */
+void tegra_edp_throttle_cpu_now(u8 factor)
+{
+ if (factor > 1) {
+ if (!is_lp_cluster())
+ tegra3_super_clk_skipper_update(
+ &tegra_clk_cclk_g, 0, factor - 1);
+ } else {
+ tegra3_super_clk_skipper_update(&tegra_clk_cclk_g, 0, 0);
+ tegra3_super_clk_skipper_update(&tegra_clk_cclk_lp, 0, 0);
+ }
+}
+
+#ifdef CONFIG_CPU_FREQ
+
+/*
+ * Frequency table index must be sequential starting at 0 and frequencies
+ * must be ascending.
+ */
+
+static struct cpufreq_frequency_table freq_table_300MHz[] = {
+ { 0, 204000 },
+ { 1, 300000 },
+ { 2, CPUFREQ_TABLE_END },
+};
+
+static struct cpufreq_frequency_table freq_table_1p0GHz[] = {
+ { 0, 102000 },
+ { 1, 204000 },
+ { 2, 312000 },
+ { 3, 456000 },
+ { 4, 608000 },
+ { 5, 760000 },
+ { 6, 816000 },
+ { 7, 912000 },
+ { 8, 1000000 },
+ { 9, CPUFREQ_TABLE_END },
+};
+
+static struct cpufreq_frequency_table freq_table_1p3GHz[] = {
+ { 0, 102000 },
+ { 1, 204000 },
+ { 2, 340000 },
+ { 3, 475000 },
+ { 4, 640000 },
+ { 5, 760000 },
+ { 6, 880000 },
+ { 7, 1000000 },
+ { 8, 1100000 },
+ { 9, 1200000 },
+ {10, 1300000 },
+ {11, CPUFREQ_TABLE_END },
+};
+
+static struct cpufreq_frequency_table freq_table_1p4GHz[] = {
+ { 0, 102000 },
+ { 1, 204000 },
+ { 2, 370000 },
+ { 3, 475000 },
+ { 4, 620000 },
+ { 5, 760000 },
+ { 6, 880000 },
+ { 7, 1000000 },
+ { 8, 1100000 },
+ { 9, 1200000 },
+ {10, 1300000 },
+ {11, 1400000 },
+ {12, CPUFREQ_TABLE_END },
+};
+
+static struct cpufreq_frequency_table freq_table_1p5GHz[] = {
+ { 0, 102000 },
+ { 1, 204000 },
+ { 2, 340000 },
+ { 3, 475000 },
+ { 4, 640000 },
+ { 5, 760000 },
+ { 6, 880000 },
+ { 7, 1000000 },
+ { 8, 1100000 },
+ { 9, 1200000 },
+ {10, 1300000 },
+ {11, 1400000 },
+ {12, 1500000 },
+ {13, CPUFREQ_TABLE_END },
+};
+
+static struct cpufreq_frequency_table freq_table_1p7GHz[] = {
+ { 0, 102000 },
+ { 1, 204000 },
+ { 2, 370000 },
+ { 3, 475000 },
+ { 4, 620000 },
+ { 5, 800000 },
+ { 6, 1000000 },
+ { 7, 1150000 },
+ { 8, 1300000 },
+ { 9, 1400000 },
+ {10, 1500000 },
+ {11, 1600000 },
+ {12, 1700000 },
+ {13, CPUFREQ_TABLE_END },
+};
+
+static struct tegra_cpufreq_table_data cpufreq_tables[] = {
+ { freq_table_300MHz, 0, 1 },
+ { freq_table_1p0GHz, 1, 7, 2},
+ { freq_table_1p3GHz, 1, 9, 2},
+ { freq_table_1p4GHz, 1, 10, 2},
+ { freq_table_1p5GHz, 1, 11, 2},
+ { freq_table_1p7GHz, 1, 11, 2},
+};
+
+static int clip_cpu_rate_limits(
+ struct cpufreq_frequency_table *freq_table,
+ struct cpufreq_policy *policy,
+ struct clk *cpu_clk_g,
+ struct clk *cpu_clk_lp)
+{
+ int idx, ret;
+
+ /* clip CPU G mode maximum frequency to table entry */
+ ret = cpufreq_frequency_table_target(policy, freq_table,
+ cpu_clk_g->max_rate / 1000, CPUFREQ_RELATION_H, &idx);
+ if (ret) {
+ pr_err("%s: G CPU max rate %lu outside of cpufreq table",
+ __func__, cpu_clk_g->max_rate);
+ return ret;
+ }
+ cpu_clk_g->max_rate = freq_table[idx].frequency * 1000;
+ if (cpu_clk_g->max_rate < cpu_clk_lp->max_rate) {
+ pr_err("%s: G CPU max rate %lu is below LP CPU max rate %lu",
+ __func__, cpu_clk_g->max_rate, cpu_clk_lp->max_rate);
+ return -EINVAL;
+ }
+
+ /* clip CPU LP mode maximum frequency to table entry, and
+ set CPU G mode minimum frequency one table step below */
+ ret = cpufreq_frequency_table_target(policy, freq_table,
+ cpu_clk_lp->max_rate / 1000, CPUFREQ_RELATION_H, &idx);
+ if (ret || !idx) {
+ pr_err("%s: LP CPU max rate %lu %s of cpufreq table", __func__,
+ cpu_clk_lp->max_rate, ret ? "outside" : "at the bottom");
+ return ret;
+ }
+ cpu_clk_lp->max_rate = freq_table[idx].frequency * 1000;
+ cpu_clk_g->min_rate = freq_table[idx-1].frequency * 1000;
+ return 0;
+}
+
+struct tegra_cpufreq_table_data *tegra_cpufreq_table_get(void)
+{
+ int i, ret;
+ unsigned long selection_rate;
+ struct clk *cpu_clk_g = tegra_get_clock_by_name("cpu_g");
+ struct clk *cpu_clk_lp = tegra_get_clock_by_name("cpu_lp");
+
+ /* For table selection use top cpu_g rate in dvfs ladder; selection
+ rate may exceed cpu max_rate (e.g., because of edp limitations on
+ cpu voltage) - in any case max_rate will be clipped to the table */
+ if (cpu_clk_g->dvfs && cpu_clk_g->dvfs->num_freqs)
+ selection_rate =
+ cpu_clk_g->dvfs->freqs[cpu_clk_g->dvfs->num_freqs - 1];
+ else
+ selection_rate = cpu_clk_g->max_rate;
+
+ for (i = 0; i < ARRAY_SIZE(cpufreq_tables); i++) {
+ struct cpufreq_policy policy;
+ policy.cpu = 0; /* any on-line cpu */
+ ret = cpufreq_frequency_table_cpuinfo(
+ &policy, cpufreq_tables[i].freq_table);
+ if (!ret) {
+ if ((policy.max * 1000) == selection_rate) {
+ ret = clip_cpu_rate_limits(
+ cpufreq_tables[i].freq_table,
+ &policy, cpu_clk_g, cpu_clk_lp);
+ if (!ret)
+ return &cpufreq_tables[i];
+ }
+ }
+ }
+ WARN(1, "%s: No cpufreq table matching G & LP cpu ranges", __func__);
+ return NULL;
+}
+
+/* On DDR3 platforms there is an implicit dependency in this mapping: when cpu
+ * exceeds max dvfs level for LP CPU clock at TEGRA_EMC_BRIDGE_MVOLTS_MIN, the
+ * respective emc rate should be above TEGRA_EMC_BRIDGE_RATE_MIN
+ */
+/* FIXME: explicitly check this dependency */
+unsigned long tegra_emc_to_cpu_ratio(unsigned long cpu_rate)
+{
+ static unsigned long emc_max_rate = 0;
+
+ if (emc_max_rate == 0)
+ emc_max_rate = clk_round_rate(
+ tegra_get_clock_by_name("emc"), ULONG_MAX);
+
+ /* Vote on memory bus frequency based on cpu frequency;
+ cpu rate is in kHz, emc rate is in Hz */
+ if (cpu_rate >= 750000)
+ return emc_max_rate; /* cpu >= 750 MHz, emc max */
+ else if (cpu_rate >= 450000)
+ return emc_max_rate/2; /* cpu >= 500 MHz, emc max/2 */
+ else if (cpu_rate >= 250000)
+ return 100000000; /* cpu >= 250 MHz, emc 100 MHz */
+ else
+ return 0; /* emc min */
+}
+#endif
+
+#ifdef CONFIG_PM_SLEEP
+static u32 clk_rst_suspend[RST_DEVICES_NUM + CLK_OUT_ENB_NUM +
+ PERIPH_CLK_SOURCE_NUM + 22];
+
+static int tegra_clk_suspend(void)
+{
+ unsigned long off;
+ u32 *ctx = clk_rst_suspend;
+
+ *ctx++ = clk_readl(OSC_CTRL) & OSC_CTRL_MASK;
+ *ctx++ = clk_readl(CPU_SOFTRST_CTRL);
+ *ctx++ = clk_readl(tegra_pll_c.reg + PLL_BASE);
+ *ctx++ = clk_readl(tegra_pll_c.reg + PLL_MISC(&tegra_pll_c));
+ *ctx++ = clk_readl(tegra_pll_a.reg + PLL_BASE);
+ *ctx++ = clk_readl(tegra_pll_a.reg + PLL_MISC(&tegra_pll_a));
+ *ctx++ = clk_readl(tegra_pll_d.reg + PLL_BASE);
+ *ctx++ = clk_readl(tegra_pll_d.reg + PLL_MISC(&tegra_pll_d));
+ *ctx++ = clk_readl(tegra_pll_d2.reg + PLL_BASE);
+ *ctx++ = clk_readl(tegra_pll_d2.reg + PLL_MISC(&tegra_pll_d2));
+
+ *ctx++ = clk_readl(tegra_pll_m_out1.reg);
+ *ctx++ = clk_readl(tegra_pll_a_out0.reg);
+ *ctx++ = clk_readl(tegra_pll_c_out1.reg);
+
+ *ctx++ = clk_readl(tegra_clk_cclk_g.reg);
+ *ctx++ = clk_readl(tegra_clk_cclk_g.reg + SUPER_CLK_DIVIDER);
+ *ctx++ = clk_readl(tegra_clk_cclk_lp.reg);
+ *ctx++ = clk_readl(tegra_clk_cclk_lp.reg + SUPER_CLK_DIVIDER);
+
+ *ctx++ = clk_readl(tegra_clk_sclk.reg);
+ *ctx++ = clk_readl(tegra_clk_sclk.reg + SUPER_CLK_DIVIDER);
+ *ctx++ = clk_readl(tegra_clk_pclk.reg);
+
+ for (off = PERIPH_CLK_SOURCE_I2S1; off <= PERIPH_CLK_SOURCE_OSC;
+ off += 4) {
+ if (off == PERIPH_CLK_SOURCE_EMC)
+ continue;
+ *ctx++ = clk_readl(off);
+ }
+ for (off = PERIPH_CLK_SOURCE_G3D2; off <= PERIPH_CLK_SOURCE_SE;
+ off+=4) {
+ *ctx++ = clk_readl(off);
+ }
+ for (off = AUDIO_DLY_CLK; off <= AUDIO_SYNC_CLK_SPDIF; off+=4) {
+ *ctx++ = clk_readl(off);
+ }
+
+ *ctx++ = clk_readl(RST_DEVICES_L);
+ *ctx++ = clk_readl(RST_DEVICES_H);
+ *ctx++ = clk_readl(RST_DEVICES_U);
+ *ctx++ = clk_readl(RST_DEVICES_V);
+ *ctx++ = clk_readl(RST_DEVICES_W);
+
+ *ctx++ = clk_readl(CLK_OUT_ENB_L);
+ *ctx++ = clk_readl(CLK_OUT_ENB_H);
+ *ctx++ = clk_readl(CLK_OUT_ENB_U);
+ *ctx++ = clk_readl(CLK_OUT_ENB_V);
+ *ctx++ = clk_readl(CLK_OUT_ENB_W);
+
+ *ctx++ = clk_readl(MISC_CLK_ENB);
+ *ctx++ = clk_readl(CLK_MASK_ARM);
+
+ return 0;
+}
+
+static void tegra_clk_resume(void)
+{
+ unsigned long off;
+ const u32 *ctx = clk_rst_suspend;
+ u32 val;
+ u32 pllc_base;
+ u32 plla_base;
+ u32 plld_base;
+ u32 plld2_base;
+ struct clk *p;
+
+ val = clk_readl(OSC_CTRL) & ~OSC_CTRL_MASK;
+ val |= *ctx++;
+ clk_writel(val, OSC_CTRL);
+ clk_writel(*ctx++, CPU_SOFTRST_CTRL);
+
+ /* Since we are going to reset devices in this function, pllc/a is
+ * required to be enabled. The actual value will be restore back later.
+ */
+ pllc_base = *ctx++;
+ clk_writel(pllc_base | PLL_BASE_ENABLE, tegra_pll_c.reg + PLL_BASE);
+ clk_writel(*ctx++, tegra_pll_c.reg + PLL_MISC(&tegra_pll_c));
+
+ plla_base = *ctx++;
+ clk_writel(plla_base | PLL_BASE_ENABLE, tegra_pll_a.reg + PLL_BASE);
+ clk_writel(*ctx++, tegra_pll_a.reg + PLL_MISC(&tegra_pll_a));
+
+ plld_base = *ctx++;
+ clk_writel(plld_base | PLL_BASE_ENABLE, tegra_pll_d.reg + PLL_BASE);
+ clk_writel(*ctx++, tegra_pll_d.reg + PLL_MISC(&tegra_pll_d));
+
+ plld2_base = *ctx++;
+ clk_writel(plld2_base | PLL_BASE_ENABLE, tegra_pll_d2.reg + PLL_BASE);
+ clk_writel(*ctx++, tegra_pll_d2.reg + PLL_MISC(&tegra_pll_d2));
+
+ udelay(1000);
+
+ clk_writel(*ctx++, tegra_pll_m_out1.reg);
+ clk_writel(*ctx++, tegra_pll_a_out0.reg);
+ clk_writel(*ctx++, tegra_pll_c_out1.reg);
+
+ clk_writel(*ctx++, tegra_clk_cclk_g.reg);
+ clk_writel(*ctx++, tegra_clk_cclk_g.reg + SUPER_CLK_DIVIDER);
+ clk_writel(*ctx++, tegra_clk_cclk_lp.reg);
+ clk_writel(*ctx++, tegra_clk_cclk_lp.reg + SUPER_CLK_DIVIDER);
+
+ clk_writel(*ctx++, tegra_clk_sclk.reg);
+ clk_writel(*ctx++, tegra_clk_sclk.reg + SUPER_CLK_DIVIDER);
+ clk_writel(*ctx++, tegra_clk_pclk.reg);
+
+ /* enable all clocks before configuring clock sources */
+ clk_writel(0xfdfffff1ul, CLK_OUT_ENB_L);
+ clk_writel(0xfefff7f7ul, CLK_OUT_ENB_H);
+ clk_writel(0x75f79bfful, CLK_OUT_ENB_U);
+ clk_writel(0xfffffffful, CLK_OUT_ENB_V);
+ clk_writel(0x00003ffful, CLK_OUT_ENB_W);
+ wmb();
+
+ for (off = PERIPH_CLK_SOURCE_I2S1; off <= PERIPH_CLK_SOURCE_OSC;
+ off += 4) {
+ if (off == PERIPH_CLK_SOURCE_EMC)
+ continue;
+ clk_writel(*ctx++, off);
+ }
+ for (off = PERIPH_CLK_SOURCE_G3D2; off <= PERIPH_CLK_SOURCE_SE;
+ off += 4) {
+ clk_writel(*ctx++, off);
+ }
+ for (off = AUDIO_DLY_CLK; off <= AUDIO_SYNC_CLK_SPDIF; off+=4) {
+ clk_writel(*ctx++, off);
+ }
+ wmb();
+
+ clk_writel(*ctx++, RST_DEVICES_L);
+ clk_writel(*ctx++, RST_DEVICES_H);
+ clk_writel(*ctx++, RST_DEVICES_U);
+
+ /* For LP0 resume, don't reset lpcpu, since we are running from it */
+ val = *ctx++;
+ val &= ~RST_DEVICES_V_SWR_CPULP_RST_DIS;
+ clk_writel(val, RST_DEVICES_V);
+
+ clk_writel(*ctx++, RST_DEVICES_W);
+ wmb();
+
+ clk_writel(*ctx++, CLK_OUT_ENB_L);
+ clk_writel(*ctx++, CLK_OUT_ENB_H);
+ clk_writel(*ctx++, CLK_OUT_ENB_U);
+
+ /* For LP0 resume, clk to lpcpu is required to be on */
+ val = *ctx++;
+ val |= CLK_OUT_ENB_V_CLK_ENB_CPULP_EN;
+ clk_writel(val, CLK_OUT_ENB_V);
+
+ clk_writel(*ctx++, CLK_OUT_ENB_W);
+ wmb();
+
+ clk_writel(*ctx++, MISC_CLK_ENB);
+ clk_writel(*ctx++, CLK_MASK_ARM);
+
+ /* Restore back the actual pllc/a value */
+ /* FIXME: need to root cause why pllc is required to be on
+ * clk_writel(pllc_base, tegra_pll_c.reg + PLL_BASE);
+ */
+ clk_writel(plla_base, tegra_pll_a.reg + PLL_BASE);
+ clk_writel(plld_base, tegra_pll_d.reg + PLL_BASE);
+ clk_writel(plld2_base, tegra_pll_d2.reg + PLL_BASE);
+
+ /* Since EMC clock is not restored, and may not preserve parent across
+ suspend, update current state, and mark EMC DFS as out of sync */
+ p = tegra_clk_emc.parent;
+ tegra3_periph_clk_init(&tegra_clk_emc);
+
+ if (p != tegra_clk_emc.parent) {
+ /* FIXME: old parent is left enabled here even if EMC was its
+ only child before suspend (never happens on Tegra3) */
+ pr_debug("EMC parent(refcount) across suspend: %s(%d) : %s(%d)",
+ p->name, p->refcnt, tegra_clk_emc.parent->name,
+ tegra_clk_emc.parent->refcnt);
+
+ BUG_ON(!p->refcnt);
+ p->refcnt--;
+
+ /* the new parent is enabled by low level code, but ref count
+ need to be updated up to the root */
+ p = tegra_clk_emc.parent;
+ while (p && ((p->refcnt++) == 0))
+ p = p->parent;
+ }
+ tegra_emc_timing_invalidate();
+
+ tegra3_pll_clk_init(&tegra_pll_u); /* Re-init utmi parameters */
+ tegra3_pll_clk_init(&tegra_pll_p); /* Fire a bug if not restored */
+}
+#else
+#define tegra_clk_suspend NULL
+#define tegra_clk_resume NULL
+#endif
+
+static struct syscore_ops tegra_clk_syscore_ops = {
+ .suspend = tegra_clk_suspend,
+ .resume = tegra_clk_resume,
+};
+
+void __init tegra_soc_init_clocks(void)
+{
+ int i;
+ struct clk *c;
+
+ for (i = 0; i < ARRAY_SIZE(tegra_ptr_clks); i++)
+ tegra3_init_one_clock(tegra_ptr_clks[i]);
+
+ for (i = 0; i < ARRAY_SIZE(tegra_list_clks); i++)
+ tegra3_init_one_clock(&tegra_list_clks[i]);
+
+ for (i = 0; i < ARRAY_SIZE(tegra_clk_duplicates); i++) {
+ c = tegra_get_clock_by_name(tegra_clk_duplicates[i].name);
+ if (!c) {
+ pr_err("%s: Unknown duplicate clock %s\n", __func__,
+ tegra_clk_duplicates[i].name);
+ continue;
+ }
+
+ tegra_clk_duplicates[i].lookup.clk = c;
+ clkdev_add(&tegra_clk_duplicates[i].lookup);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(tegra_sync_source_list); i++)
+ tegra3_init_one_clock(&tegra_sync_source_list[i]);
+ for (i = 0; i < ARRAY_SIZE(tegra_clk_audio_list); i++)
+ tegra3_init_one_clock(&tegra_clk_audio_list[i]);
+ for (i = 0; i < ARRAY_SIZE(tegra_clk_audio_2x_list); i++)
+ tegra3_init_one_clock(&tegra_clk_audio_2x_list[i]);
+
+ init_clk_out_mux();
+ for (i = 0; i < ARRAY_SIZE(tegra_clk_out_list); i++)
+ tegra3_init_one_clock(&tegra_clk_out_list[i]);
+
+ emc_bridge = &tegra_clk_emc_bridge;
+
+ /* Initialize to default */
+ tegra_init_cpu_edp_limits(0);
+
+ register_syscore_ops(&tegra_clk_syscore_ops);
+}
diff --git a/arch/arm/mach-tegra/tegra3_dvfs.c b/arch/arm/mach-tegra/tegra3_dvfs.c
new file mode 100644
index 000000000000..4ec22d1b9485
--- /dev/null
+++ b/arch/arm/mach-tegra/tegra3_dvfs.c
@@ -0,0 +1,864 @@
+/*
+ * arch/arm/mach-tegra/tegra3_dvfs.c
+ *
+ * Copyright (C) 2010-2011 NVIDIA Corporation.
+ *
+ * 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/init.h>
+#include <linux/string.h>
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/kobject.h>
+#include <linux/err.h>
+
+#include "clock.h"
+#include "dvfs.h"
+#include "fuse.h"
+#include "board.h"
+#include "tegra3_emc.h"
+
+static bool tegra_dvfs_cpu_disabled;
+static bool tegra_dvfs_core_disabled;
+
+static const int cpu_millivolts[MAX_DVFS_FREQS] =
+ {800, 825, 850, 875, 900, 925, 950, 975, 1000, 1025, 1050, 1075, 1100, 1125, 1150, 1200, 1237};
+
+static const int core_millivolts[MAX_DVFS_FREQS] =
+ {1000, 1050, 1100, 1150, 1200, 1250, 1300};
+
+#define KHZ 1000
+#define MHZ 1000000
+
+/* VDD_CPU >= (VDD_CORE - cpu_below_core) */
+/* VDD_CORE >= min_level(VDD_CPU), see tegra3_get_core_floor_mv() below */
+#define VDD_CPU_BELOW_VDD_CORE 300
+static int cpu_below_core = VDD_CPU_BELOW_VDD_CORE;
+
+#define VDD_SAFE_STEP 100
+
+static struct dvfs_rail tegra3_dvfs_rail_vdd_cpu = {
+ .reg_id = "vdd_cpu",
+ .max_millivolts = 1250,
+ .min_millivolts = 850,
+ .step = VDD_SAFE_STEP,
+ .jmp_to_zero = true,
+};
+
+static struct dvfs_rail tegra3_dvfs_rail_vdd_core = {
+ .reg_id = "vdd_core",
+ .max_millivolts = 1300,
+ .min_millivolts = 1000,
+ .step = VDD_SAFE_STEP,
+};
+
+static struct dvfs_rail *tegra3_dvfs_rails[] = {
+ &tegra3_dvfs_rail_vdd_cpu,
+ &tegra3_dvfs_rail_vdd_core,
+};
+
+static int tegra3_get_core_floor_mv(int cpu_mv)
+{
+ if (cpu_mv <= 825)
+ return 1000;
+ if (cpu_mv <= 975)
+ return 1100;
+ if ((tegra_cpu_speedo_id() < 2) ||
+ (tegra_cpu_speedo_id() == 4))
+ return 1200;
+ if (cpu_mv <= 1075)
+ return 1200;
+ if (cpu_mv <= 1250)
+ return 1300;
+ BUG();
+}
+
+/* vdd_core must be >= min_level as a function of vdd_cpu */
+static int tegra3_dvfs_rel_vdd_cpu_vdd_core(struct dvfs_rail *vdd_cpu,
+ struct dvfs_rail *vdd_core)
+{
+ int core_floor = max(vdd_cpu->new_millivolts, vdd_cpu->millivolts);
+ core_floor = tegra3_get_core_floor_mv(core_floor);
+ return max(vdd_core->new_millivolts, core_floor);
+}
+
+/* vdd_cpu must be >= (vdd_core - cpu_below_core) */
+static int tegra3_dvfs_rel_vdd_core_vdd_cpu(struct dvfs_rail *vdd_core,
+ struct dvfs_rail *vdd_cpu)
+{
+ int cpu_floor;
+
+ if (vdd_cpu->new_millivolts == 0)
+ return 0; /* If G CPU is off, core relations can be ignored */
+
+ cpu_floor = max(vdd_core->new_millivolts, vdd_core->millivolts) -
+ cpu_below_core;
+ return max(vdd_cpu->new_millivolts, cpu_floor);
+}
+
+static struct dvfs_relationship tegra3_dvfs_relationships[] = {
+ {
+ .from = &tegra3_dvfs_rail_vdd_cpu,
+ .to = &tegra3_dvfs_rail_vdd_core,
+ .solve = tegra3_dvfs_rel_vdd_cpu_vdd_core,
+ .solved_at_nominal = true,
+ },
+ {
+ .from = &tegra3_dvfs_rail_vdd_core,
+ .to = &tegra3_dvfs_rail_vdd_cpu,
+ .solve = tegra3_dvfs_rel_vdd_core_vdd_cpu,
+ },
+};
+
+#define CPU_DVFS(_clk_name, _speedo_id, _process_id, _mult, _freqs...) \
+ { \
+ .clk_name = _clk_name, \
+ .speedo_id = _speedo_id, \
+ .process_id = _process_id, \
+ .freqs = {_freqs}, \
+ .freqs_mult = _mult, \
+ .millivolts = cpu_millivolts, \
+ .auto_dvfs = true, \
+ .dvfs_rail = &tegra3_dvfs_rail_vdd_cpu, \
+ }
+
+static struct dvfs cpu_dvfs_table[] = {
+ /* Cpu voltages (mV): 800, 825, 850, 875, 900, 925, 950, 975, 1000, 1025, 1050, 1075, 1100, 1125, 1150, 1200, 1237 */
+ CPU_DVFS("cpu_g", 0, 0, MHZ, 1, 1, 684, 684, 817, 817, 817, 1026, 1102, 1149, 1187, 1225, 1282, 1300),
+ CPU_DVFS("cpu_g", 0, 1, MHZ, 1, 1, 807, 807, 948, 948, 948, 1117, 1171, 1206, 1300),
+ CPU_DVFS("cpu_g", 0, 2, MHZ, 1, 1, 883, 883, 1039, 1039, 1039, 1178, 1206, 1300),
+ CPU_DVFS("cpu_g", 0, 3, MHZ, 1, 1, 931, 931, 1102, 1102, 1102, 1216, 1300),
+
+ CPU_DVFS("cpu_g", 1, 0, MHZ, 1, 1, 550, 550, 680, 680, 680, 820, 970, 1040, 1080, 1150, 1200, 1280, 1300),
+ CPU_DVFS("cpu_g", 1, 1, MHZ, 1, 1, 650, 650, 820, 820, 820, 1000, 1060, 1100, 1200, 1300),
+ CPU_DVFS("cpu_g", 1, 2, MHZ, 1, 1, 720, 720, 880, 880, 880, 1090, 1180, 1200, 1300),
+ CPU_DVFS("cpu_g", 1, 3, MHZ, 1, 1, 800, 800, 1000, 1000, 1000, 1180, 1230, 1300),
+
+ CPU_DVFS("cpu_g", 2, 1, MHZ, 1, 1, 650, 650, 820, 820, 820, 1000, 1060, 1100, 1200, 1250, 1300, 1330, 1400),
+ CPU_DVFS("cpu_g", 2, 2, MHZ, 1, 1, 720, 720, 880, 880, 880, 1090, 1180, 1200, 1300, 1310, 1350, 1400),
+ CPU_DVFS("cpu_g", 2, 3, MHZ, 1, 1, 800, 800, 1000, 1000, 1000, 1180, 1230, 1300, 1320, 1350, 1400),
+
+ CPU_DVFS("cpu_g", 3, 1, MHZ, 1, 1, 650, 650, 820, 820, 820, 1000, 1060, 1100, 1200, 1250, 1300, 1330, 1400),
+ CPU_DVFS("cpu_g", 3, 2, MHZ, 1, 1, 720, 720, 880, 880, 880, 1090, 1180, 1200, 1300, 1310, 1350, 1400),
+ CPU_DVFS("cpu_g", 3, 3, MHZ, 1, 1, 800, 800, 1000, 1000, 1000, 1180, 1230, 1300, 1320, 1350, 1400),
+
+ CPU_DVFS("cpu_g", 4, 0, MHZ, 1, 1, 550, 550, 680, 680, 680, 820, 970, 1040, 1080, 1150, 1200, 1280, 1350, 1400, 1500),
+ CPU_DVFS("cpu_g", 4, 1, MHZ, 1, 1, 650, 650, 820, 820, 820, 1000, 1060, 1100, 1200, 1250, 1300, 1360, 1400, 1500),
+ CPU_DVFS("cpu_g", 4, 2, MHZ, 1, 1, 720, 720, 880, 880, 880, 1090, 1180, 1200, 1300, 1310, 1380, 1400, 1500),
+ CPU_DVFS("cpu_g", 4, 3, MHZ, 1, 1, 800, 800, 1000, 1000, 1000, 1180, 1230, 1300, 1330, 1380, 1400, 1500),
+
+ CPU_DVFS("cpu_g", 5, 3, MHZ, 1, 1, 800, 800, 1000, 1000, 1000, 1180, 1230, 1300, 1330, 1380, 1400, 1470, 1500, 1540, 1700),
+ CPU_DVFS("cpu_g", 5, 4, MHZ, 1, 1, 840, 840, 1000, 1000, 1000, 1200, 1280, 1330, 1380, 1400, 1480, 1500, 1520, 1700),
+
+ CPU_DVFS("cpu_g", 6, 3, MHZ, 1, 1, 800, 800, 1000, 1000, 1000, 1180, 1230, 1300, 1330, 1380, 1400, 1470, 1500, 1540, 1700),
+ CPU_DVFS("cpu_g", 6, 4, MHZ, 1, 1, 840, 840, 1000, 1000, 1000, 1200, 1280, 1330, 1380, 1400, 1480, 1500, 1520, 1700),
+
+ CPU_DVFS("cpu_g", 7, 0, MHZ, 1, 1, 550, 550, 680, 680, 680, 820, 970, 1040, 1080, 1150, 1200, 1280, 1300),
+ CPU_DVFS("cpu_g", 7, 1, MHZ, 1, 1, 650, 650, 820, 820, 820, 1000, 1060, 1100, 1200, 1300),
+ CPU_DVFS("cpu_g", 7, 2, MHZ, 1, 1, 720, 720, 880, 880, 880, 1090, 1180, 1200, 1300),
+ CPU_DVFS("cpu_g", 7, 3, MHZ, 1, 1, 800, 800, 1000, 1000, 1000, 1180, 1200, 1300),
+ CPU_DVFS("cpu_g", 7, 4, MHZ, 1, 1, 840, 840, 1000, 1000, 1000, 1200, 1300),
+
+ CPU_DVFS("cpu_g", 8, 0, MHZ, 1, 1, 550, 550, 680, 680, 680, 820, 970, 1040, 1080, 1150, 1200, 1280, 1300),
+ CPU_DVFS("cpu_g", 8, 1, MHZ, 1, 1, 650, 650, 820, 820, 820, 1000, 1060, 1100, 1200, 1300),
+ CPU_DVFS("cpu_g", 8, 2, MHZ, 1, 1, 720, 720, 880, 880, 880, 1090, 1180, 1200, 1300),
+ CPU_DVFS("cpu_g", 8, 3, MHZ, 1, 1, 800, 800, 1000, 1000, 1000, 1180, 1200, 1300),
+ CPU_DVFS("cpu_g", 8, 4, MHZ, 1, 1, 840, 840, 1000, 1000, 1000, 1200, 1300),
+
+ /*
+ * "Safe entry" to be used when no match for chip speedo, process
+ * corner is found (just to boot at low rate); must be the last one
+ */
+ CPU_DVFS("cpu_g", -1, -1, MHZ, 1, 1, 216, 216, 300),
+};
+
+#define CORE_DVFS(_clk_name, _speedo_id, _auto, _mult, _freqs...) \
+ { \
+ .clk_name = _clk_name, \
+ .speedo_id = _speedo_id, \
+ .process_id = -1, \
+ .freqs = {_freqs}, \
+ .freqs_mult = _mult, \
+ .millivolts = core_millivolts, \
+ .auto_dvfs = _auto, \
+ .dvfs_rail = &tegra3_dvfs_rail_vdd_core, \
+ }
+
+static struct dvfs core_dvfs_table[] = {
+ /* Core voltages (mV): 1000, 1050, 1100, 1150, 1200, 1250, 1300 */
+ /* Clock limits for internal blocks, PLLs */
+ CORE_DVFS("cpu_lp", 0, 1, KHZ, 294000, 342000, 427000, 475000, 500000, 500000, 500000),
+ CORE_DVFS("cpu_lp", 1, 1, KHZ, 294000, 342000, 427000, 475000, 500000, 500000, 500000),
+ CORE_DVFS("cpu_lp", 2, 1, KHZ, 295000, 370000, 428000, 475000, 513000, 579000, 620000),
+
+ CORE_DVFS("emc", 0, 1, KHZ, 266500, 266500, 266500, 266500, 533000, 533000, 533000),
+ CORE_DVFS("emc", 1, 1, KHZ, 408000, 408000, 408000, 408000, 667000, 667000, 667000),
+ CORE_DVFS("emc", 2, 1, KHZ, 408000, 408000, 408000, 408000, 667000, 667000, 800000),
+
+ CORE_DVFS("sbus", 0, 1, KHZ, 136000, 164000, 191000, 216000, 216000, 216000, 216000),
+ CORE_DVFS("sbus", 1, 1, KHZ, 205000, 205000, 227000, 227000, 267000, 267000, 267000),
+ CORE_DVFS("sbus", 2, 1, KHZ, 205000, 205000, 227000, 227000, 267000, 334000, 334000),
+
+ CORE_DVFS("vi", 0, 1, KHZ, 216000, 285000, 300000, 300000, 300000, 300000, 300000),
+ CORE_DVFS("vi", 1, 1, KHZ, 216000, 267000, 300000, 371000, 409000, 409000, 409000),
+ CORE_DVFS("vi", 2, 1, KHZ, 219000, 267000, 300000, 371000, 409000, 425000, 425000),
+
+ CORE_DVFS("vde", 0, 1, KHZ, 228000, 275000, 332000, 380000, 416000, 416000, 416000),
+ CORE_DVFS("mpe", 0, 1, KHZ, 234000, 285000, 332000, 380000, 416000, 416000, 416000),
+ CORE_DVFS("2d", 0, 1, KHZ, 267000, 285000, 332000, 380000, 416000, 416000, 416000),
+ CORE_DVFS("epp", 0, 1, KHZ, 267000, 285000, 332000, 380000, 416000, 416000, 416000),
+ CORE_DVFS("3d", 0, 1, KHZ, 234000, 285000, 332000, 380000, 416000, 416000, 416000),
+ CORE_DVFS("3d2", 0, 1, KHZ, 234000, 285000, 332000, 380000, 416000, 416000, 416000),
+ CORE_DVFS("se", 0, 1, KHZ, 267000, 285000, 332000, 380000, 416000, 416000, 416000),
+
+ CORE_DVFS("vde", 1, 1, KHZ, 228000, 275000, 332000, 380000, 416000, 416000, 416000),
+ CORE_DVFS("mpe", 1, 1, KHZ, 234000, 285000, 332000, 380000, 416000, 416000, 416000),
+ CORE_DVFS("2d", 1, 1, KHZ, 267000, 285000, 332000, 380000, 416000, 416000, 416000),
+ CORE_DVFS("epp", 1, 1, KHZ, 267000, 285000, 332000, 380000, 416000, 416000, 416000),
+ CORE_DVFS("3d", 1, 1, KHZ, 234000, 285000, 332000, 380000, 416000, 416000, 416000),
+ CORE_DVFS("3d2", 1, 1, KHZ, 234000, 285000, 332000, 380000, 416000, 416000, 416000),
+ CORE_DVFS("se", 1, 1, KHZ, 267000, 285000, 332000, 380000, 416000, 416000, 416000),
+
+ CORE_DVFS("vde", 2, 1, KHZ, 247000, 304000, 352000, 400000, 437000, 484000, 520000),
+ CORE_DVFS("mpe", 2, 1, KHZ, 247000, 304000, 361000, 408000, 446000, 484000, 520000),
+ CORE_DVFS("2d", 2, 1, KHZ, 267000, 304000, 361000, 408000, 446000, 484000, 520000),
+ CORE_DVFS("epp", 2, 1, KHZ, 267000, 304000, 361000, 408000, 446000, 484000, 520000),
+ CORE_DVFS("3d", 2, 1, KHZ, 247000, 304000, 361000, 408000, 446000, 484000, 520000),
+ CORE_DVFS("3d2", 2, 1, KHZ, 247000, 304000, 361000, 408000, 446000, 484000, 520000),
+ CORE_DVFS("se", 2, 1, KHZ, 267000, 304000, 361000, 408000, 446000, 484000, 520000),
+
+ CORE_DVFS("host1x",-1, 1, KHZ, 152000, 188000, 222000, 254000, 267000, 267000, 267000),
+
+ CORE_DVFS("cbus", 0, 1, KHZ, 228000, 275000, 332000, 380000, 416000, 416000, 416000),
+ CORE_DVFS("cbus", 1, 1, KHZ, 228000, 275000, 332000, 380000, 416000, 416000, 416000),
+ CORE_DVFS("cbus", 2, 1, KHZ, 247000, 304000, 352000, 400000, 437000, 484000, 520000),
+
+ CORE_DVFS("pll_c", -1, 1, KHZ, 667000, 667000, 800000, 800000, 1066000, 1066000, 1066000),
+ CORE_DVFS("pll_m", -1, 1, KHZ, 667000, 667000, 800000, 800000, 1066000, 1066000, 1066000),
+
+ /* Core voltages (mV): 1000, 1050, 1100, 1150, 1200, 1250, 1300 */
+ /* Clock limits for I/O peripherals */
+ CORE_DVFS("mipi", 0, 1, KHZ, 1, 1, 1, 1, 1, 1, 1),
+ CORE_DVFS("mipi", 1, 1, KHZ, 1, 1, 1, 1, 60000, 60000, 60000),
+ CORE_DVFS("mipi", 2, 1, KHZ, 1, 1, 1, 1, 60000, 60000, 60000),
+
+ CORE_DVFS("fuse_burn", -1, 1, KHZ, 1, 1, 1, 26000, 26000, 26000, 26000),
+ CORE_DVFS("sdmmc1",-1, 1, KHZ, 104000, 104000, 104000, 104000, 208000, 208000, 208000),
+ CORE_DVFS("sdmmc3",-1, 1, KHZ, 104000, 104000, 104000, 104000, 208000, 208000, 208000),
+ CORE_DVFS("ndflash", -1, 1, KHZ, 120000, 120000, 120000, 200000, 200000, 200000, 200000),
+ CORE_DVFS("nor", -1, 1, KHZ, 115000, 130000, 130000, 133000, 133000, 133000, 133000),
+ CORE_DVFS("sbc1", -1, 1, KHZ, 40000, 60000, 60000, 60000, 100000, 100000, 100000),
+ CORE_DVFS("sbc2", -1, 1, KHZ, 40000, 60000, 60000, 60000, 100000, 100000, 100000),
+ CORE_DVFS("sbc3", -1, 1, KHZ, 40000, 60000, 60000, 60000, 100000, 100000, 100000),
+ CORE_DVFS("sbc4", -1, 1, KHZ, 40000, 60000, 60000, 60000, 100000, 100000, 100000),
+ CORE_DVFS("sbc5", -1, 1, KHZ, 40000, 60000, 60000, 60000, 100000, 100000, 100000),
+ CORE_DVFS("sbc6", -1, 1, KHZ, 40000, 60000, 60000, 60000, 100000, 100000, 100000),
+ CORE_DVFS("tvo", -1, 1, KHZ, 1, 297000, 297000, 297000, 297000, 297000, 297000),
+ CORE_DVFS("cve", -1, 1, KHZ, 1, 297000, 297000, 297000, 297000, 297000, 297000),
+ CORE_DVFS("dsia", -1, 1, KHZ, 275000, 275000, 275000, 275000, 275000, 275000, 275000),
+ CORE_DVFS("dsib", -1, 1, KHZ, 275000, 275000, 275000, 275000, 275000, 275000, 275000),
+
+ /*
+ * The clock rate for the display controllers that determines the
+ * necessary core voltage depends on a divider that is internal
+ * to the display block. Disable auto-dvfs on the display clocks,
+ * and let the display driver call tegra_dvfs_set_rate manually
+ */
+ CORE_DVFS("disp1", 0, 0, KHZ, 120000, 120000, 120000, 120000, 190000, 190000, 190000),
+ CORE_DVFS("disp1", 1, 0, KHZ, 151000, 268000, 268000, 268000, 268000, 268000, 268000),
+ CORE_DVFS("disp1", 2, 0, KHZ, 151000, 268000, 268000, 268000, 268000, 268000, 268000),
+
+ CORE_DVFS("disp2", 0, 0, KHZ, 120000, 120000, 120000, 120000, 190000, 190000, 190000),
+ CORE_DVFS("disp2", 1, 0, KHZ, 151000, 268000, 268000, 268000, 268000, 268000, 268000),
+ CORE_DVFS("disp2", 2, 0, KHZ, 151000, 268000, 268000, 268000, 268000, 268000, 268000),
+};
+
+
+int tegra_dvfs_disable_core_set(const char *arg, const struct kernel_param *kp)
+{
+ int ret;
+
+ ret = param_set_bool(arg, kp);
+ if (ret)
+ return ret;
+
+ if (tegra_dvfs_core_disabled)
+ tegra_dvfs_rail_disable(&tegra3_dvfs_rail_vdd_core);
+ else
+ tegra_dvfs_rail_enable(&tegra3_dvfs_rail_vdd_core);
+
+ return 0;
+}
+
+int tegra_dvfs_disable_cpu_set(const char *arg, const struct kernel_param *kp)
+{
+ int ret;
+
+ ret = param_set_bool(arg, kp);
+ if (ret)
+ return ret;
+
+ if (tegra_dvfs_cpu_disabled)
+ tegra_dvfs_rail_disable(&tegra3_dvfs_rail_vdd_cpu);
+ else
+ tegra_dvfs_rail_enable(&tegra3_dvfs_rail_vdd_cpu);
+
+ return 0;
+}
+
+int tegra_dvfs_disable_get(char *buffer, const struct kernel_param *kp)
+{
+ return param_get_bool(buffer, kp);
+}
+
+static struct kernel_param_ops tegra_dvfs_disable_core_ops = {
+ .set = tegra_dvfs_disable_core_set,
+ .get = tegra_dvfs_disable_get,
+};
+
+static struct kernel_param_ops tegra_dvfs_disable_cpu_ops = {
+ .set = tegra_dvfs_disable_cpu_set,
+ .get = tegra_dvfs_disable_get,
+};
+
+module_param_cb(disable_core, &tegra_dvfs_disable_core_ops,
+ &tegra_dvfs_core_disabled, 0644);
+module_param_cb(disable_cpu, &tegra_dvfs_disable_cpu_ops,
+ &tegra_dvfs_cpu_disabled, 0644);
+
+static bool __init is_pllm_dvfs(struct clk *c, struct dvfs *d)
+{
+#ifdef CONFIG_TEGRA_PLLM_RESTRICTED
+ /* Restricting PLLM usage on T30 and T33, rev A02+, allows to apply
+ maximum PLLM frequency to clock tree at minimum core voltage;
+ no need to enable dvfs on PLLM in this case */
+ if ((tegra_cpu_speedo_id() == 2) || (tegra_cpu_speedo_id() == 5))
+ return false;
+#endif
+ /* Check if PLLM boot frequency can be applied to clock tree at
+ minimum voltage. If yes, no need to enable dvfs on PLLM */
+ if (clk_get_rate_all_locked(c) <= d->freqs[0] * d->freqs_mult)
+ return false;
+
+ return true;
+}
+
+static void __init init_dvfs_one(struct dvfs *d, int nominal_mv_index)
+{
+ int ret;
+ struct clk *c = tegra_get_clock_by_name(d->clk_name);
+
+ if (!c) {
+ pr_debug("tegra3_dvfs: no clock found for %s\n",
+ d->clk_name);
+ return;
+ }
+
+ /*
+ * Update max rate for auto-dvfs clocks, except EMC.
+ * EMC is a special case, since EMC dvfs is board dependent: max rate
+ * and EMC scaling frequencies are determined by tegra BCT (flashed
+ * together with the image) and board specific EMC DFS table; we will
+ * check the scaling ladder against nominal core voltage when the table
+ * is loaded (and if on particular board the table is not loaded, EMC
+ * scaling is disabled).
+ */
+ if (!(c->flags & PERIPH_EMC_ENB) && d->auto_dvfs) {
+ BUG_ON(!d->freqs[nominal_mv_index]);
+ tegra_init_max_rate(
+ c, d->freqs[nominal_mv_index] * d->freqs_mult);
+ }
+ d->max_millivolts = d->dvfs_rail->nominal_millivolts;
+
+ /*
+ * Check if we may skip enabling dvfs on PLLM. PLLM is a special case,
+ * since its frequency never exceeds boot rate, and configuration with
+ * restricted PLLM usage is possible.
+ */
+ if (!(c->flags & PLLM) || is_pllm_dvfs(c, d)) {
+ ret = tegra_enable_dvfs_on_clk(c, d);
+ if (ret)
+ pr_err("tegra3_dvfs: failed to enable dvfs on %s\n",
+ c->name);
+ }
+}
+
+static bool __init match_dvfs_one(struct dvfs *d, int speedo_id, int process_id)
+{
+ if ((d->process_id != -1 && d->process_id != process_id) ||
+ (d->speedo_id != -1 && d->speedo_id != speedo_id)) {
+ pr_debug("tegra3_dvfs: rejected %s speedo %d,"
+ " process %d\n", d->clk_name, d->speedo_id,
+ d->process_id);
+ return false;
+ }
+ return true;
+}
+
+static int __init get_cpu_nominal_mv_index(
+ int speedo_id, int process_id, struct dvfs **cpu_dvfs)
+{
+ int i, j, mv;
+ struct dvfs *d;
+ struct clk *c;
+
+ /*
+ * Find maximum cpu voltage that satisfies cpu_to_core dependency for
+ * nominal core voltage ("solve from cpu to core at nominal"). Clip
+ * result to the nominal cpu level for the chips with this speedo_id.
+ */
+ mv = tegra3_dvfs_rail_vdd_core.nominal_millivolts;
+ for (i = 0; i < MAX_DVFS_FREQS; i++) {
+ if ((cpu_millivolts[i] == 0) ||
+ tegra3_get_core_floor_mv(cpu_millivolts[i]) > mv)
+ break;
+ }
+ BUG_ON(i == 0);
+ mv = cpu_millivolts[i - 1];
+ BUG_ON(mv < tegra3_dvfs_rail_vdd_cpu.min_millivolts);
+ mv = min(mv, tegra_cpu_speedo_mv());
+
+ /*
+ * Find matching cpu dvfs entry, and use it to determine index to the
+ * final nominal voltage, that satisfies the following requirements:
+ * - allows CPU to run at minimum of the maximum rates specified in
+ * the dvfs entry and clock tree
+ * - does not violate cpu_to_core dependency as determined above
+ */
+ for (i = 0, j = 0; j < ARRAY_SIZE(cpu_dvfs_table); j++) {
+ d = &cpu_dvfs_table[j];
+ if (match_dvfs_one(d, speedo_id, process_id)) {
+ c = tegra_get_clock_by_name(d->clk_name);
+ BUG_ON(!c);
+
+ for (; i < MAX_DVFS_FREQS; i++) {
+ if ((d->freqs[i] == 0) ||
+ (cpu_millivolts[i] == 0) ||
+ (mv < cpu_millivolts[i]))
+ break;
+
+ if (c->max_rate <= d->freqs[i]*d->freqs_mult) {
+ i++;
+ break;
+ }
+ }
+ break;
+ }
+ }
+
+ BUG_ON(i == 0);
+ if (j == (ARRAY_SIZE(cpu_dvfs_table) - 1))
+ pr_err("tegra3_dvfs: WARNING!!!\n"
+ "tegra3_dvfs: no cpu dvfs table found for chip speedo_id"
+ " %d and process_id %d: set CPU rate limit at %lu\n"
+ "tegra3_dvfs: WARNING!!!\n",
+ speedo_id, process_id, d->freqs[i-1] * d->freqs_mult);
+
+ *cpu_dvfs = d;
+ return (i - 1);
+}
+
+static int __init get_core_nominal_mv_index(int speedo_id)
+{
+ int i;
+ int mv = tegra_core_speedo_mv();
+ int core_edp_limit = get_core_edp();
+
+ /*
+ * Start with nominal level for the chips with this speedo_id. Then,
+ * make sure core nominal voltage is below edp limit for the board
+ * (if edp limit is set).
+ */
+ if (core_edp_limit)
+ mv = min(mv, core_edp_limit);
+
+ /* Round nominal level down to the nearest core scaling step */
+ for (i = 0; i < MAX_DVFS_FREQS; i++) {
+ if ((core_millivolts[i] == 0) || (mv < core_millivolts[i]))
+ break;
+ }
+
+ if (i == 0) {
+ pr_err("tegra3_dvfs: unable to adjust core dvfs table to"
+ " nominal voltage %d\n", mv);
+ return -ENOSYS;
+ }
+ return (i - 1);
+}
+
+void __init tegra_soc_init_dvfs(void)
+{
+ int cpu_speedo_id = tegra_cpu_speedo_id();
+ int soc_speedo_id = tegra_soc_speedo_id();
+ int cpu_process_id = tegra_cpu_process_id();
+ int core_process_id = tegra_core_process_id();
+
+ int i;
+ int core_nominal_mv_index;
+ int cpu_nominal_mv_index;
+ struct dvfs *cpu_dvfs = NULL;
+
+#ifndef CONFIG_TEGRA_CORE_DVFS
+ tegra_dvfs_core_disabled = true;
+#endif
+#ifndef CONFIG_TEGRA_CPU_DVFS
+ tegra_dvfs_cpu_disabled = true;
+#endif
+
+ /*
+ * Find nominal voltages for core (1st) and cpu rails before rail
+ * init. Nominal voltage index in the scaling ladder will also be
+ * used to determine max dvfs frequency for the respective domains.
+ */
+ core_nominal_mv_index = get_core_nominal_mv_index(soc_speedo_id);
+ if (core_nominal_mv_index < 0) {
+ tegra3_dvfs_rail_vdd_core.disabled = true;
+ tegra_dvfs_core_disabled = true;
+ core_nominal_mv_index = 0;
+ }
+ tegra3_dvfs_rail_vdd_core.nominal_millivolts =
+ core_millivolts[core_nominal_mv_index];
+
+ cpu_nominal_mv_index = get_cpu_nominal_mv_index(
+ cpu_speedo_id, cpu_process_id, &cpu_dvfs);
+ BUG_ON((cpu_nominal_mv_index < 0) || (!cpu_dvfs));
+ tegra3_dvfs_rail_vdd_cpu.nominal_millivolts =
+ cpu_millivolts[cpu_nominal_mv_index];
+
+ /* Init rail structures and dependencies */
+ tegra_dvfs_init_rails(tegra3_dvfs_rails, ARRAY_SIZE(tegra3_dvfs_rails));
+ tegra_dvfs_add_relationships(tegra3_dvfs_relationships,
+ ARRAY_SIZE(tegra3_dvfs_relationships));
+
+ /* Search core dvfs table for speedo/process matching entries and
+ initialize dvfs-ed clocks */
+ for (i = 0; i < ARRAY_SIZE(core_dvfs_table); i++) {
+ struct dvfs *d = &core_dvfs_table[i];
+ if (!match_dvfs_one(d, soc_speedo_id, core_process_id))
+ continue;
+ init_dvfs_one(d, core_nominal_mv_index);
+ }
+
+ /* Initialize matching cpu dvfs entry already found when nominal
+ voltage was determined */
+ init_dvfs_one(cpu_dvfs, cpu_nominal_mv_index);
+
+ /* Finally disable dvfs on rails if necessary */
+ if (tegra_dvfs_core_disabled)
+ tegra_dvfs_rail_disable(&tegra3_dvfs_rail_vdd_core);
+ if (tegra_dvfs_cpu_disabled)
+ tegra_dvfs_rail_disable(&tegra3_dvfs_rail_vdd_cpu);
+
+ pr_info("tegra dvfs: VDD_CPU nominal %dmV, scaling %s\n",
+ tegra3_dvfs_rail_vdd_cpu.nominal_millivolts,
+ tegra_dvfs_cpu_disabled ? "disabled" : "enabled");
+ pr_info("tegra dvfs: VDD_CORE nominal %dmV, scaling %s\n",
+ tegra3_dvfs_rail_vdd_core.nominal_millivolts,
+ tegra_dvfs_core_disabled ? "disabled" : "enabled");
+}
+
+int tegra_dvfs_rail_disable_prepare(struct dvfs_rail *rail)
+{
+ int ret = 0;
+
+ if (tegra_emc_get_dram_type() != DRAM_TYPE_DDR3)
+ return ret;
+
+ if (((&tegra3_dvfs_rail_vdd_core == rail) &&
+ (rail->nominal_millivolts > TEGRA_EMC_BRIDGE_MVOLTS_MIN)) ||
+ ((&tegra3_dvfs_rail_vdd_cpu == rail) &&
+ (tegra3_get_core_floor_mv(rail->nominal_millivolts) >
+ TEGRA_EMC_BRIDGE_MVOLTS_MIN))) {
+ struct clk *bridge = tegra_get_clock_by_name("bridge.emc");
+ BUG_ON(!bridge);
+
+ ret = clk_enable(bridge);
+ pr_info("%s: %s: %s bridge.emc\n", __func__,
+ rail->reg_id, ret ? "failed to enable" : "enabled");
+ }
+ return ret;
+}
+
+int tegra_dvfs_rail_post_enable(struct dvfs_rail *rail)
+{
+ if (tegra_emc_get_dram_type() != DRAM_TYPE_DDR3)
+ return 0;
+
+ if (((&tegra3_dvfs_rail_vdd_core == rail) &&
+ (rail->nominal_millivolts > TEGRA_EMC_BRIDGE_MVOLTS_MIN)) ||
+ ((&tegra3_dvfs_rail_vdd_cpu == rail) &&
+ (tegra3_get_core_floor_mv(rail->nominal_millivolts) >
+ TEGRA_EMC_BRIDGE_MVOLTS_MIN))) {
+ struct clk *bridge = tegra_get_clock_by_name("bridge.emc");
+ BUG_ON(!bridge);
+
+ clk_disable(bridge);
+ pr_info("%s: %s: disabled bridge.emc\n",
+ __func__, rail->reg_id);
+ }
+ return 0;
+}
+
+/*
+ * sysfs and dvfs interfaces to cap tegra core domains frequencies
+ */
+static DEFINE_MUTEX(core_cap_lock);
+
+struct core_cap {
+ int refcnt;
+ int level;
+};
+static struct core_cap tegra3_core_cap;
+static struct core_cap kdvfs_core_cap;
+static struct core_cap user_core_cap;
+
+static struct kobject *cap_kobj;
+
+/* Arranged in order required for enabling/lowering the cap */
+static struct {
+ const char *cap_name;
+ struct clk *cap_clk;
+ unsigned long freqs[MAX_DVFS_FREQS];
+} core_cap_table[] = {
+ { .cap_name = "cap.cbus" },
+ { .cap_name = "cap.sclk" },
+ { .cap_name = "cap.emc" },
+};
+
+
+static void core_cap_level_set(int level)
+{
+ int i, j;
+
+ for (j = 0; j < ARRAY_SIZE(core_millivolts); j++) {
+ int v = core_millivolts[j];
+ if ((v == 0) || (level < v))
+ break;
+ }
+ j = (j == 0) ? 0 : j - 1;
+ level = core_millivolts[j];
+
+ if (level < tegra3_core_cap.level) {
+ for (i = 0; i < ARRAY_SIZE(core_cap_table); i++)
+ if (core_cap_table[i].cap_clk)
+ clk_set_rate(core_cap_table[i].cap_clk,
+ core_cap_table[i].freqs[j]);
+ } else if (level > tegra3_core_cap.level) {
+ for (i = ARRAY_SIZE(core_cap_table) - 1; i >= 0; i--)
+ if (core_cap_table[i].cap_clk)
+ clk_set_rate(core_cap_table[i].cap_clk,
+ core_cap_table[i].freqs[j]);
+ }
+ tegra3_core_cap.level = level;
+}
+
+static void core_cap_update(void)
+{
+ int new_level = tegra3_dvfs_rail_vdd_core.max_millivolts;
+
+ if (kdvfs_core_cap.refcnt)
+ new_level = min(new_level, kdvfs_core_cap.level);
+ if (user_core_cap.refcnt)
+ new_level = min(new_level, user_core_cap.level);
+
+ if (tegra3_core_cap.level != new_level)
+ core_cap_level_set(new_level);
+}
+
+static void core_cap_enable(bool enable)
+{
+ int i;
+
+ if (enable) {
+ tegra3_core_cap.refcnt++;
+ if (tegra3_core_cap.refcnt == 1)
+ for (i = 0; i < ARRAY_SIZE(core_cap_table); i++)
+ if (core_cap_table[i].cap_clk)
+ clk_enable(core_cap_table[i].cap_clk);
+ } else if (tegra3_core_cap.refcnt) {
+ tegra3_core_cap.refcnt--;
+ if (tegra3_core_cap.refcnt == 0)
+ for (i = ARRAY_SIZE(core_cap_table) - 1; i >= 0; i--)
+ if (core_cap_table[i].cap_clk)
+ clk_disable(core_cap_table[i].cap_clk);
+ }
+ core_cap_update();
+}
+
+static ssize_t
+core_cap_state_show(struct kobject *kobj, struct kobj_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "%d (%d)\n", tegra3_core_cap.refcnt ? 1 : 0,
+ user_core_cap.refcnt ? 1 : 0);
+}
+static ssize_t
+core_cap_state_store(struct kobject *kobj, struct kobj_attribute *attr,
+ const char *buf, size_t count)
+{
+ int state;
+
+ if (sscanf(buf, "%d", &state) != 1)
+ return -1;
+
+ mutex_lock(&core_cap_lock);
+
+ if (state) {
+ user_core_cap.refcnt++;
+ if (user_core_cap.refcnt == 1)
+ core_cap_enable(true);
+ } else if (user_core_cap.refcnt) {
+ user_core_cap.refcnt--;
+ if (user_core_cap.refcnt == 0)
+ core_cap_enable(false);
+ }
+
+ mutex_unlock(&core_cap_lock);
+ return count;
+}
+
+static ssize_t
+core_cap_level_show(struct kobject *kobj, struct kobj_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "%d (%d)\n", tegra3_core_cap.level,
+ user_core_cap.level);
+}
+static ssize_t
+core_cap_level_store(struct kobject *kobj, struct kobj_attribute *attr,
+ const char *buf, size_t count)
+{
+ int level;
+
+ if (sscanf(buf, "%d", &level) != 1)
+ return -1;
+
+ mutex_lock(&core_cap_lock);
+ user_core_cap.level = level;
+ core_cap_update();
+ mutex_unlock(&core_cap_lock);
+ return count;
+}
+
+static struct kobj_attribute cap_state_attribute =
+ __ATTR(core_cap_state, 0644, core_cap_state_show, core_cap_state_store);
+static struct kobj_attribute cap_level_attribute =
+ __ATTR(core_cap_level, 0644, core_cap_level_show, core_cap_level_store);
+
+const struct attribute *cap_attributes[] = {
+ &cap_state_attribute.attr,
+ &cap_level_attribute.attr,
+ NULL,
+};
+
+void tegra_dvfs_core_cap_enable(bool enable)
+{
+ mutex_lock(&core_cap_lock);
+
+ if (enable) {
+ kdvfs_core_cap.refcnt++;
+ if (kdvfs_core_cap.refcnt == 1)
+ core_cap_enable(true);
+ } else if (kdvfs_core_cap.refcnt) {
+ kdvfs_core_cap.refcnt--;
+ if (kdvfs_core_cap.refcnt == 0)
+ core_cap_enable(false);
+ }
+ mutex_unlock(&core_cap_lock);
+}
+
+void tegra_dvfs_core_cap_level_set(int level)
+{
+ mutex_lock(&core_cap_lock);
+ kdvfs_core_cap.level = level;
+ core_cap_update();
+ mutex_unlock(&core_cap_lock);
+}
+
+static int __init init_core_cap_one(struct clk *c, unsigned long *freqs)
+{
+ int i, v, next_v;
+ unsigned long rate, next_rate = 0;
+
+ for (i = 0; i < ARRAY_SIZE(core_millivolts); i++) {
+ v = core_millivolts[i];
+ if (v == 0)
+ break;
+
+ for (;;) {
+ rate = next_rate;
+ next_rate = clk_round_rate(c, rate + 1000);
+ if (IS_ERR_VALUE(next_rate)) {
+ pr_debug("tegra3_dvfs: failed to round %s"
+ " rate %lu", c->name, rate);
+ return -EINVAL;
+ }
+ if (rate == next_rate)
+ break;
+
+ next_v = tegra_dvfs_predict_millivolts(
+ c->parent, next_rate);
+ if (IS_ERR_VALUE(next_rate)) {
+ pr_debug("tegra3_dvfs: failed to predict %s mV"
+ " for rate %lu", c->name, next_rate);
+ return -EINVAL;
+ }
+ if (next_v > v)
+ break;
+ }
+
+ if (rate == 0) {
+ rate = next_rate;
+ pr_warn("tegra3_dvfs: minimum %s cap %lu requires"
+ " %d mV", c->name, rate, next_v);
+ }
+ freqs[i] = rate;
+ next_rate = rate;
+ }
+ return 0;
+}
+
+static int __init tegra_dvfs_init_core_cap(void)
+{
+ int i;
+ struct clk *c = NULL;
+
+ tegra3_core_cap.level = kdvfs_core_cap.level = user_core_cap.level =
+ tegra3_dvfs_rail_vdd_core.max_millivolts;
+
+ for (i = 0; i < ARRAY_SIZE(core_cap_table); i++) {
+ c = tegra_get_clock_by_name(core_cap_table[i].cap_name);
+ if (!c || !c->parent ||
+ init_core_cap_one(c, core_cap_table[i].freqs)) {
+ pr_err("tegra3_dvfs: failed to initialize %s frequency"
+ " table", core_cap_table[i].cap_name);
+ continue;
+ }
+ core_cap_table[i].cap_clk = c;
+ }
+
+ cap_kobj = kobject_create_and_add("tegra_cap", kernel_kobj);
+ if (!cap_kobj) {
+ pr_err("tegra3_dvfs: failed to create sysfs cap object");
+ return 0;
+ }
+
+ if (sysfs_create_files(cap_kobj, cap_attributes)) {
+ pr_err("tegra3_dvfs: failed to create sysfs cap interface");
+ return 0;
+ }
+ pr_info("tegra dvfs: tegra sysfs cap interface is initialized\n");
+
+ return 0;
+}
+late_initcall(tegra_dvfs_init_core_cap);
diff --git a/arch/arm/mach-tegra/tegra3_emc.c b/arch/arm/mach-tegra/tegra3_emc.c
new file mode 100644
index 000000000000..1a6dd8194328
--- /dev/null
+++ b/arch/arm/mach-tegra/tegra3_emc.c
@@ -0,0 +1,1069 @@
+/*
+ * arch/arm/mach-tegra/tegra3_emc.c
+ *
+ * Copyright (C) 2011 NVIDIA Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/suspend.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+
+#include <asm/cputime.h>
+
+#include <mach/iomap.h>
+
+#include "clock.h"
+#include "dvfs.h"
+#include "tegra3_emc.h"
+
+#ifdef CONFIG_TEGRA_EMC_SCALING_ENABLE
+static bool emc_enable = true;
+#else
+static bool emc_enable;
+#endif
+module_param(emc_enable, bool, 0644);
+
+#define EMC_MIN_RATE_DDR3 50000000
+#define EMC_STATUS_UPDATE_TIMEOUT 100
+#define TEGRA_EMC_TABLE_MAX_SIZE 16
+
+enum {
+ DLL_CHANGE_NONE = 0,
+ DLL_CHANGE_ON,
+ DLL_CHANGE_OFF,
+};
+
+#define EMC_CLK_DIV_SHIFT 0
+#define EMC_CLK_DIV_MASK (0xFF << EMC_CLK_DIV_SHIFT)
+#define EMC_CLK_SOURCE_SHIFT 30
+#define EMC_CLK_SOURCE_MASK (0x3 << EMC_CLK_SOURCE_SHIFT)
+#define EMC_CLK_LOW_JITTER_ENABLE (0x1 << 29)
+#define EMC_CLK_MC_SAME_FREQ (0x1 << 16)
+
+#define BURST_REG_LIST \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_RC), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_RFC), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_RAS), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_RP), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_R2W), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_W2R), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_R2P), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_W2P), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_RD_RCD), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_WR_RCD), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_RRD), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_REXT), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_WEXT), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_WDV), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_QUSE), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_QRST), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_QSAFE), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_RDV), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_REFRESH), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_BURST_REFRESH_NUM), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_PRE_REFRESH_REQ_CNT), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_PDEX2WR), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_PDEX2RD), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_PCHG2PDEN), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_ACT2PDEN), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_AR2PDEN), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_RW2PDEN), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_TXSR), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_TXSRDLL), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_TCKE), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_TFAW), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_TRPAB), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_TCLKSTABLE), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_TCLKSTOP), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_TREFBW), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_QUSE_EXTRA), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_FBIO_CFG6), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_ODT_WRITE), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_ODT_READ), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_FBIO_CFG5), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_CFG_DIG_DLL), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_CFG_DIG_DLL_PERIOD), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_DLL_XFORM_DQS0), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_DLL_XFORM_DQS1), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_DLL_XFORM_DQS2), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_DLL_XFORM_DQS3), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_DLL_XFORM_DQS4), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_DLL_XFORM_DQS5), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_DLL_XFORM_DQS6), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_DLL_XFORM_DQS7), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_DLL_XFORM_QUSE0), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_DLL_XFORM_QUSE1), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_DLL_XFORM_QUSE2), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_DLL_XFORM_QUSE3), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_DLL_XFORM_QUSE4), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_DLL_XFORM_QUSE5), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_DLL_XFORM_QUSE6), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_DLL_XFORM_QUSE7), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_DLI_TRIM_TXDQS0), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_DLI_TRIM_TXDQS1), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_DLI_TRIM_TXDQS2), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_DLI_TRIM_TXDQS3), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_DLI_TRIM_TXDQS4), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_DLI_TRIM_TXDQS5), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_DLI_TRIM_TXDQS6), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_DLI_TRIM_TXDQS7), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_DLL_XFORM_DQ0), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_DLL_XFORM_DQ1), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_DLL_XFORM_DQ2), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_DLL_XFORM_DQ3), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_XM2CMDPADCTRL), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_XM2DQSPADCTRL2), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_XM2DQPADCTRL2), \
+ DEFINE_REG(0 , EMC_XM2CLKPADCTRL), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_XM2COMPPADCTRL), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_XM2VTTGENPADCTRL), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_XM2VTTGENPADCTRL2), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_XM2QUSEPADCTRL), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_XM2DQSPADCTRL3), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_CTT_TERM_CTRL), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_ZCAL_INTERVAL), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_ZCAL_WAIT_CNT), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_MRS_WAIT_CNT), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_AUTO_CAL_CONFIG), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_CTT), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_CTT_DURATION), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_DYN_SELF_REF_CONTROL), \
+ \
+ DEFINE_REG(TEGRA_MC_BASE, MC_EMEM_ARB_CFG), \
+ DEFINE_REG(TEGRA_MC_BASE, MC_EMEM_ARB_OUTSTANDING_REQ), \
+ DEFINE_REG(TEGRA_MC_BASE, MC_EMEM_ARB_TIMING_RCD), \
+ DEFINE_REG(TEGRA_MC_BASE, MC_EMEM_ARB_TIMING_RP), \
+ DEFINE_REG(TEGRA_MC_BASE, MC_EMEM_ARB_TIMING_RC), \
+ DEFINE_REG(TEGRA_MC_BASE, MC_EMEM_ARB_TIMING_RAS), \
+ DEFINE_REG(TEGRA_MC_BASE, MC_EMEM_ARB_TIMING_FAW), \
+ DEFINE_REG(TEGRA_MC_BASE, MC_EMEM_ARB_TIMING_RRD), \
+ DEFINE_REG(TEGRA_MC_BASE, MC_EMEM_ARB_TIMING_RAP2PRE), \
+ DEFINE_REG(TEGRA_MC_BASE, MC_EMEM_ARB_TIMING_WAP2PRE), \
+ DEFINE_REG(TEGRA_MC_BASE, MC_EMEM_ARB_TIMING_R2R), \
+ DEFINE_REG(TEGRA_MC_BASE, MC_EMEM_ARB_TIMING_W2W), \
+ DEFINE_REG(TEGRA_MC_BASE, MC_EMEM_ARB_TIMING_R2W), \
+ DEFINE_REG(TEGRA_MC_BASE, MC_EMEM_ARB_TIMING_W2R), \
+ DEFINE_REG(TEGRA_MC_BASE, MC_EMEM_ARB_DA_TURNS), \
+ DEFINE_REG(TEGRA_MC_BASE, MC_EMEM_ARB_DA_COVERS), \
+ DEFINE_REG(TEGRA_MC_BASE, MC_EMEM_ARB_MISC0), \
+ DEFINE_REG(TEGRA_MC_BASE, MC_EMEM_ARB_RING1_THROTTLE), \
+ \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_FBIO_SPARE), \
+ DEFINE_REG(TEGRA_EMC_BASE, EMC_CFG_RSV),
+
+#define DEFINE_REG(base, reg) ((base) ? ((u32)IO_ADDRESS((base)) + (reg)) : 0)
+static const u32 burst_reg_addr[TEGRA_EMC_NUM_REGS] = {
+ BURST_REG_LIST
+};
+#undef DEFINE_REG
+
+#define DEFINE_REG(base, reg) reg##_INDEX
+enum {
+ BURST_REG_LIST
+};
+#undef DEFINE_REG
+
+static int emc_num_burst_regs;
+
+static struct clk_mux_sel tegra_emc_clk_sel[TEGRA_EMC_TABLE_MAX_SIZE];
+static int emc_last_sel;
+static struct tegra_emc_table start_timing;
+static bool emc_timing_in_sync;
+
+static const struct tegra_emc_table *tegra_emc_table;
+static int tegra_emc_table_size;
+
+static u32 dram_dev_num;
+static u32 emc_cfg_saved;
+static u32 dram_type = -1;
+
+static struct clk *emc;
+static struct clk *bridge;
+
+static struct {
+ cputime64_t time_at_clock[TEGRA_EMC_TABLE_MAX_SIZE];
+ u64 last_update;
+ u64 clkchange_count;
+ spinlock_t spinlock;
+} emc_stats;
+
+static void __iomem *emc_base = IO_ADDRESS(TEGRA_EMC_BASE);
+static void __iomem *mc_base = IO_ADDRESS(TEGRA_MC_BASE);
+static void __iomem *clk_base = IO_ADDRESS(TEGRA_CLK_RESET_BASE);
+
+static inline void emc_writel(u32 val, unsigned long addr)
+{
+ writel(val, (u32)emc_base + addr);
+ barrier();
+}
+static inline u32 emc_readl(unsigned long addr)
+{
+ return readl((u32)emc_base + addr);
+}
+static inline void mc_writel(u32 val, unsigned long addr)
+{
+ writel(val, (u32)mc_base + addr);
+ barrier();
+}
+static inline u32 mc_readl(unsigned long addr)
+{
+ return readl((u32)mc_base + addr);
+}
+
+static void emc_last_stats_update(int last_sel)
+{
+ unsigned long flags;
+ u64 cur_jiffies = get_jiffies_64();
+
+ spin_lock_irqsave(&emc_stats.spinlock, flags);
+
+ emc_stats.time_at_clock[emc_last_sel] = cputime64_add(
+ emc_stats.time_at_clock[emc_last_sel], cputime64_sub(
+ cur_jiffies, emc_stats.last_update));
+
+ emc_stats.last_update = cur_jiffies;
+
+ if (last_sel < TEGRA_EMC_TABLE_MAX_SIZE) {
+ emc_stats.clkchange_count++;
+ emc_last_sel = last_sel;
+ }
+ spin_unlock_irqrestore(&emc_stats.spinlock, flags);
+}
+
+static int wait_for_update(u32 status_reg, u32 bit_mask, bool updated_state)
+{
+ int i;
+ for (i = 0; i < EMC_STATUS_UPDATE_TIMEOUT; i++) {
+ if (!!(emc_readl(status_reg) & bit_mask) == updated_state)
+ return 0;
+ udelay(1);
+ }
+ return -ETIMEDOUT;
+}
+
+static inline void emc_timing_update(void)
+{
+ int err;
+
+ emc_writel(0x1, EMC_TIMING_CONTROL);
+ err = wait_for_update(EMC_STATUS,
+ EMC_STATUS_TIMING_UPDATE_STALLED, false);
+ if (err) {
+ pr_err("%s: timing update error: %d", __func__, err);
+ BUG();
+ }
+}
+
+static inline void auto_cal_disable(void)
+{
+ int err;
+
+ emc_writel(0, EMC_AUTO_CAL_INTERVAL);
+ err = wait_for_update(EMC_AUTO_CAL_STATUS,
+ EMC_AUTO_CAL_STATUS_ACTIVE, false);
+ if (err) {
+ pr_err("%s: disable auto-cal error: %d", __func__, err);
+ BUG();
+ }
+}
+
+static inline void set_mc_arbiter_limits(void)
+{
+ u32 reg = mc_readl(MC_EMEM_ARB_OUTSTANDING_REQ);
+ u32 max_val = 0x50 << EMC_MRS_WAIT_CNT_SHORT_WAIT_SHIFT;
+
+ if (!(reg & MC_EMEM_ARB_OUTSTANDING_REQ_HOLDOFF_OVERRIDE) ||
+ ((reg & MC_EMEM_ARB_OUTSTANDING_REQ_MAX_MASK) > max_val)) {
+ reg = MC_EMEM_ARB_OUTSTANDING_REQ_LIMIT_ENABLE |
+ MC_EMEM_ARB_OUTSTANDING_REQ_HOLDOFF_OVERRIDE | max_val;
+ mc_writel(reg, MC_EMEM_ARB_OUTSTANDING_REQ);
+ mc_writel(0x1, MC_TIMING_CONTROL);
+ }
+}
+
+static inline bool dqs_preset(const struct tegra_emc_table *next_timing,
+ const struct tegra_emc_table *last_timing)
+{
+ bool ret = false;
+
+#define DQS_SET(reg, bit) \
+ do { \
+ if ((next_timing->burst_regs[EMC_##reg##_INDEX] & \
+ EMC_##reg##_##bit##_ENABLE) && \
+ (!(last_timing->burst_regs[EMC_##reg##_INDEX] & \
+ EMC_##reg##_##bit##_ENABLE))) { \
+ emc_writel(last_timing->burst_regs[EMC_##reg##_INDEX] \
+ | EMC_##reg##_##bit##_ENABLE, EMC_##reg); \
+ ret = true; \
+ } \
+ } while (0)
+
+ DQS_SET(XM2DQSPADCTRL2, VREF);
+ DQS_SET(XM2DQSPADCTRL3, VREF);
+ DQS_SET(XM2QUSEPADCTRL, IVREF);
+
+ return ret;
+}
+
+static inline void overwrite_mrs_wait_cnt(
+ const struct tegra_emc_table *next_timing,
+ bool zcal_long)
+{
+ u32 reg;
+ u32 cnt = 512;
+
+ /* For ddr3 when DLL is re-started: overwrite EMC DFS table settings
+ for MRS_WAIT_LONG with maximum of MRS_WAIT_SHORT settings and
+ expected operation length. Reduce the latter by the overlapping
+ zq-calibration, if any */
+ if (zcal_long)
+ cnt -= dram_dev_num * 256;
+
+ reg = (next_timing->burst_regs[EMC_MRS_WAIT_CNT_INDEX] &
+ EMC_MRS_WAIT_CNT_SHORT_WAIT_MASK) >>
+ EMC_MRS_WAIT_CNT_SHORT_WAIT_SHIFT;
+ if (cnt < reg)
+ cnt = reg;
+
+ reg = (next_timing->burst_regs[EMC_MRS_WAIT_CNT_INDEX] &
+ (~EMC_MRS_WAIT_CNT_LONG_WAIT_MASK));
+ reg |= (cnt << EMC_MRS_WAIT_CNT_LONG_WAIT_SHIFT) &
+ EMC_MRS_WAIT_CNT_LONG_WAIT_MASK;
+
+ emc_writel(reg, EMC_MRS_WAIT_CNT);
+}
+
+static inline bool need_qrst(const struct tegra_emc_table *next_timing,
+ const struct tegra_emc_table *last_timing,
+ u32 emc_dpd_reg)
+{
+ u32 last_mode = (last_timing->burst_regs[EMC_FBIO_CFG5_INDEX] &
+ EMC_CFG5_QUSE_MODE_MASK) >> EMC_CFG5_QUSE_MODE_SHIFT;
+ u32 next_mode = (next_timing->burst_regs[EMC_FBIO_CFG5_INDEX] &
+ EMC_CFG5_QUSE_MODE_MASK) >> EMC_CFG5_QUSE_MODE_SHIFT;
+
+ /* QUSE DPD is disabled */
+ bool ret = !(emc_dpd_reg & EMC_SEL_DPD_CTRL_QUSE_DPD_ENABLE) &&
+
+ /* QUSE uses external mode before or after clock change */
+ (((last_mode != EMC_CFG5_QUSE_MODE_PULSE_INTERN) &&
+ (last_mode != EMC_CFG5_QUSE_MODE_INTERNAL_LPBK)) ||
+ ((next_mode != EMC_CFG5_QUSE_MODE_PULSE_INTERN) &&
+ (next_mode != EMC_CFG5_QUSE_MODE_INTERNAL_LPBK))) &&
+
+ /* QUSE pad switches from schmitt to vref mode */
+ (((last_timing->burst_regs[EMC_XM2QUSEPADCTRL_INDEX] &
+ EMC_XM2QUSEPADCTRL_IVREF_ENABLE) == 0) &&
+ ((next_timing->burst_regs[EMC_XM2QUSEPADCTRL_INDEX] &
+ EMC_XM2QUSEPADCTRL_IVREF_ENABLE) != 0));
+
+ return ret;
+}
+
+static inline void periodic_qrst_enable(u32 emc_cfg_reg, u32 emc_dbg_reg)
+{
+ /* enable write mux => enable periodic QRST => restore mux */
+ emc_writel(emc_dbg_reg | EMC_DBG_WRITE_MUX_ACTIVE, EMC_DBG);
+ emc_writel(emc_cfg_reg | EMC_CFG_PERIODIC_QRST, EMC_CFG);
+ emc_writel(emc_dbg_reg, EMC_DBG);
+}
+
+static inline int get_dll_change(const struct tegra_emc_table *next_timing,
+ const struct tegra_emc_table *last_timing)
+{
+ bool next_dll_enabled = !(next_timing->emc_mode_1 & 0x1);
+ bool last_dll_enabled = !(last_timing->emc_mode_1 & 0x1);
+
+ if (next_dll_enabled == last_dll_enabled)
+ return DLL_CHANGE_NONE;
+ else if (next_dll_enabled)
+ return DLL_CHANGE_ON;
+ else
+ return DLL_CHANGE_OFF;
+}
+
+static inline void set_dram_mode(const struct tegra_emc_table *next_timing,
+ const struct tegra_emc_table *last_timing,
+ int dll_change)
+{
+ if (dram_type == DRAM_TYPE_DDR3) {
+ /* first mode_1, then mode_2, then mode_reset*/
+ if (next_timing->emc_mode_1 != last_timing->emc_mode_1)
+ emc_writel(next_timing->emc_mode_1, EMC_EMRS);
+ if (next_timing->emc_mode_2 != last_timing->emc_mode_2)
+ emc_writel(next_timing->emc_mode_2, EMC_EMRS);
+
+ if ((next_timing->emc_mode_reset !=
+ last_timing->emc_mode_reset) ||
+ (dll_change == DLL_CHANGE_ON))
+ {
+ u32 reg = next_timing->emc_mode_reset &
+ (~EMC_MODE_SET_DLL_RESET);
+ if (dll_change == DLL_CHANGE_ON) {
+ reg |= EMC_MODE_SET_DLL_RESET;
+ reg |= EMC_MODE_SET_LONG_CNT;
+ }
+ emc_writel(reg, EMC_MRS);
+ }
+ } else {
+ /* first mode_2, then mode_1; mode_reset is not applicable */
+ if (next_timing->emc_mode_2 != last_timing->emc_mode_2)
+ emc_writel(next_timing->emc_mode_2, EMC_MRW);
+ if (next_timing->emc_mode_1 != last_timing->emc_mode_1)
+ emc_writel(next_timing->emc_mode_1, EMC_MRW);
+ }
+}
+
+static inline void do_clock_change(u32 clk_setting)
+{
+ int err;
+
+ mc_readl(MC_EMEM_ADR_CFG); /* completes prev writes */
+ writel(clk_setting, (u32)clk_base + emc->reg);
+
+ err = wait_for_update(EMC_INTSTATUS,
+ EMC_INTSTATUS_CLKCHANGE_COMPLETE, true);
+ if (err) {
+ pr_err("%s: clock change completion error: %d", __func__, err);
+ BUG();
+ }
+}
+
+static noinline void emc_set_clock(const struct tegra_emc_table *next_timing,
+ const struct tegra_emc_table *last_timing,
+ u32 clk_setting)
+{
+ int i, dll_change, pre_wait;
+ bool dyn_sref_enabled, vref_cal_toggle, qrst_used, zcal_long;
+
+ u32 emc_cfg_reg = emc_readl(EMC_CFG);
+ u32 emc_dbg_reg = emc_readl(EMC_DBG);
+
+ dyn_sref_enabled = emc_cfg_reg & EMC_CFG_DYN_SREF_ENABLE;
+ dll_change = get_dll_change(next_timing, last_timing);
+ zcal_long = (next_timing->burst_regs[EMC_ZCAL_INTERVAL_INDEX] != 0) &&
+ (last_timing->burst_regs[EMC_ZCAL_INTERVAL_INDEX] == 0);
+
+ /* FIXME: remove steps enumeration below? */
+
+ /* 1. clear clkchange_complete interrupts */
+ emc_writel(EMC_INTSTATUS_CLKCHANGE_COMPLETE, EMC_INTSTATUS);
+
+ /* 2. disable dynamic self-refresh and preset dqs vref, then wait for
+ possible self-refresh entry/exit and/or dqs vref settled - waiting
+ before the clock change decreases worst case change stall time */
+ pre_wait = 0;
+ if (dyn_sref_enabled) {
+ emc_cfg_reg &= ~EMC_CFG_DYN_SREF_ENABLE;
+ emc_writel(emc_cfg_reg, EMC_CFG);
+ pre_wait = 5; /* 5us+ for self-refresh entry/exit */
+ }
+
+ /* 2.25 update MC arbiter settings */
+ set_mc_arbiter_limits();
+
+ /* 2.5 check dq/dqs vref delay */
+ if (dqs_preset(next_timing, last_timing)) {
+ if (pre_wait < 3)
+ pre_wait = 3; /* 3us+ for dqs vref settled */
+ }
+ if (pre_wait) {
+ emc_timing_update();
+ udelay(pre_wait);
+ }
+
+ /* 3. disable auto-cal if vref mode is switching */
+ vref_cal_toggle = (next_timing->emc_acal_interval != 0) &&
+ ((next_timing->burst_regs[EMC_XM2COMPPADCTRL_INDEX] ^
+ last_timing->burst_regs[EMC_XM2COMPPADCTRL_INDEX]) &
+ EMC_XM2COMPPADCTRL_VREF_CAL_ENABLE);
+ if (vref_cal_toggle)
+ auto_cal_disable();
+
+ /* 4. program burst shadow registers */
+ for (i = 0; i < emc_num_burst_regs; i++) {
+ if (!burst_reg_addr[i])
+ continue;
+ __raw_writel(next_timing->burst_regs[i], burst_reg_addr[i]);
+ }
+ wmb();
+ barrier();
+
+ /* On ddr3 when DLL is re-started predict MRS long wait count and
+ overwrite DFS table setting */
+ if ((dram_type == DRAM_TYPE_DDR3) && (dll_change == DLL_CHANGE_ON))
+ overwrite_mrs_wait_cnt(next_timing, zcal_long);
+
+ /* the last read below makes sure prev writes are completed */
+ qrst_used = need_qrst(next_timing, last_timing,
+ emc_readl(EMC_SEL_DPD_CTRL));
+
+ /* 5. flow control marker 1 (no EMC read access after this) */
+ emc_writel(1, EMC_STALL_BEFORE_CLKCHANGE);
+
+ /* 6. enable periodic QRST */
+ if (qrst_used)
+ periodic_qrst_enable(emc_cfg_reg, emc_dbg_reg);
+
+ /* 6.1 disable auto-refresh to save time after clock change */
+ emc_writel(EMC_REFCTRL_DISABLE_ALL(dram_dev_num), EMC_REFCTRL);
+
+ /* 7. turn Off dll and enter self-refresh on DDR3 */
+ if (dram_type == DRAM_TYPE_DDR3) {
+ if (dll_change == DLL_CHANGE_OFF)
+ emc_writel(next_timing->emc_mode_1, EMC_EMRS);
+ emc_writel(DRAM_BROADCAST(dram_dev_num) |
+ EMC_SELF_REF_CMD_ENABLED, EMC_SELF_REF);
+ }
+
+ /* 8. flow control marker 2 */
+ emc_writel(1, EMC_STALL_AFTER_CLKCHANGE);
+
+ /* 8.1 enable write mux, update unshadowed pad control */
+ emc_writel(emc_dbg_reg | EMC_DBG_WRITE_MUX_ACTIVE, EMC_DBG);
+ emc_writel(next_timing->burst_regs[EMC_XM2CLKPADCTRL_INDEX],
+ EMC_XM2CLKPADCTRL);
+
+ /* 9. restore periodic QRST, and disable write mux */
+ if ((qrst_used) || (next_timing->emc_periodic_qrst !=
+ last_timing->emc_periodic_qrst)) {
+ emc_cfg_reg = next_timing->emc_periodic_qrst ?
+ emc_cfg_reg | EMC_CFG_PERIODIC_QRST :
+ emc_cfg_reg & (~EMC_CFG_PERIODIC_QRST);
+ emc_writel(emc_cfg_reg, EMC_CFG);
+ }
+ emc_writel(emc_dbg_reg, EMC_DBG);
+
+ /* 10. exit self-refresh on DDR3 */
+ if (dram_type == DRAM_TYPE_DDR3)
+ emc_writel(DRAM_BROADCAST(dram_dev_num), EMC_SELF_REF);
+
+ /* 11. set dram mode registers */
+ set_dram_mode(next_timing, last_timing, dll_change);
+
+ /* 12. issue zcal command if turning zcal On */
+ if (zcal_long) {
+ emc_writel(EMC_ZQ_CAL_LONG_CMD_DEV0, EMC_ZQ_CAL);
+ if (dram_dev_num > 1)
+ emc_writel(EMC_ZQ_CAL_LONG_CMD_DEV1, EMC_ZQ_CAL);
+ }
+
+ /* 13. flow control marker 3 */
+ emc_writel(1, EMC_UNSTALL_RW_AFTER_CLKCHANGE);
+
+ /* 14. read any MC register to ensure the programming is done
+ change EMC clock source register (EMC read access restored)
+ wait for clk change completion */
+ do_clock_change(clk_setting);
+
+ /* 14.1 re-enable auto-refresh */
+ emc_writel(EMC_REFCTRL_ENABLE_ALL(dram_dev_num), EMC_REFCTRL);
+
+ /* 15. restore auto-cal */
+ if (vref_cal_toggle)
+ emc_writel(next_timing->emc_acal_interval,
+ EMC_AUTO_CAL_INTERVAL);
+
+ /* 16. restore dynamic self-refresh */
+ if (next_timing->rev >= 0x32)
+ dyn_sref_enabled = next_timing->emc_dsr;
+ if (dyn_sref_enabled) {
+ emc_cfg_reg |= EMC_CFG_DYN_SREF_ENABLE;
+ emc_writel(emc_cfg_reg, EMC_CFG);
+ }
+
+ /* 17. set zcal wait count */
+ if (zcal_long)
+ emc_writel(next_timing->emc_zcal_cnt_long, EMC_ZCAL_WAIT_CNT);
+
+ /* 18. update restored timing */
+ udelay(2);
+ emc_timing_update();
+}
+
+static inline void emc_get_timing(struct tegra_emc_table *timing)
+{
+ int i;
+
+ for (i = 0; i < emc_num_burst_regs; i++) {
+ if (burst_reg_addr[i])
+ timing->burst_regs[i] = __raw_readl(burst_reg_addr[i]);
+ else
+ timing->burst_regs[i] = 0;
+ }
+ timing->emc_acal_interval = 0;
+ timing->emc_zcal_cnt_long = 0;
+ timing->emc_mode_reset = 0;
+ timing->emc_mode_1 = 0;
+ timing->emc_mode_2 = 0;
+ timing->emc_periodic_qrst = (emc_readl(EMC_CFG) &
+ EMC_CFG_PERIODIC_QRST) ? 1 : 0;
+}
+
+/* After deep sleep EMC power features are not restored.
+ * Do it at run-time after the 1st clock change.
+ */
+static inline void emc_cfg_power_restore(void)
+{
+ u32 reg = emc_readl(EMC_CFG);
+ u32 pwr_mask = EMC_CFG_PWR_MASK;
+
+ if (tegra_emc_table[0].rev >= 0x32)
+ pwr_mask &= ~EMC_CFG_DYN_SREF_ENABLE;
+
+ if ((reg ^ emc_cfg_saved) & pwr_mask) {
+ reg = (reg & (~pwr_mask)) | (emc_cfg_saved & pwr_mask);
+ emc_writel(reg, EMC_CFG);
+ emc_timing_update();
+ }
+}
+
+/* The EMC registers have shadow registers. When the EMC clock is updated
+ * in the clock controller, the shadow registers are copied to the active
+ * registers, allowing glitchless memory bus frequency changes.
+ * This function updates the shadow registers for a new clock frequency,
+ * and relies on the clock lock on the emc clock to avoid races between
+ * multiple frequency changes */
+int tegra_emc_set_rate(unsigned long rate)
+{
+ int i;
+ u32 clk_setting;
+ const struct tegra_emc_table *last_timing;
+
+ if (!tegra_emc_table)
+ return -EINVAL;
+
+ /* Table entries specify rate in kHz */
+ rate = rate / 1000;
+
+ for (i = 0; i < tegra_emc_table_size; i++) {
+ if (tegra_emc_clk_sel[i].input == NULL)
+ continue; /* invalid entry */
+
+ if (tegra_emc_table[i].rate == rate)
+ break;
+ }
+
+ if (i >= tegra_emc_table_size)
+ return -EINVAL;
+
+ if (!emc_timing_in_sync) {
+ /* can not assume that boot timing matches dfs table even
+ if boot frequency matches one of the table nodes */
+ emc_get_timing(&start_timing);
+ last_timing = &start_timing;
+ }
+ else
+ last_timing = &tegra_emc_table[emc_last_sel];
+
+ clk_setting = tegra_emc_clk_sel[i].value;
+ emc_set_clock(&tegra_emc_table[i], last_timing, clk_setting);
+ if (!emc_timing_in_sync)
+ emc_cfg_power_restore();
+ emc_timing_in_sync = true;
+ emc_last_stats_update(i);
+
+ pr_debug("%s: rate %lu setting 0x%x\n", __func__, rate, clk_setting);
+
+ return 0;
+}
+
+/* Select the closest EMC rate that is higher than the requested rate */
+long tegra_emc_round_rate(unsigned long rate)
+{
+ int i;
+ int best = -1;
+ unsigned long distance = ULONG_MAX;
+
+ if (!tegra_emc_table)
+ return clk_get_rate_locked(emc); /* no table - no rate change */
+
+ if (!emc_enable)
+ return -EINVAL;
+
+ pr_debug("%s: %lu\n", __func__, rate);
+
+ /* Table entries specify rate in kHz */
+ rate = rate / 1000;
+
+ for (i = 0; i < tegra_emc_table_size; i++) {
+ if (tegra_emc_clk_sel[i].input == NULL)
+ continue; /* invalid entry */
+
+ if (tegra_emc_table[i].rate >= rate &&
+ (tegra_emc_table[i].rate - rate) < distance) {
+ distance = tegra_emc_table[i].rate - rate;
+ best = i;
+ }
+ }
+
+ if (best < 0)
+ return -EINVAL;
+
+ pr_debug("%s: using %lu\n", __func__, tegra_emc_table[best].rate);
+
+ return tegra_emc_table[best].rate * 1000;
+}
+
+struct clk *tegra_emc_predict_parent(unsigned long rate, u32 *div_value)
+{
+ int i;
+
+ if (!tegra_emc_table)
+ return NULL;
+
+ pr_debug("%s: %lu\n", __func__, rate);
+
+ /* Table entries specify rate in kHz */
+ rate = rate / 1000;
+
+ for (i = 0; i < tegra_emc_table_size; i++) {
+ if (tegra_emc_table[i].rate == rate) {
+ *div_value = (tegra_emc_clk_sel[i].value &
+ EMC_CLK_DIV_MASK) >> EMC_CLK_DIV_SHIFT;
+ return tegra_emc_clk_sel[i].input;
+ }
+ }
+
+ return NULL;
+}
+
+static const struct clk_mux_sel *find_matching_input(
+ unsigned long table_rate,
+ u32 *div_value)
+{
+ unsigned long inp_rate;
+ const struct clk_mux_sel *sel;
+
+ for (sel = emc->inputs; sel->input != NULL; sel++) {
+ /* Table entries specify rate in kHz */
+ inp_rate = clk_get_rate(sel->input) / 1000;
+
+ if ((inp_rate >= table_rate) &&
+ (inp_rate % table_rate == 0)) {
+ *div_value = 2 * inp_rate / table_rate - 2;
+ return sel;
+ }
+ }
+ return NULL;
+}
+
+static void adjust_emc_dvfs_table(const struct tegra_emc_table *table,
+ int table_size)
+{
+ int i, j;
+ unsigned long rate;
+
+ if (table[0].rev < 0x33)
+ return;
+
+ for (i = 0; i < MAX_DVFS_FREQS; i++) {
+ int mv = emc->dvfs->millivolts[i];
+ if (!mv)
+ break;
+
+ /* For each dvfs voltage find maximum supported rate;
+ use 1MHz placeholder if not found */
+ for (rate = 1000, j = 0; j < table_size; j++) {
+ if (tegra_emc_clk_sel[j].input == NULL)
+ continue; /* invalid entry */
+
+ if ((mv >= table[j].emc_min_mv) &&
+ (rate < table[j].rate))
+ rate = table[j].rate;
+ }
+ /* Table entries specify rate in kHz */
+ emc->dvfs->freqs[i] = rate * 1000;
+ }
+}
+
+static bool is_emc_bridge(void)
+{
+ int mv;
+ unsigned long rate;
+
+ bridge = tegra_get_clock_by_name("bridge.emc");
+ BUG_ON(!bridge);
+
+ /* LPDDR2 does not need a bridge entry in DFS table: just lock bridge
+ rate at minimum so it won't interfere with emc bus operations */
+ if (dram_type == DRAM_TYPE_LPDDR2) {
+ clk_set_rate(bridge, 0);
+ return true;
+ }
+
+ /* DDR3 requires EMC DFS table to include a bridge entry with frequency
+ above minimum bridge threshold, and voltage below bridge threshold */
+ rate = clk_round_rate(bridge, TEGRA_EMC_BRIDGE_RATE_MIN);
+ if (IS_ERR_VALUE(rate))
+ return false;
+
+ mv = tegra_dvfs_predict_millivolts(emc, rate);
+ if (IS_ERR_VALUE(mv) || (mv > TEGRA_EMC_BRIDGE_MVOLTS_MIN))
+ return false;
+
+ if (clk_set_rate(bridge, rate))
+ return false;
+
+ return true;
+}
+
+static int tegra_emc_suspend_notify(struct notifier_block *nb,
+ unsigned long event, void *data)
+{
+ if (event != PM_SUSPEND_PREPARE)
+ return NOTIFY_OK;
+
+ if (dram_type == DRAM_TYPE_DDR3) {
+ if (clk_enable(bridge)) {
+ pr_info("Tegra emc suspend:"
+ " failed to enable bridge.emc\n");
+ return NOTIFY_STOP;
+ }
+ pr_info("Tegra emc suspend: enabled bridge.emc\n");
+ }
+ return NOTIFY_OK;
+};
+static struct notifier_block tegra_emc_suspend_nb = {
+ .notifier_call = tegra_emc_suspend_notify,
+ .priority = 2,
+};
+
+static int tegra_emc_resume_notify(struct notifier_block *nb,
+ unsigned long event, void *data)
+{
+ if (event != PM_POST_SUSPEND)
+ return NOTIFY_OK;
+
+ if (dram_type == DRAM_TYPE_DDR3) {
+ clk_disable(bridge);
+ pr_info("Tegra emc resume: disabled bridge.emc\n");
+ }
+ return NOTIFY_OK;
+};
+static struct notifier_block tegra_emc_resume_nb = {
+ .notifier_call = tegra_emc_resume_notify,
+ .priority = -1,
+};
+
+void tegra_init_emc(const struct tegra_emc_table *table, int table_size)
+{
+ int i, mv;
+ u32 reg, div_value;
+ bool max_entry = false;
+ unsigned long boot_rate, max_rate;
+ const struct clk_mux_sel *sel;
+
+ emc_stats.clkchange_count = 0;
+ spin_lock_init(&emc_stats.spinlock);
+ emc_stats.last_update = get_jiffies_64();
+
+ boot_rate = clk_get_rate(emc) / 1000;
+ max_rate = clk_get_max_rate(emc) / 1000;
+
+ if ((dram_type != DRAM_TYPE_DDR3) && (dram_type != DRAM_TYPE_LPDDR2)) {
+ pr_err("tegra: not supported DRAM type %u\n", dram_type);
+ return;
+ }
+
+ if (emc->parent != tegra_get_clock_by_name("pll_m")) {
+ pr_err("tegra: boot parent %s is not supported by EMC DFS\n",
+ emc->parent->name);
+ return;
+ }
+
+ if (!table || !table_size) {
+ pr_err("tegra: EMC DFS table is empty\n");
+ return;
+ }
+
+ tegra_emc_table_size = min(table_size, TEGRA_EMC_TABLE_MAX_SIZE);
+ switch (table[0].rev) {
+ case 0x30:
+ emc_num_burst_regs = 105;
+ break;
+ case 0x31:
+ case 0x32:
+ case 0x33:
+ emc_num_burst_regs = 107;
+ break;
+ default:
+ pr_err("tegra: invalid EMC DFS table: unknown rev 0x%x\n",
+ table[0].rev);
+ return;
+ }
+
+ /* Match EMC source/divider settings with table entries */
+ for (i = 0; i < tegra_emc_table_size; i++) {
+ unsigned long table_rate = table[i].rate;
+ if (!table_rate)
+ continue;
+
+ BUG_ON(table[i].rev != table[0].rev);
+
+ sel = find_matching_input(table_rate, &div_value);
+ if (!sel)
+ continue;
+
+ if (table_rate == boot_rate)
+ emc_last_sel = i;
+
+ if (table_rate == max_rate)
+ max_entry = true;
+
+ tegra_emc_clk_sel[i] = *sel;
+ BUG_ON(div_value >
+ (EMC_CLK_DIV_MASK >> EMC_CLK_DIV_SHIFT));
+ tegra_emc_clk_sel[i].value <<= EMC_CLK_SOURCE_SHIFT;
+ tegra_emc_clk_sel[i].value |= (div_value << EMC_CLK_DIV_SHIFT);
+
+ if ((div_value == 0) &&
+ (tegra_emc_clk_sel[i].input == emc->parent)) {
+ tegra_emc_clk_sel[i].value |= EMC_CLK_LOW_JITTER_ENABLE;
+ }
+
+ if (table[i].burst_regs[MC_EMEM_ARB_MISC0_INDEX] &
+ MC_EMEM_ARB_MISC0_EMC_SAME_FREQ)
+ tegra_emc_clk_sel[i].value |= EMC_CLK_MC_SAME_FREQ;
+ }
+
+ /* Validate EMC rate and voltage limits */
+ if (!max_entry) {
+ pr_err("tegra: invalid EMC DFS table: entry for max rate"
+ " %lu kHz is not found\n", max_rate);
+ return;
+ }
+
+ tegra_emc_table = table;
+
+ adjust_emc_dvfs_table(tegra_emc_table, tegra_emc_table_size);
+ mv = tegra_dvfs_predict_millivolts(emc, max_rate * 1000);
+ if ((mv <= 0) || (mv > emc->dvfs->max_millivolts)) {
+ tegra_emc_table = NULL;
+ pr_err("tegra: invalid EMC DFS table: maximum rate %lu kHz does"
+ " not match nominal voltage %d\n",
+ max_rate, emc->dvfs->max_millivolts);
+ return;
+ }
+
+ if (!is_emc_bridge()) {
+ tegra_emc_table = NULL;
+ pr_err("tegra: invalid EMC DFS table: emc bridge not found");
+ return;
+ }
+ pr_info("tegra: validated EMC DFS table\n");
+
+ /* Configure clock change mode according to dram type */
+ reg = emc_readl(EMC_CFG_2) & (~EMC_CFG_2_MODE_MASK);
+ reg |= ((dram_type == DRAM_TYPE_LPDDR2) ? EMC_CFG_2_PD_MODE :
+ EMC_CFG_2_SREF_MODE) << EMC_CFG_2_MODE_SHIFT;
+ emc_writel(reg, EMC_CFG_2);
+
+ register_pm_notifier(&tegra_emc_suspend_nb);
+ register_pm_notifier(&tegra_emc_resume_nb);
+}
+
+void tegra_emc_timing_invalidate(void)
+{
+ emc_timing_in_sync = false;
+}
+
+void tegra_emc_dram_type_init(struct clk *c)
+{
+ emc = c;
+
+ dram_type = (emc_readl(EMC_FBIO_CFG5) &
+ EMC_CFG5_TYPE_MASK) >> EMC_CFG5_TYPE_SHIFT;
+ if (dram_type == DRAM_TYPE_DDR3)
+ emc->min_rate = EMC_MIN_RATE_DDR3;
+
+ dram_dev_num = (mc_readl(MC_EMEM_ADR_CFG) & 0x1) + 1; /* 2 dev max */
+ emc_cfg_saved = emc_readl(EMC_CFG);
+}
+
+int tegra_emc_get_dram_type(void)
+{
+ return dram_type;
+}
+
+#ifdef CONFIG_DEBUG_FS
+
+static struct dentry *emc_debugfs_root;
+
+static int emc_stats_show(struct seq_file *s, void *data)
+{
+ int i;
+
+ emc_last_stats_update(TEGRA_EMC_TABLE_MAX_SIZE);
+
+ seq_printf(s, "%-10s %-10s \n", "rate kHz", "time");
+ for (i = 0; i < tegra_emc_table_size; i++) {
+ if (tegra_emc_clk_sel[i].input == NULL)
+ continue; /* invalid entry */
+
+ seq_printf(s, "%-10lu %-10llu \n", tegra_emc_table[i].rate,
+ cputime64_to_clock_t(emc_stats.time_at_clock[i]));
+ }
+ seq_printf(s, "%-15s %llu\n", "transitions:",
+ emc_stats.clkchange_count);
+ seq_printf(s, "%-15s %llu\n", "time-stamp:",
+ cputime64_to_clock_t(emc_stats.last_update));
+
+ return 0;
+}
+
+static int emc_stats_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, emc_stats_show, inode->i_private);
+}
+
+static const struct file_operations emc_stats_fops = {
+ .open = emc_stats_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int __init tegra_emc_debug_init(void)
+{
+ if (!tegra_emc_table)
+ return 0;
+
+ emc_debugfs_root = debugfs_create_dir("tegra_emc", NULL);
+ if (!emc_debugfs_root)
+ return -ENOMEM;
+
+ if (!debugfs_create_file(
+ "stats", S_IRUGO, emc_debugfs_root, NULL, &emc_stats_fops))
+ goto err_out;
+
+ return 0;
+
+err_out:
+ debugfs_remove_recursive(emc_debugfs_root);
+ return -ENOMEM;
+}
+
+late_initcall(tegra_emc_debug_init);
+#endif
diff --git a/arch/arm/mach-tegra/tegra3_emc.h b/arch/arm/mach-tegra/tegra3_emc.h
new file mode 100644
index 000000000000..cc0abc680166
--- /dev/null
+++ b/arch/arm/mach-tegra/tegra3_emc.h
@@ -0,0 +1,279 @@
+/*
+ * arch/arm/mach-tegra/tegra3_emc.h
+ *
+ * Copyright (C) 2011 NVIDIA Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef _MACH_TEGRA_TEGRA3_EMC_H
+#define _MACH_TEGRA_TEGRA3_EMC_H
+
+#define TEGRA_EMC_NUM_REGS 110
+
+#define TEGRA_EMC_BRIDGE_RATE_MIN 300000000
+#define TEGRA_EMC_BRIDGE_MVOLTS_MIN 1200
+
+struct tegra_emc_table {
+ u8 rev;
+ unsigned long rate;
+
+ /* unconditionally updated in one burst shot */
+ u32 burst_regs[TEGRA_EMC_NUM_REGS];
+
+ /* updated separately under some conditions */
+ u32 emc_zcal_cnt_long;
+ u32 emc_acal_interval;
+ u32 emc_periodic_qrst;
+ u32 emc_mode_reset;
+ u32 emc_mode_1;
+ u32 emc_mode_2;
+ u32 emc_dsr;
+ int emc_min_mv;
+};
+
+struct clk;
+
+void tegra_init_emc(const struct tegra_emc_table *table, int table_size);
+
+void tegra_emc_dram_type_init(struct clk *c);
+int tegra_emc_get_dram_type(void);
+
+#ifdef CONFIG_PM_SLEEP
+void tegra_mc_timing_restore(void);
+#else
+static inline void tegra_mc_timing_restore(void)
+{ }
+#endif
+
+#define EMC_INTSTATUS 0x0
+#define EMC_INTSTATUS_CLKCHANGE_COMPLETE (0x1 << 4)
+
+#define EMC_DBG 0x8
+#define EMC_DBG_WRITE_MUX_ACTIVE (0x1 << 1)
+
+#define EMC_CFG 0xc
+#define EMC_CFG_PERIODIC_QRST (0x1 << 21)
+#define EMC_CFG_DYN_SREF_ENABLE (0x1 << 28)
+#define EMC_CFG_PWR_MASK (0xF << 28)
+
+#define EMC_REFCTRL 0x20
+#define EMC_REFCTRL_DEV_SEL_SHIFT 0
+#define EMC_REFCTRL_DEV_SEL_MASK (0x3 << EMC_REFCTRL_DEV_SEL_SHIFT)
+#define EMC_REFCTRL_ENABLE (0x1 << 31)
+#define EMC_REFCTRL_ENABLE_ALL(num) \
+ ((((num > 1) ? 0 : 2) << EMC_REFCTRL_DEV_SEL_SHIFT) \
+ | EMC_REFCTRL_ENABLE)
+#define EMC_REFCTRL_DISABLE_ALL(num) \
+ (((num > 1) ? 0 : 2) << EMC_REFCTRL_DEV_SEL_SHIFT)
+
+#define EMC_TIMING_CONTROL 0x28
+#define EMC_RC 0x2c
+#define EMC_RFC 0x30
+#define EMC_RAS 0x34
+#define EMC_RP 0x38
+#define EMC_R2W 0x3c
+#define EMC_W2R 0x40
+#define EMC_R2P 0x44
+#define EMC_W2P 0x48
+#define EMC_RD_RCD 0x4c
+#define EMC_WR_RCD 0x50
+#define EMC_RRD 0x54
+#define EMC_REXT 0x58
+#define EMC_WDV 0x5c
+#define EMC_QUSE 0x60
+#define EMC_QRST 0x64
+#define EMC_QSAFE 0x68
+#define EMC_RDV 0x6c
+#define EMC_REFRESH 0x70
+#define EMC_BURST_REFRESH_NUM 0x74
+#define EMC_PDEX2WR 0x78
+#define EMC_PDEX2RD 0x7c
+#define EMC_PCHG2PDEN 0x80
+#define EMC_ACT2PDEN 0x84
+#define EMC_AR2PDEN 0x88
+#define EMC_RW2PDEN 0x8c
+#define EMC_TXSR 0x90
+#define EMC_TCKE 0x94
+#define EMC_TFAW 0x98
+#define EMC_TRPAB 0x9c
+#define EMC_TCLKSTABLE 0xa0
+#define EMC_TCLKSTOP 0xa4
+#define EMC_TREFBW 0xa8
+#define EMC_QUSE_EXTRA 0xac
+#define EMC_ODT_WRITE 0xb0
+#define EMC_ODT_READ 0xb4
+#define EMC_WEXT 0xb8
+#define EMC_CTT 0xbc
+
+#define EMC_MRS_WAIT_CNT 0xc8
+#define EMC_MRS_WAIT_CNT_SHORT_WAIT_SHIFT 0
+#define EMC_MRS_WAIT_CNT_SHORT_WAIT_MASK \
+ (0x3FF << EMC_MRS_WAIT_CNT_SHORT_WAIT_SHIFT)
+#define EMC_MRS_WAIT_CNT_LONG_WAIT_SHIFT 16
+#define EMC_MRS_WAIT_CNT_LONG_WAIT_MASK \
+ (0x3FF << EMC_MRS_WAIT_CNT_LONG_WAIT_SHIFT)
+
+#define EMC_MRS 0xcc
+#define EMC_MODE_SET_DLL_RESET (0x1 << 8)
+#define EMC_MODE_SET_LONG_CNT (0x1 << 26)
+#define EMC_EMRS 0xd0
+
+#define EMC_SELF_REF 0xe0
+#define EMC_SELF_REF_CMD_ENABLED (0x1 << 0)
+#define EMC_SELF_REF_DEV_SEL_SHIFT 30
+#define EMC_SELF_REF_DEV_SEL_MASK (0x3 << EMC_SELF_REF_DEV_SEL_SHIFT)
+enum {
+ DRAM_DEV_SEL_ALL = 0,
+ DRAM_DEV_SEL_0 = (2 << EMC_SELF_REF_DEV_SEL_SHIFT),
+ DRAM_DEV_SEL_1 = (1 << EMC_SELF_REF_DEV_SEL_SHIFT),
+};
+#define DRAM_BROADCAST(num) \
+ (((num) > 1) ? DRAM_DEV_SEL_ALL : DRAM_DEV_SEL_0)
+
+#define EMC_MRW 0xe8
+#define EMC_MRR 0xec
+#define EMC_XM2DQSPADCTRL3 0xf8
+#define EMC_XM2DQSPADCTRL3_VREF_ENABLE (0x1 << 5)
+#define EMC_FBIO_SPARE 0x100
+
+#define EMC_FBIO_CFG5 0x104
+#define EMC_CFG5_TYPE_SHIFT 0x0
+#define EMC_CFG5_TYPE_MASK (0x3 << EMC_CFG5_TYPE_SHIFT)
+enum {
+ DRAM_TYPE_DDR3 = 0,
+ DRAM_TYPE_LPDDR2 = 2,
+};
+#define EMC_CFG5_QUSE_MODE_SHIFT 13
+#define EMC_CFG5_QUSE_MODE_MASK (0x7 << EMC_CFG5_QUSE_MODE_SHIFT)
+enum {
+ EMC_CFG5_QUSE_MODE_NORMAL = 0,
+ EMC_CFG5_QUSE_MODE_ALWAYS_ON,
+ EMC_CFG5_QUSE_MODE_INTERNAL_LPBK,
+ EMC_CFG5_QUSE_MODE_PULSE_INTERN,
+ EMC_CFG5_QUSE_MODE_PULSE_EXTERN,
+};
+
+#define EMC_FBIO_CFG6 0x114
+#define EMC_CFG_RSV 0x120
+#define EMC_AUTO_CAL_CONFIG 0x2a4
+#define EMC_AUTO_CAL_INTERVAL 0x2a8
+#define EMC_AUTO_CAL_STATUS 0x2ac
+#define EMC_AUTO_CAL_STATUS_ACTIVE (0x1 << 31)
+#define EMC_STATUS 0x2b4
+#define EMC_STATUS_TIMING_UPDATE_STALLED (0x1 << 23)
+
+#define EMC_CFG_2 0x2b8
+#define EMC_CFG_2_MODE_SHIFT 0
+#define EMC_CFG_2_MODE_MASK (0x7 << EMC_CFG_2_MODE_SHIFT)
+#define EMC_CFG_2_SREF_MODE 0x1
+#define EMC_CFG_2_PD_MODE 0x3
+
+#define EMC_CFG_DIG_DLL 0x2bc
+#define EMC_CFG_DIG_DLL_PERIOD 0x2c0
+#define EMC_CTT_DURATION 0x2d8
+#define EMC_CTT_TERM_CTRL 0x2dc
+#define EMC_ZCAL_INTERVAL 0x2e0
+#define EMC_ZCAL_WAIT_CNT 0x2e4
+
+#define EMC_ZQ_CAL 0x2ec
+#define EMC_ZQ_CAL_CMD (0x1 << 0)
+#define EMC_ZQ_CAL_LONG (0x1 << 4)
+#define EMC_ZQ_CAL_LONG_CMD_DEV0 \
+ (DRAM_DEV_SEL_0 | EMC_ZQ_CAL_LONG | EMC_ZQ_CAL_CMD)
+#define EMC_ZQ_CAL_LONG_CMD_DEV1 \
+ (DRAM_DEV_SEL_1 | EMC_ZQ_CAL_LONG | EMC_ZQ_CAL_CMD)
+
+#define EMC_XM2CMDPADCTRL 0x2f0
+#define EMC_XM2DQSPADCTRL2 0x2fc
+#define EMC_XM2DQSPADCTRL2_VREF_ENABLE (0x1 << 5)
+#define EMC_XM2DQPADCTRL2 0x304
+#define EMC_XM2CLKPADCTRL 0x308
+#define EMC_XM2COMPPADCTRL 0x30c
+#define EMC_XM2COMPPADCTRL_VREF_CAL_ENABLE (0x1 << 10)
+#define EMC_XM2VTTGENPADCTRL 0x310
+#define EMC_XM2VTTGENPADCTRL2 0x314
+#define EMC_XM2QUSEPADCTRL 0x318
+#define EMC_XM2QUSEPADCTRL_IVREF_ENABLE (0x1 << 4)
+#define EMC_DLL_XFORM_DQS0 0x328
+#define EMC_DLL_XFORM_DQS1 0x32c
+#define EMC_DLL_XFORM_DQS2 0x330
+#define EMC_DLL_XFORM_DQS3 0x334
+#define EMC_DLL_XFORM_DQS4 0x338
+#define EMC_DLL_XFORM_DQS5 0x33c
+#define EMC_DLL_XFORM_DQS6 0x340
+#define EMC_DLL_XFORM_DQS7 0x344
+#define EMC_DLL_XFORM_QUSE0 0x348
+#define EMC_DLL_XFORM_QUSE1 0x34c
+#define EMC_DLL_XFORM_QUSE2 0x350
+#define EMC_DLL_XFORM_QUSE3 0x354
+#define EMC_DLL_XFORM_QUSE4 0x358
+#define EMC_DLL_XFORM_QUSE5 0x35c
+#define EMC_DLL_XFORM_QUSE6 0x360
+#define EMC_DLL_XFORM_QUSE7 0x364
+#define EMC_DLL_XFORM_DQ0 0x368
+#define EMC_DLL_XFORM_DQ1 0x36c
+#define EMC_DLL_XFORM_DQ2 0x370
+#define EMC_DLL_XFORM_DQ3 0x374
+#define EMC_DLI_TRIM_TXDQS0 0x3a8
+#define EMC_DLI_TRIM_TXDQS1 0x3ac
+#define EMC_DLI_TRIM_TXDQS2 0x3b0
+#define EMC_DLI_TRIM_TXDQS3 0x3b4
+#define EMC_DLI_TRIM_TXDQS4 0x3b8
+#define EMC_DLI_TRIM_TXDQS5 0x3bc
+#define EMC_DLI_TRIM_TXDQS6 0x3c0
+#define EMC_DLI_TRIM_TXDQS7 0x3c4
+#define EMC_STALL_BEFORE_CLKCHANGE 0x3c8
+#define EMC_STALL_AFTER_CLKCHANGE 0x3cc
+#define EMC_UNSTALL_RW_AFTER_CLKCHANGE 0x3d0
+#define EMC_SEL_DPD_CTRL 0x3d8
+#define EMC_SEL_DPD_CTRL_QUSE_DPD_ENABLE (0x1 << 9)
+#define EMC_PRE_REFRESH_REQ_CNT 0x3dc
+#define EMC_DYN_SELF_REF_CONTROL 0x3e0
+#define EMC_TXSRDLL 0x3e4
+
+#define MC_EMEM_ADR_CFG 0x54
+#define MC_EMEM_ARB_CFG 0x90
+#define MC_EMEM_ARB_OUTSTANDING_REQ 0x94
+#define MC_EMEM_ARB_OUTSTANDING_REQ_MAX_SHIFT 0
+#define MC_EMEM_ARB_OUTSTANDING_REQ_MAX_MASK \
+ (0x1FF << MC_EMEM_ARB_OUTSTANDING_REQ_MAX_SHIFT)
+#define MC_EMEM_ARB_OUTSTANDING_REQ_HOLDOFF_OVERRIDE (0x1 << 30)
+#define MC_EMEM_ARB_OUTSTANDING_REQ_LIMIT_ENABLE (0x1 << 31)
+#define MC_EMEM_ARB_TIMING_RCD 0x98
+#define MC_EMEM_ARB_TIMING_RP 0x9c
+#define MC_EMEM_ARB_TIMING_RC 0xa0
+#define MC_EMEM_ARB_TIMING_RAS 0xa4
+#define MC_EMEM_ARB_TIMING_FAW 0xa8
+#define MC_EMEM_ARB_TIMING_RRD 0xac
+#define MC_EMEM_ARB_TIMING_RAP2PRE 0xb0
+#define MC_EMEM_ARB_TIMING_WAP2PRE 0xb4
+#define MC_EMEM_ARB_TIMING_R2R 0xb8
+#define MC_EMEM_ARB_TIMING_W2W 0xbc
+#define MC_EMEM_ARB_TIMING_R2W 0xc0
+#define MC_EMEM_ARB_TIMING_W2R 0xc4
+#define MC_EMEM_ARB_DA_TURNS 0xd0
+#define MC_EMEM_ARB_DA_COVERS 0xd4
+#define MC_EMEM_ARB_MISC0 0xd8
+#define MC_EMEM_ARB_MISC0_EMC_SAME_FREQ (0x1 << 27)
+#define MC_EMEM_ARB_MISC1 0xdc
+#define MC_EMEM_ARB_RING1_THROTTLE 0xe0
+#define MC_EMEM_ARB_RING3_THROTTLE 0xe4
+#define MC_EMEM_ARB_OVERRIDE 0xe8
+#define MC_TIMING_CONTROL 0xfc
+#define MC_RESERVED_RSV 0x3fc
+
+#endif
diff --git a/arch/arm/mach-tegra/tegra3_speedo.c b/arch/arm/mach-tegra/tegra3_speedo.c
new file mode 100644
index 000000000000..5c37fd3f3a84
--- /dev/null
+++ b/arch/arm/mach-tegra/tegra3_speedo.c
@@ -0,0 +1,373 @@
+/*
+ * arch/arm/mach-tegra/tegra3_speedo.c
+ *
+ * Copyright (c) 2011, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <mach/iomap.h>
+
+#include "fuse.h"
+
+#define CORE_PROCESS_CORNERS_NUM 1
+#define CPU_PROCESS_CORNERS_NUM 5
+
+#define FUSE_SPEEDO_CALIB_0 0x114
+#define FUSE_PACKAGE_INFO 0X1FC
+
+/* Maximum speedo levels for each core process corner */
+static const u32 core_process_speedos[][CORE_PROCESS_CORNERS_NUM] = {
+/* proc_id 0 */
+ {180}, /* [0]: soc_speedo_id 0: any A01 */
+
+/* T30 family */
+ {180}, /* [1]: soc_speedo_id 1: AP30 */
+ {204}, /* [2]: soc_speedo_id 2: T30 */
+ {192}, /* [3]: soc_speedo_id 2: T30S */
+
+/* Characterization SKUs */
+ {168}, /* [4]: soc_speedo_id 1: AP30 char */
+ {192}, /* [5]: soc_speedo_id 2: T30 char */
+ {184}, /* [6]: soc_speedo_id 2: T30S char */
+
+/* T33 family */
+ {180}, /* [7]: soc_speedo_id = 1 - AP33 */
+ {208}, /* [8]: soc_speedo_id = 2 - T33 */
+ {192}, /* [9]: soc_speedo_id = 2 - T33S */
+
+/* T30 'L' family */
+ {192}, /* [10]: soc_speedo_id 1: T30L */
+ {192}, /* [11]: soc_speedo_id 1: T30SL */
+};
+
+/* Maximum speedo levels for each CPU process corner */
+static const u32 cpu_process_speedos[][CPU_PROCESS_CORNERS_NUM] = {
+/* proc_id 0 1 2 3 4*/
+ {306, 338, 360, 376, UINT_MAX}, /* [0]: cpu_speedo_id 0: any A01 */
+
+/* T30 family */
+ {304, 336, 359, 375, UINT_MAX}, /* [1]: cpu_speedo_id 1: AP30 */
+ {336, 336, 359, 375, UINT_MAX}, /* [2]: cpu_speedo_id 2: T30 */
+ {336, 336, 359, 375, UINT_MAX}, /* [3]: cpu_speedo_id 3: T30S */
+
+/* Characterization SKUs */
+ {292, 324, 348, 364, UINT_MAX}, /* [4]: cpu_speedo_id 1: AP30char */
+ {324, 324, 348, 364, UINT_MAX}, /* [5]: cpu_speedo_id 2: T30char */
+ {324, 324, 348, 364, UINT_MAX}, /* [6]: cpu_speedo_id 3: T30Schar */
+
+/* T33 family */
+ {305, 337, 359, 376, UINT_MAX}, /* [7]: cpu_speedo_id = 4 - AP33 */
+ {368, 368, 368, 368, 392}, /* [8]: cpu_speedo_id = 5 - T33 */
+ {376, 376, 376, 376, 392}, /* [9]: cpu_speedo_id = 6 - T33S */
+
+/* T30 'L' family */
+ {305, 337, 359, 376, 392}, /* [10]: cpu_speedo_id 7: T30L */
+ {305, 337, 359, 376, 392}, /* [11]: cpu_speedo_id 8: T30SL */
+};
+
+/*
+ * Common speedo_value array threshold index for both core_process_speedos and
+ * cpu_process_speedos arrays. Make sure these two arrays are always in synch.
+ */
+static int threshold_index;
+
+static int cpu_process_id;
+static int core_process_id;
+static int cpu_speedo_id;
+static int soc_speedo_id;
+static int package_id;
+
+static void fuse_speedo_calib(u32 *speedo_g, u32 *speedo_lp)
+{
+ u32 reg;
+
+ BUG_ON(!speedo_g || !speedo_lp);
+ reg = tegra_fuse_readl(FUSE_SPEEDO_CALIB_0);
+
+ /* Speedo LP = Lower 16-bits Multiplied by 4 */
+ *speedo_lp = (reg & 0xFFFF) * 4;
+
+ /* Speedo G = Upper 16-bits Multiplied by 4 */
+ *speedo_g = ((reg >> 16) & 0xFFFF) * 4;
+}
+
+static void rev_sku_to_speedo_ids(int rev, int sku)
+{
+ switch (rev) {
+ case TEGRA_REVISION_A01: /* any A01 */
+ cpu_speedo_id = 0;
+ soc_speedo_id = 0;
+ threshold_index = 0;
+ break;
+
+ case TEGRA_REVISION_A02:
+ case TEGRA_REVISION_A03:
+ switch (sku) {
+ case 0x87: /* AP30 */
+ case 0x82: /* T30V */
+ cpu_speedo_id = 1;
+ soc_speedo_id = 1;
+ threshold_index = 1;
+ break;
+
+ case 0x81: /* T30 */
+ switch (package_id) {
+ case 1: /* MID => T30 */
+ cpu_speedo_id = 2;
+ soc_speedo_id = 2;
+ threshold_index = 2;
+ break;
+ case 2: /* DSC => AP33 */
+ cpu_speedo_id = 4;
+ soc_speedo_id = 1;
+ threshold_index = 7;
+ break;
+ default:
+ pr_err("Tegra3 Rev-A02: Reserved pkg: %d\n",
+ package_id);
+ BUG();
+ break;
+ }
+ break;
+
+ case 0x80: /* T33 or T33S */
+ switch (package_id) {
+ case 1: /* MID => T33 */
+ cpu_speedo_id = 5;
+ soc_speedo_id = 2;
+ threshold_index = 8;
+ break;
+ case 2: /* DSC => T33S */
+ cpu_speedo_id = 6;
+ soc_speedo_id = 2;
+ threshold_index = 9;
+ break;
+ default:
+ pr_err("Tegra3 Rev-A02: Reserved pkg: %d\n",
+ package_id);
+ BUG();
+ break;
+ }
+ break;
+
+ case 0x83: /* T30L or T30S */
+ switch (package_id) {
+ case 1: /* MID => T30L */
+ cpu_speedo_id = 7;
+ soc_speedo_id = 1;
+ threshold_index = 10;
+ break;
+ case 2: /* DSC => T30S */
+ cpu_speedo_id = 3;
+ soc_speedo_id = 2;
+ threshold_index = 3;
+ break;
+ default:
+ pr_err("Tegra3 Rev-A02: Reserved pkg: %d\n",
+ package_id);
+ BUG();
+ break;
+ }
+ break;
+
+ case 0x8F: /* T30SL */
+ cpu_speedo_id = 8;
+ soc_speedo_id = 1;
+ threshold_index = 11;
+ break;
+
+/* Characterization SKUs */
+ case 0x08: /* AP30 char */
+ cpu_speedo_id = 1;
+ soc_speedo_id = 1;
+ threshold_index = 4;
+ break;
+ case 0x02: /* T30 char */
+ cpu_speedo_id = 2;
+ soc_speedo_id = 2;
+ threshold_index = 5;
+ break;
+ case 0x04: /* T30S char */
+ cpu_speedo_id = 3;
+ soc_speedo_id = 2;
+ threshold_index = 6;
+ break;
+
+ case 0: /* ENG - check package_id */
+ pr_info("Tegra3 ENG SKU: Checking package_id\n");
+ switch (package_id) {
+ case 1: /* MID => assume T30 */
+ cpu_speedo_id = 2;
+ soc_speedo_id = 2;
+ threshold_index = 2;
+ break;
+ case 2: /* DSC => assume T30S */
+ cpu_speedo_id = 3;
+ soc_speedo_id = 2;
+ threshold_index = 3;
+ break;
+ default:
+ pr_err("Tegra3 Rev-A02: Reserved pkg: %d\n",
+ package_id);
+ BUG();
+ break;
+ }
+ break;
+
+ default:
+ /* FIXME: replace with BUG() when all SKU's valid */
+ pr_err("Tegra3 Rev-A02: Unknown SKU %d\n", sku);
+ cpu_speedo_id = 0;
+ soc_speedo_id = 0;
+ threshold_index = 0;
+ break;
+ }
+ break;
+ default:
+ BUG();
+ break;
+ }
+}
+
+void tegra_init_speedo_data(void)
+{
+ u32 cpu_speedo_val, core_speedo_val;
+ int iv;
+
+ /* Package info: 4 bits - 0,3:reserved 1:MID 2:DSC */
+ package_id = tegra_fuse_readl(FUSE_PACKAGE_INFO) & 0x0F;
+
+ /* Arrays must be of equal size - each index corresponds to a SKU */
+ BUG_ON(ARRAY_SIZE(cpu_process_speedos) !=
+ ARRAY_SIZE(core_process_speedos));
+
+ rev_sku_to_speedo_ids(tegra_get_revision(), tegra_sku_id());
+ BUG_ON(threshold_index >= ARRAY_SIZE(cpu_process_speedos));
+
+ fuse_speedo_calib(&cpu_speedo_val, &core_speedo_val);
+ pr_debug("%s CPU speedo value %u\n", __func__, cpu_speedo_val);
+ pr_debug("%s Core speedo value %u\n", __func__, core_speedo_val);
+
+ for (iv = 0; iv < CPU_PROCESS_CORNERS_NUM; iv++) {
+ if (cpu_speedo_val <
+ cpu_process_speedos[threshold_index][iv]) {
+ break;
+ }
+ }
+ cpu_process_id = iv -1;
+
+ if (cpu_process_id == -1) {
+ pr_err("****************************************************");
+ pr_err("****************************************************");
+ pr_err("* tegra3_speedo: CPU speedo value %3d out of range *",
+ cpu_speedo_val);
+ pr_err("****************************************************");
+ pr_err("****************************************************");
+
+ cpu_process_id = INVALID_PROCESS_ID;
+ cpu_speedo_id = 1;
+ }
+
+ for (iv = 0; iv < CORE_PROCESS_CORNERS_NUM; iv++) {
+ if (core_speedo_val <
+ core_process_speedos[threshold_index][iv]) {
+ break;
+ }
+ }
+ core_process_id = iv -1;
+
+ if (core_process_id == -1) {
+ pr_err("****************************************************");
+ pr_err("****************************************************");
+ pr_err("* tegra3_speedo: CORE speedo value %3d out of range *",
+ core_speedo_val);
+ pr_err("****************************************************");
+ pr_err("****************************************************");
+
+ core_process_id = INVALID_PROCESS_ID;
+ soc_speedo_id = 1;
+ }
+
+ pr_info("Tegra3: CPU Speedo ID %d, Soc Speedo ID %d",
+ cpu_speedo_id, soc_speedo_id);
+}
+
+int tegra_cpu_process_id(void)
+{
+ /* FIXME: remove when ready to deprecate invalid process-id boards */
+ if (cpu_process_id == INVALID_PROCESS_ID)
+ return 0;
+ else
+ return cpu_process_id;
+}
+
+int tegra_core_process_id(void)
+{
+ /* FIXME: remove when ready to deprecate invalid process-id boards */
+ if (core_process_id == INVALID_PROCESS_ID)
+ return 0;
+ else
+ return core_process_id;
+}
+
+int tegra_cpu_speedo_id(void)
+{
+ return cpu_speedo_id;
+}
+
+int tegra_soc_speedo_id(void)
+{
+ return soc_speedo_id;
+}
+
+int tegra_package_id(void)
+{
+ return package_id;
+}
+
+/*
+ * CPU and core nominal voltage levels as determined by chip SKU and speedo
+ * (not final - can be lowered by dvfs tables and rail dependencies; the
+ * latter is resolved by the dvfs code)
+ */
+static const int cpu_speedo_nominal_millivolts[] =
+/* speedo_id 0, 1, 2, 3, 4, 5, 6, 7, 8 */
+ { 1125, 1150, 1150, 1150, 1237, 1237, 1237, 1150, 1150 };
+
+int tegra_cpu_speedo_mv(void)
+{
+ BUG_ON(cpu_speedo_id >= ARRAY_SIZE(cpu_speedo_nominal_millivolts));
+ return cpu_speedo_nominal_millivolts[cpu_speedo_id];
+}
+
+int tegra_core_speedo_mv(void)
+{
+ switch (soc_speedo_id) {
+ case 0:
+ return 1200;
+ case 1:
+ if ((cpu_speedo_id != 7) && (cpu_speedo_id != 8))
+ return 1200;
+ /* fall thru for T30L or T30SL */
+ case 2:
+ return 1300;
+ default:
+ BUG();
+ }
+}
diff --git a/arch/arm/mach-tegra/tegra3_thermal.c b/arch/arm/mach-tegra/tegra3_thermal.c
new file mode 100644
index 000000000000..27e09545806d
--- /dev/null
+++ b/arch/arm/mach-tegra/tegra3_thermal.c
@@ -0,0 +1,350 @@
+/*
+ * arch/arm/mach-tegra/tegra3_thermal.c
+ *
+ * Copyright (C) 2010-2011 NVIDIA Corporation.
+ *
+ * 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/cpufreq.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/uaccess.h>
+#include <linux/thermal.h>
+#include <mach/thermal.h>
+#include <mach/edp.h>
+#include <linux/slab.h>
+
+#include "clock.h"
+#include "cpu-tegra.h"
+#include "dvfs.h"
+
+#define MAX_ZONES (16)
+
+struct tegra_thermal {
+ struct tegra_thermal_device *device;
+ long temp_throttle_tj;
+ long temp_shutdown_tj;
+#ifdef CONFIG_TEGRA_THERMAL_SYSFS
+ struct thermal_zone_device *thz;
+ int tc1;
+ int tc2;
+ long passive_delay;
+#else
+ long temp_throttle_low_tj;
+#endif
+#ifdef CONFIG_TEGRA_EDP_LIMITS
+ int edp_thermal_zone_val;
+ long edp_offset;
+ long hysteresis_edp;
+#endif
+};
+
+static struct tegra_thermal thermal_state = {
+ .device = NULL,
+#ifdef CONFIG_TEGRA_EDP_LIMITS
+ .edp_thermal_zone_val = -1,
+#endif
+};
+
+#ifndef CONFIG_TEGRA_THERMAL_SYSFS
+static bool throttle_enb;
+struct mutex mutex;
+#endif
+
+#ifdef CONFIG_TEGRA_EDP_LIMITS
+static inline long edp2tj(struct tegra_thermal *thermal,
+ long edp_temp)
+{
+ return edp_temp + thermal->edp_offset;
+}
+
+static inline long tj2edp(struct tegra_thermal *thermal,
+ long temp_tj)
+{
+ return temp_tj - thermal->edp_offset;
+}
+#endif
+
+static inline long dev2tj(struct tegra_thermal_device *dev,
+ long dev_temp)
+{
+ return dev_temp + dev->offset;
+}
+
+static inline long tj2dev(struct tegra_thermal_device *dev,
+ long tj_temp)
+{
+ return tj_temp - dev->offset;
+}
+
+#ifdef CONFIG_TEGRA_THERMAL_SYSFS
+
+static int tegra_thermal_zone_bind(struct thermal_zone_device *thermal,
+ struct thermal_cooling_device *cdevice) {
+ /* Support only Thermal Throttling (1 trip) for now */
+ return thermal_zone_bind_cooling_device(thermal, 0, cdevice);
+}
+
+static int tegra_thermal_zone_unbind(struct thermal_zone_device *thermal,
+ struct thermal_cooling_device *cdevice) {
+ /* Support only Thermal Throttling (1 trip) for now */
+ return thermal_zone_unbind_cooling_device(thermal, 0, cdevice);
+}
+
+static int tegra_thermal_zone_get_temp(struct thermal_zone_device *thz,
+ long *temp)
+{
+ struct tegra_thermal *thermal = thz->devdata;
+ thermal->device->get_temp(thermal->device->data, temp);
+
+ return 0;
+}
+
+static int tegra_thermal_zone_get_trip_type(
+ struct thermal_zone_device *thermal,
+ int trip,
+ enum thermal_trip_type *type) {
+
+ /* Support only Thermal Throttling (1 trip) for now */
+ if (trip != 0)
+ return -EINVAL;
+
+ *type = THERMAL_TRIP_PASSIVE;
+
+ return 0;
+}
+
+static int tegra_thermal_zone_get_trip_temp(struct thermal_zone_device *thz,
+ int trip,
+ long *temp) {
+ struct tegra_thermal *thermal = thz->devdata;
+
+ /* Support only Thermal Throttling (1 trip) for now */
+ if (trip != 0)
+ return -EINVAL;
+
+ *temp = tj2dev(thermal->device, thermal->temp_throttle_tj);
+
+ return 0;
+}
+
+static struct thermal_zone_device_ops tegra_thermal_zone_ops = {
+ .bind = tegra_thermal_zone_bind,
+ .unbind = tegra_thermal_zone_unbind,
+ .get_temp = tegra_thermal_zone_get_temp,
+ .get_trip_type = tegra_thermal_zone_get_trip_type,
+ .get_trip_temp = tegra_thermal_zone_get_trip_temp,
+};
+#endif
+
+/* The thermal sysfs handles notifying the throttling
+ * cooling device */
+#ifndef CONFIG_TEGRA_THERMAL_SYSFS
+static void tegra_therm_throttle(bool enable)
+{
+ if (throttle_enb != enable) {
+ mutex_lock(&mutex);
+ tegra_throttling_enable(enable);
+ throttle_enb = enable;
+ mutex_unlock(&mutex);
+ }
+}
+#endif
+
+/* Make sure this function remains stateless */
+void tegra_thermal_alert(void *data)
+{
+ struct tegra_thermal *thermal = data;
+ int err;
+ long temp_dev, temp_tj;
+ long lo_limit_throttle_tj, hi_limit_throttle_tj;
+ long lo_limit_edp_tj = 0, hi_limit_edp_tj = 0;
+ int lo_limit_tj = 0, hi_limit_tj = 0;
+#ifdef CONFIG_TEGRA_EDP_LIMITS
+ const struct tegra_edp_limits *z;
+ int zones_sz;
+ int i;
+#endif
+
+ if (thermal != &thermal_state)
+ BUG();
+
+#ifdef CONFIG_TEGRA_THERMAL_SYSFS
+ if (thermal->thz) {
+ if (!thermal->thz->passive)
+ thermal_zone_device_update(thermal->thz);
+ }
+#endif
+
+ err = thermal->device->get_temp(thermal->device->data, &temp_dev);
+ if (err) {
+ pr_err("%s: get temp fail(%d)", __func__, err);
+ return;
+ }
+
+ /* Convert all temps to tj and then do all work/logic in terms of
+ tj in order to avoid confusion */
+ temp_tj = dev2tj(thermal->device, temp_dev);
+
+ lo_limit_throttle_tj = dev2tj(thermal->device, 0);
+ hi_limit_throttle_tj = thermal->temp_throttle_tj;
+
+#ifndef CONFIG_TEGRA_THERMAL_SYSFS
+ /* Check to see if we are currently throttling */
+ if ((tegra_is_throttling() &&
+ (temp_tj > thermal->temp_throttle_low_tj))
+ || (temp_tj >= thermal->temp_throttle_tj)) {
+ lo_limit_throttle_tj = thermal->temp_throttle_low_tj;
+ hi_limit_throttle_tj = thermal->temp_shutdown_tj;
+ }
+#endif
+
+#ifdef CONFIG_TEGRA_EDP_LIMITS
+ tegra_get_cpu_edp_limits(&z, &zones_sz);
+
+/* edp table based off of tdiode measurements */
+#define EDP_TEMP_TJ(_index) edp2tj(thermal, z[_index].temperature * 1000)
+
+ if (temp_tj < EDP_TEMP_TJ(0)) {
+ lo_limit_edp_tj = dev2tj(thermal->device, 0);
+ hi_limit_edp_tj = EDP_TEMP_TJ(0);
+ } else if (temp_tj >= EDP_TEMP_TJ(zones_sz-1)) {
+ lo_limit_edp_tj = EDP_TEMP_TJ(zones_sz-1) -
+ thermal->hysteresis_edp;
+ hi_limit_edp_tj = thermal->temp_shutdown_tj;
+ } else {
+ for (i = 0; (i + 1) < zones_sz; i++) {
+ if ((temp_tj >= EDP_TEMP_TJ(i)) &&
+ (temp_tj < EDP_TEMP_TJ(i+1))) {
+ lo_limit_edp_tj = EDP_TEMP_TJ(i) -
+ thermal->hysteresis_edp;
+ hi_limit_edp_tj = EDP_TEMP_TJ(i+1);
+ break;
+ }
+ }
+ }
+#undef EDP_TEMP_TJ
+#else
+ lo_limit_edp_tj = 0;
+ hi_limit_edp_tj = thermal->temp_shutdown_tj;
+#endif
+
+ /* Get smallest window size */
+ lo_limit_tj = max(lo_limit_throttle_tj, lo_limit_edp_tj);
+ hi_limit_tj = min(hi_limit_throttle_tj, hi_limit_edp_tj);
+
+ thermal->device->set_limits(thermal->device->data,
+ tj2dev(thermal->device, lo_limit_tj),
+ tj2dev(thermal->device, hi_limit_tj));
+
+#ifndef CONFIG_TEGRA_THERMAL_SYSFS
+ if (temp_tj >= thermal->temp_throttle_tj) {
+ /* start throttling */
+ if (!tegra_is_throttling())
+ tegra_therm_throttle(true);
+ } else if (temp_tj <= thermal->temp_throttle_low_tj) {
+ /* switch off throttling */
+ if (tegra_is_throttling())
+ tegra_therm_throttle(false);
+ }
+#endif
+
+#ifdef CONFIG_TEGRA_EDP_LIMITS
+ /* inform edp governor */
+ if (thermal->edp_thermal_zone_val != temp_tj)
+ tegra_edp_update_thermal_zone(tj2edp(thermal, temp_tj)/1000);
+
+ thermal->edp_thermal_zone_val = temp_tj;
+#endif
+}
+
+int tegra_thermal_set_device(struct tegra_thermal_device *device)
+{
+#ifdef CONFIG_TEGRA_THERMAL_SYSFS
+ struct thermal_zone_device *thz;
+#endif
+
+ /* only support one device */
+ if (thermal_state.device)
+ return -EINVAL;
+
+ thermal_state.device = device;
+
+#ifdef CONFIG_TEGRA_THERMAL_SYSFS
+ thz = thermal_zone_device_register("thermal",
+ 1, /* trips */
+ &thermal_state,
+ &tegra_thermal_zone_ops,
+ thermal_state.tc1, /* dT/dt */
+ thermal_state.tc2, /* throttle */
+ thermal_state.passive_delay,
+ 0); /* polling delay */
+
+ if (IS_ERR(thz)) {
+ thz = NULL;
+ return -ENODEV;
+ }
+
+ thermal_state.thz = thz;
+#endif
+ thermal_state.device->set_alert(thermal_state.device->data,
+ tegra_thermal_alert,
+ &thermal_state);
+
+ thermal_state.device->set_shutdown_temp(thermal_state.device->data,
+ tj2dev(device, thermal_state.temp_shutdown_tj));
+
+ /* initialize limits */
+ tegra_thermal_alert(&thermal_state);
+
+ return 0;
+}
+
+int __init tegra_thermal_init(struct tegra_thermal_data *data)
+{
+#ifdef CONFIG_TEGRA_THERMAL_SYSFS
+ thermal_state.tc1 = data->tc1;
+ thermal_state.tc2 = data->tc2;
+ thermal_state.passive_delay = data->passive_delay;
+#else
+ mutex_init(&mutex);
+ thermal_state.temp_throttle_low_tj = data->temp_throttle +
+ data->temp_offset -
+ data->hysteresis_throttle;
+#endif
+#ifdef CONFIG_TEGRA_EDP_LIMITS
+ thermal_state.edp_offset = data->edp_offset;
+ thermal_state.hysteresis_edp = data->hysteresis_edp;
+#endif
+ thermal_state.temp_throttle_tj = data->temp_throttle +
+ data->temp_offset;
+ thermal_state.temp_shutdown_tj = data->temp_shutdown +
+ data->temp_offset;
+
+ return 0;
+}
+
+int tegra_thermal_exit(void)
+{
+#ifdef CONFIG_TEGRA_THERMAL_SYSFS
+ if (thermal_state.thz)
+ thermal_zone_device_unregister(thermal_state.thz);
+#endif
+
+ return 0;
+}
diff --git a/arch/arm/mach-tegra/tegra3_throttle.c b/arch/arm/mach-tegra/tegra3_throttle.c
new file mode 100644
index 000000000000..f927be7800d6
--- /dev/null
+++ b/arch/arm/mach-tegra/tegra3_throttle.c
@@ -0,0 +1,367 @@
+/*
+ * arch/arm/mach-tegra/tegra3_throttle.c
+ *
+ * Copyright (c) 2011, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/cpufreq.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/uaccess.h>
+#include <linux/thermal.h>
+
+#include "clock.h"
+#include "cpu-tegra.h"
+#include "dvfs.h"
+
+/* tegra throttling require frequencies in the table to be in ascending order */
+static struct cpufreq_frequency_table *cpu_freq_table;
+static struct mutex *cpu_throttle_lock;
+
+static struct {
+ unsigned int cpu_freq;
+ int core_cap_level;
+ int ms;
+} throttle_table[] = {
+ { 0, 1000, 2000 }, /* placeholder for cpu floor rate */
+ { 640000, 1000, 2000 },
+ { 640000, 1000, 2000 },
+ { 640000, 1000, 2000 },
+ { 640000, 1000, 2000 },
+ { 640000, 1000, 2000 },
+ { 760000, 1000, 2000 },
+ { 760000, 1050, 2000 },
+ {1000000, 1050, 2000 },
+ {1000000, 1100, 2000 },
+};
+
+static int is_throttling;
+static int throttle_index;
+static struct delayed_work throttle_work;
+static struct workqueue_struct *workqueue;
+static DEFINE_MUTEX(tegra_throttle_lock);
+#ifdef CONFIG_TEGRA_THERMAL_SYSFS
+static struct thermal_cooling_device *cdev;
+#endif
+
+static unsigned int clip_to_table(unsigned int cpu_freq)
+{
+ int i;
+
+ for (i = 0; cpu_freq_table[i].frequency != CPUFREQ_TABLE_END; i++) {
+ if (cpu_freq_table[i].frequency > cpu_freq)
+ break;
+ }
+ i = (i == 0) ? 0 : i-1;
+ return cpu_freq_table[i].frequency;
+}
+
+static void tegra_throttle_work_func(struct work_struct *work)
+{
+ unsigned int cpu_freq;
+ int core_level;
+
+ mutex_lock(cpu_throttle_lock);
+ if (!is_throttling) {
+ mutex_unlock(cpu_throttle_lock);
+ return;
+ }
+
+ cpu_freq = tegra_getspeed(0);
+ throttle_index -= throttle_index ? 1 : 0;
+
+ core_level = throttle_table[throttle_index].core_cap_level;
+ if (throttle_table[throttle_index].cpu_freq < cpu_freq)
+ tegra_cpu_set_speed_cap(NULL);
+
+ if (throttle_index || (throttle_table[0].cpu_freq < cpu_freq))
+ queue_delayed_work(workqueue, &throttle_work,
+ msecs_to_jiffies(throttle_table[throttle_index].ms));
+
+ mutex_unlock(cpu_throttle_lock);
+
+ tegra_dvfs_core_cap_level_set(core_level);
+}
+
+/*
+ * tegra_throttling_enable
+ * This function may sleep
+ */
+void tegra_throttling_enable(bool enable)
+{
+ mutex_lock(&tegra_throttle_lock);
+ mutex_lock(cpu_throttle_lock);
+
+ if (enable && !(is_throttling++)) {
+ int core_level;
+ unsigned int cpu_freq = tegra_getspeed(0);
+ throttle_index = ARRAY_SIZE(throttle_table) - 1;
+
+ core_level = throttle_table[throttle_index].core_cap_level;
+ if (throttle_table[throttle_index].cpu_freq < cpu_freq)
+ tegra_cpu_set_speed_cap(NULL);
+
+ queue_delayed_work(workqueue, &throttle_work,
+ msecs_to_jiffies(throttle_table[throttle_index].ms));
+
+ mutex_unlock(cpu_throttle_lock);
+
+ tegra_dvfs_core_cap_level_set(core_level);
+ tegra_dvfs_core_cap_enable(true);
+
+ mutex_unlock(&tegra_throttle_lock);
+ return;
+ }
+
+ if (!enable && is_throttling) {
+ if (!(--is_throttling)) {
+ /* restore speed requested by governor */
+ tegra_cpu_set_speed_cap(NULL);
+ mutex_unlock(cpu_throttle_lock);
+
+ tegra_dvfs_core_cap_enable(false);
+ cancel_delayed_work_sync(&throttle_work);
+ mutex_unlock(&tegra_throttle_lock);
+ return;
+ }
+ }
+
+ mutex_unlock(cpu_throttle_lock);
+ mutex_unlock(&tegra_throttle_lock);
+}
+EXPORT_SYMBOL_GPL(tegra_throttling_enable);
+
+unsigned int tegra_throttle_governor_speed(unsigned int requested_speed)
+{
+ return is_throttling ?
+ min(requested_speed, throttle_table[throttle_index].cpu_freq) :
+ requested_speed;
+}
+
+bool tegra_is_throttling(void)
+{
+ return is_throttling;
+}
+
+#ifdef CONFIG_TEGRA_THERMAL_SYSFS
+
+static int
+tegra_throttle_get_max_state(struct thermal_cooling_device *cdev,
+ unsigned long *max_state)
+{
+ *max_state = ARRAY_SIZE(throttle_table);
+ return 0;
+}
+
+static int
+tegra_throttle_get_cur_state(struct thermal_cooling_device *cdev,
+ unsigned long *cur_state)
+{
+ mutex_lock(cpu_throttle_lock);
+ *cur_state = is_throttling ?
+ (ARRAY_SIZE(throttle_table) - throttle_index) :
+ 0;
+ mutex_unlock(cpu_throttle_lock);
+
+ return 0;
+}
+
+static int
+tegra_throttle_set_cur_state(struct thermal_cooling_device *cdev,
+ unsigned long cur_state)
+{
+ int core_level;
+
+ mutex_lock(cpu_throttle_lock);
+ if (cur_state == 0) {
+ /* restore speed requested by governor */
+ if (is_throttling) {
+ tegra_dvfs_core_cap_enable(false);
+ is_throttling = false;
+ }
+
+ tegra_cpu_set_speed_cap(NULL);
+ } else {
+ if (!is_throttling) {
+ tegra_dvfs_core_cap_enable(true);
+ is_throttling = true;
+ }
+
+ throttle_index = ARRAY_SIZE(throttle_table) - cur_state;
+ core_level = throttle_table[throttle_index].core_cap_level;
+ tegra_dvfs_core_cap_level_set(core_level);
+
+ tegra_cpu_set_speed_cap(NULL);
+ }
+
+ mutex_unlock(cpu_throttle_lock);
+
+ return 0;
+}
+
+struct thermal_cooling_device_ops tegra_throttle_cooling_ops = {
+ .get_max_state = tegra_throttle_get_max_state,
+ .get_cur_state = tegra_throttle_get_cur_state,
+ .set_cur_state = tegra_throttle_set_cur_state,
+};
+#endif
+
+int __init tegra_throttle_init(struct mutex *cpu_lock)
+{
+ int i;
+ struct tegra_cpufreq_table_data *table_data =
+ tegra_cpufreq_table_get();
+ if (IS_ERR_OR_NULL(table_data))
+ return -EINVAL;
+
+ /*
+ * High-priority, others flags default: not bound to a specific
+ * CPU, has rescue worker task (in case of allocation deadlock,
+ * etc.). Single-threaded.
+ */
+ workqueue = alloc_workqueue("cpu-tegra",
+ WQ_HIGHPRI | WQ_UNBOUND | WQ_RESCUER, 1);
+ if (!workqueue)
+ return -ENOMEM;
+ INIT_DELAYED_WORK(&throttle_work, tegra_throttle_work_func);
+
+ cpu_throttle_lock = cpu_lock;
+ cpu_freq_table = table_data->freq_table;
+ throttle_table[0].cpu_freq =
+ cpu_freq_table[table_data->throttle_lowest_index].frequency;
+
+ for (i = 0; i < ARRAY_SIZE(throttle_table); i++) {
+ unsigned int cpu_freq = throttle_table[i].cpu_freq;
+ throttle_table[i].cpu_freq = clip_to_table(cpu_freq);
+ }
+
+#ifdef CONFIG_TEGRA_THERMAL_SYSFS
+ cdev = thermal_cooling_device_register("Throttle", NULL,
+ &tegra_throttle_cooling_ops);
+
+ if (IS_ERR(cdev)) {
+ cdev = NULL;
+ return -ENODEV;
+ }
+#endif
+
+ return 0;
+}
+
+void tegra_throttle_exit(void)
+{
+#ifdef CONFIG_TEGRA_THERMAL_SYSFS
+ if (cdev) {
+ thermal_cooling_device_unregister(cdev);
+ cdev = NULL;
+ }
+#endif
+ destroy_workqueue(workqueue);
+}
+
+#ifdef CONFIG_DEBUG_FS
+
+static int throttle_debug_set(void *data, u64 val)
+{
+ tegra_throttling_enable(val);
+ return 0;
+}
+static int throttle_debug_get(void *data, u64 *val)
+{
+ *val = (u64) is_throttling;
+ return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(throttle_fops, throttle_debug_get, throttle_debug_set,
+ "%llu\n");
+static int table_show(struct seq_file *s, void *data)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(throttle_table); i++)
+ seq_printf(s, "[%d] = %7u %4d %5d\n",
+ i, throttle_table[i].cpu_freq,
+ throttle_table[i].core_cap_level, throttle_table[i].ms);
+ return 0;
+}
+
+static int table_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, table_show, inode->i_private);
+}
+
+static ssize_t table_write(struct file *file,
+ const char __user *userbuf, size_t count, loff_t *ppos)
+{
+ char buf[80];
+ int table_idx;
+ unsigned int cpu_freq;
+ int core_cap_level;
+ int ms;
+
+ if (sizeof(buf) <= count)
+ return -EINVAL;
+
+ if (copy_from_user(buf, userbuf, count))
+ return -EFAULT;
+
+ /* terminate buffer and trim - white spaces may be appended
+ * at the end when invoked from shell command line */
+ buf[count] = '\0';
+ strim(buf);
+
+ if (sscanf(buf, "[%d] = %u %d %d",
+ &table_idx, &cpu_freq, &core_cap_level, &ms) != 4)
+ return -1;
+
+ if ((table_idx < 0) || (table_idx >= ARRAY_SIZE(throttle_table)))
+ return -EINVAL;
+
+ /* round new settings before updating table */
+ throttle_table[table_idx].cpu_freq = clip_to_table(cpu_freq);
+ throttle_table[table_idx].core_cap_level = (core_cap_level / 50) * 50;
+ throttle_table[table_idx].ms = jiffies_to_msecs(msecs_to_jiffies(ms));
+
+ return count;
+}
+
+static const struct file_operations table_fops = {
+ .open = table_open,
+ .read = seq_read,
+ .write = table_write,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+
+int __init tegra_throttle_debug_init(struct dentry *cpu_tegra_debugfs_root)
+{
+ if (!debugfs_create_file("throttle", 0644, cpu_tegra_debugfs_root,
+ NULL, &throttle_fops))
+ return -ENOMEM;
+
+ if (!debugfs_create_file("throttle_table", 0644, cpu_tegra_debugfs_root,
+ NULL, &table_fops))
+ return -ENOMEM;
+
+ return 0;
+}
+#endif /* CONFIG_DEBUG_FS */
+
diff --git a/arch/arm/mach-tegra/tegra3_tsensor.c b/arch/arm/mach-tegra/tegra3_tsensor.c
new file mode 100644
index 000000000000..c498a2db2282
--- /dev/null
+++ b/arch/arm/mach-tegra/tegra3_tsensor.c
@@ -0,0 +1,133 @@
+/*
+ * arch/arm/mach-tegra/tegra3_tsensor.c
+ *
+ * Copyright (C) 2011 NVIDIA Corporation.
+ *
+ * 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/init.h>
+
+#ifdef CONFIG_SENSORS_TEGRA_TSENSOR
+#include <linux/io.h>
+#include <linux/ioport.h>
+
+#include <mach/iomap.h>
+#include <mach/tsensor.h>
+#include <mach/tegra_fuse.h>
+
+#include "board.h"
+#include "devices.h"
+#include "fuse.h"
+
+static struct tegra_tsensor_platform_data tsensor_data = {
+ .hysteresis = 5,
+ .sw_intr_temperature = 75,
+ .hw_clk_div_temperature = 80,
+ .hw_reset_temperature = 90,
+};
+
+/* fuse revision constants used for tsensor */
+#define TSENSOR_FUSE_REVISION_DECIMAL 8
+#define TSENSOR_FUSE_REVISION_INTEGER 0
+
+/* scratch register offsets needed for powering off PMU */
+#define SCRATCH54_OFFSET 0x258
+#define SCRATCH55_OFFSET 0x25C
+
+/* scratch 54 register bit field offsets */
+#define PMU_OFF_DATA_OFFSET 8
+
+/* scratch 55 register bit field offsets */
+#define RESET_TEGRA_OFFSET 31
+#define CONTROLLER_TYPE_OFFSET 30
+#define I2C_CONTROLLER_ID_OFFSET 27
+#define PINMUX_OFFSET 24
+#define CHECKSUM_OFFSET 16
+#define PMU_16BIT_SUPPORT_OFFSET 15
+/* scratch 55 register bit field masks */
+#define RESET_TEGRA_MASK 0x1
+#define CONTROLLER_TYPE_MASK 0x1
+#define I2C_CONTROLLER_ID_MASK 0x7
+#define PINMUX_MASK 0x7
+#define CHECKSUM_MASK 0xff
+#define PMU_16BIT_SUPPORT_MASK 0x1
+
+
+void __init tegra3_tsensor_init(struct tegra_tsensor_pmu_data *data)
+{
+ unsigned int reg;
+ int err;
+ u32 val, checksum;
+ void __iomem *pMem = NULL;
+ /* tsensor driver is instantiated based on fuse revision */
+ err = tegra_fuse_get_revision(&reg);
+ if (err)
+ goto labelEnd;
+ pr_info("\nTegra3 fuse revision %d ", reg);
+ if (reg < TSENSOR_FUSE_REVISION_DECIMAL)
+ goto labelEnd;
+
+ if (!data)
+ goto labelSkipPowerOff;
+
+ if (!request_mem_region(TEGRA_PMC_BASE +
+ SCRATCH54_OFFSET, 8, "tegra-tsensor"))
+ pr_err(" [%s, line=%d]: Error mem busy\n",
+ __func__, __LINE__);
+
+ pMem = ioremap(TEGRA_PMC_BASE + SCRATCH54_OFFSET, 8);
+ if (!pMem) {
+ pr_err(" [%s, line=%d]: can't ioremap "
+ "pmc iomem\n", __FILE__, __LINE__);
+ goto labelEnd;
+ }
+
+ /*
+ * Fill scratch registers to power off the device
+ * in case if temperature crosses threshold TH3
+ */
+ val = (data->poweroff_reg_data << PMU_OFF_DATA_OFFSET) |
+ data->poweroff_reg_addr;
+ writel(val, pMem);
+
+ val = ((data->reset_tegra & RESET_TEGRA_MASK) << RESET_TEGRA_OFFSET) |
+ ((data->controller_type & CONTROLLER_TYPE_MASK) <<
+ CONTROLLER_TYPE_OFFSET) |
+ ((data->i2c_controller_id & I2C_CONTROLLER_ID_MASK) <<
+ I2C_CONTROLLER_ID_OFFSET) |
+ ((data->pinmux & PINMUX_MASK) << PINMUX_OFFSET) |
+ ((data->pmu_16bit_ops & PMU_16BIT_SUPPORT_MASK) <<
+ PMU_16BIT_SUPPORT_OFFSET) | data->pmu_i2c_addr;
+
+ checksum = data->poweroff_reg_addr +
+ data->poweroff_reg_data + (val & 0xFF) +
+ ((val >> 8) & 0xFF) + ((val >> 24) & 0xFF);
+ checksum &= 0xFF;
+ checksum = 0x100 - checksum;
+
+ val |= (checksum << CHECKSUM_OFFSET);
+ writel(val, pMem + 4);
+
+labelSkipPowerOff:
+ /* set platform data for device before register */
+ tegra_tsensor_device.dev.platform_data = &tsensor_data;
+ platform_device_register(&tegra_tsensor_device);
+
+labelEnd:
+ return;
+}
+
+#else
+void __init tegra3_tsensor_init(void) { }
+#endif
+
diff --git a/arch/arm/mach-tegra/tegra_fiq_debugger.c b/arch/arm/mach-tegra/tegra_fiq_debugger.c
new file mode 100644
index 000000000000..2a19a214acb5
--- /dev/null
+++ b/arch/arm/mach-tegra/tegra_fiq_debugger.c
@@ -0,0 +1,206 @@
+/*
+ * arch/arm/mach-tegra/fiq_debugger.c
+ *
+ * Serial Debugger Interface for Tegra
+ *
+ * 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 <stdarg.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/irq.h>
+#include <linux/serial_reg.h>
+#include <linux/slab.h>
+#include <linux/stacktrace.h>
+#include <asm/fiq_debugger.h>
+#include <mach/tegra_fiq_debugger.h>
+#include <mach/system.h>
+#include <mach/fiq.h>
+
+#include <linux/uaccess.h>
+
+struct tegra_fiq_debugger {
+ struct fiq_debugger_pdata pdata;
+ void __iomem *debug_port_base;
+ bool break_seen;
+};
+
+static inline void tegra_write(struct tegra_fiq_debugger *t,
+ unsigned int val, unsigned int off)
+{
+ __raw_writeb(val, t->debug_port_base + off * 4);
+}
+
+static inline unsigned int tegra_read(struct tegra_fiq_debugger *t,
+ unsigned int off)
+{
+ return __raw_readb(t->debug_port_base + off * 4);
+}
+
+static inline unsigned int tegra_read_lsr(struct tegra_fiq_debugger *t)
+{
+ unsigned int lsr;
+
+ lsr = tegra_read(t, UART_LSR);
+ if (lsr & UART_LSR_BI)
+ t->break_seen = true;
+
+ return lsr;
+}
+
+static int debug_port_init(struct platform_device *pdev)
+{
+ struct tegra_fiq_debugger *t;
+ t = container_of(dev_get_platdata(&pdev->dev), typeof(*t), pdata);
+
+ if (tegra_read(t, UART_LSR) & UART_LSR_DR)
+ (void)tegra_read(t, UART_RX);
+ /* enable rx and lsr interrupt */
+ tegra_write(t, UART_IER_RLSI | UART_IER_RDI, UART_IER);
+ /* interrupt on every character */
+ tegra_write(t, 0, UART_IIR);
+
+ return 0;
+}
+
+static int debug_getc(struct platform_device *pdev)
+{
+ unsigned int lsr;
+ struct tegra_fiq_debugger *t;
+ t = container_of(dev_get_platdata(&pdev->dev), typeof(*t), pdata);
+
+ lsr = tegra_read_lsr(t);
+
+ if (lsr & UART_LSR_BI || t->break_seen) {
+ t->break_seen = false;
+ return FIQ_DEBUGGER_BREAK;
+ }
+
+ if (lsr & UART_LSR_DR)
+ return tegra_read(t, UART_RX);
+
+ return FIQ_DEBUGGER_NO_CHAR;
+}
+
+static void debug_putc(struct platform_device *pdev, unsigned int c)
+{
+ struct tegra_fiq_debugger *t;
+ t = container_of(dev_get_platdata(&pdev->dev), typeof(*t), pdata);
+
+ while (!(tegra_read_lsr(t) & UART_LSR_THRE))
+ cpu_relax();
+
+ tegra_write(t, c, UART_TX);
+}
+
+static void debug_flush(struct platform_device *pdev)
+{
+ struct tegra_fiq_debugger *t;
+ t = container_of(dev_get_platdata(&pdev->dev), typeof(*t), pdata);
+
+ while (!(tegra_read_lsr(t) & UART_LSR_TEMT))
+ cpu_relax();
+}
+
+static void fiq_enable(struct platform_device *pdev, unsigned int irq, bool on)
+{
+ if (on)
+ tegra_fiq_enable(irq);
+ else
+ tegra_fiq_disable(irq);
+}
+
+static int tegra_fiq_debugger_id;
+
+void tegra_serial_debug_init(unsigned int base, int irq,
+ struct clk *clk, int signal_irq, int wakeup_irq)
+{
+ struct tegra_fiq_debugger *t;
+ struct platform_device *pdev;
+ struct resource *res;
+ int res_count;
+
+ t = kzalloc(sizeof(struct tegra_fiq_debugger), GFP_KERNEL);
+ if (!t) {
+ pr_err("Failed to allocate for fiq debugger\n");
+ return;
+ }
+
+ t->pdata.uart_init = debug_port_init;
+ t->pdata.uart_getc = debug_getc;
+ t->pdata.uart_putc = debug_putc;
+ t->pdata.uart_flush = debug_flush;
+ t->pdata.fiq_enable = fiq_enable;
+
+ t->debug_port_base = ioremap(base, PAGE_SIZE);
+ if (!t->debug_port_base) {
+ pr_err("Failed to ioremap for fiq debugger\n");
+ goto out1;
+ }
+
+ res = kzalloc(sizeof(struct resource) * 3, GFP_KERNEL);
+ if (!res) {
+ pr_err("Failed to alloc fiq debugger resources\n");
+ goto out2;
+ }
+
+ pdev = kzalloc(sizeof(struct platform_device), GFP_KERNEL);
+ if (!pdev) {
+ pr_err("Failed to alloc fiq debugger platform device\n");
+ goto out3;
+ };
+
+ res[0].flags = IORESOURCE_IRQ;
+ res[0].start = irq;
+ res[0].end = irq;
+ res[0].name = "fiq";
+
+ res[1].flags = IORESOURCE_IRQ;
+ res[1].start = signal_irq;
+ res[1].end = signal_irq;
+ res[1].name = "signal";
+ res_count = 2;
+
+ if (wakeup_irq >= 0) {
+ res[2].flags = IORESOURCE_IRQ;
+ res[2].start = wakeup_irq;
+ res[2].end = wakeup_irq;
+ res[2].name = "wakeup";
+ res_count++;
+ }
+
+ pdev->name = "fiq_debugger";
+ pdev->id = tegra_fiq_debugger_id++;
+ pdev->dev.platform_data = &t->pdata;
+ pdev->resource = res;
+ pdev->num_resources = res_count;
+
+ if (platform_device_register(pdev)) {
+ pr_err("Failed to register fiq debugger\n");
+ goto out4;
+ }
+
+ return;
+
+out4:
+ kfree(pdev);
+out3:
+ kfree(res);
+out2:
+ iounmap(t->debug_port_base);
+out1:
+ kfree(t);
+}
diff --git a/arch/arm/mach-tegra/tegra_i2s_audio.c b/arch/arm/mach-tegra/tegra_i2s_audio.c
new file mode 100644
index 000000000000..fc790066694a
--- /dev/null
+++ b/arch/arm/mach-tegra/tegra_i2s_audio.c
@@ -0,0 +1,1965 @@
+/*
+ * arch/arm/mach-tegra/tegra_i2s_audio.c
+ *
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * Author:
+ * Iliyan Malchev <malchev@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.
+ *
+ */
+
+/* TODO:
+ -- replace make I2S_MAX_NUM_BUFS configurable through an ioctl
+*/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/fs.h>
+#include <linux/mutex.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/uaccess.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmapool.h>
+#include <linux/err.h>
+#include <linux/spi/spi.h>
+#include <linux/kfifo.h>
+#include <linux/debugfs.h>
+#include <linux/completion.h>
+#include <linux/platform_device.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/ktime.h>
+#include <linux/sysfs.h>
+#include <linux/wakelock.h>
+#include <linux/delay.h>
+#include <linux/tegra_audio.h>
+#include <linux/pm.h>
+#include <linux/workqueue.h>
+
+#include <mach/dma.h>
+#include <mach/iomap.h>
+#include <mach/i2s.h>
+#include <mach/audio.h>
+#include <mach/irqs.h>
+
+#include "clock.h"
+
+#define PCM_BUFFER_MAX_SIZE_ORDER PAGE_SHIFT
+
+#define TEGRA_AUDIO_DSP_NONE 0
+#define TEGRA_AUDIO_DSP_PCM 1
+#define TEGRA_AUDIO_DSP_NETWORK 2
+#define TEGRA_AUDIO_DSP_TDM 3
+
+#define I2S_MAX_NUM_BUFS 4
+#define I2S_DEFAULT_TX_NUM_BUFS 2
+#define I2S_DEFAULT_RX_NUM_BUFS 2
+
+/* per stream (input/output) */
+struct audio_stream {
+ int opened;
+ struct mutex lock;
+
+ bool active; /* is DMA in progress? */
+ int num_bufs;
+ void *buffer[I2S_MAX_NUM_BUFS];
+ dma_addr_t buf_phy[I2S_MAX_NUM_BUFS];
+ struct completion comp[I2S_MAX_NUM_BUFS];
+ struct tegra_dma_req dma_req[I2S_MAX_NUM_BUFS];
+ int last_queued;
+
+ int i2s_fifo_atn_level;
+
+ struct tegra_dma_channel *dma_chan;
+ bool stop;
+ struct completion stop_completion;
+ spinlock_t dma_req_lock;
+
+ struct work_struct allow_suspend_work;
+ struct wake_lock wake_lock;
+ char wake_lock_name[100];
+};
+
+/* per i2s controller */
+struct audio_driver_state {
+ struct list_head next;
+
+ struct platform_device *pdev;
+ struct tegra_audio_platform_data *pdata;
+ phys_addr_t i2s_phys;
+ unsigned long i2s_base;
+
+ unsigned long dma_req_sel;
+
+ int irq;
+ struct tegra_audio_in_config in_config;
+
+ struct miscdevice misc_out;
+ struct miscdevice misc_out_ctl;
+ struct audio_stream out;
+
+ struct miscdevice misc_in;
+ struct miscdevice misc_in_ctl;
+ struct audio_stream in;
+
+ /* Control for whole I2S (Data format, etc.) */
+ struct miscdevice misc_ctl;
+ unsigned int bit_format;
+};
+
+static inline bool pending_buffer_requests(struct audio_stream *stream)
+{
+ int i;
+ for (i = 0; i < stream->num_bufs; i++)
+ if (!completion_done(&stream->comp[i]))
+ return true;
+ return false;
+}
+
+static inline int buf_size(struct audio_stream *s __attribute__((unused)))
+{
+ return 1 << PCM_BUFFER_MAX_SIZE_ORDER;
+}
+
+static inline struct audio_driver_state *ads_from_misc_out(struct file *file)
+{
+ struct miscdevice *m = file->private_data;
+ struct audio_driver_state *ads =
+ container_of(m, struct audio_driver_state, misc_out);
+ BUG_ON(!ads);
+ return ads;
+}
+
+static inline struct audio_driver_state *ads_from_misc_out_ctl(
+ struct file *file)
+{
+ struct miscdevice *m = file->private_data;
+ struct audio_driver_state *ads =
+ container_of(m, struct audio_driver_state,
+ misc_out_ctl);
+ BUG_ON(!ads);
+ return ads;
+}
+
+static inline struct audio_driver_state *ads_from_misc_in(struct file *file)
+{
+ struct miscdevice *m = file->private_data;
+ struct audio_driver_state *ads =
+ container_of(m, struct audio_driver_state, misc_in);
+ BUG_ON(!ads);
+ return ads;
+}
+
+static inline struct audio_driver_state *ads_from_misc_in_ctl(
+ struct file *file)
+{
+ struct miscdevice *m = file->private_data;
+ struct audio_driver_state *ads =
+ container_of(m, struct audio_driver_state,
+ misc_in_ctl);
+ BUG_ON(!ads);
+ return ads;
+}
+
+static inline struct audio_driver_state *ads_from_misc_ctl(
+ struct file *file)
+{
+ struct miscdevice *m = file->private_data;
+ struct audio_driver_state *ads =
+ container_of(m, struct audio_driver_state,
+ misc_ctl);
+ BUG_ON(!ads);
+ return ads;
+}
+
+static inline struct audio_driver_state *ads_from_out(
+ struct audio_stream *aos)
+{
+ return container_of(aos, struct audio_driver_state, out);
+}
+
+static inline struct audio_driver_state *ads_from_in(
+ struct audio_stream *ais)
+{
+ return container_of(ais, struct audio_driver_state, in);
+}
+
+static inline void prevent_suspend(struct audio_stream *as)
+{
+ pr_debug("%s\n", __func__);
+ cancel_work_sync(&as->allow_suspend_work);
+ wake_lock(&as->wake_lock);
+}
+
+static void allow_suspend_worker(struct work_struct *w)
+{
+ struct audio_stream *as = container_of(w,
+ struct audio_stream, allow_suspend_work);
+ pr_debug("%s\n", __func__);
+ wake_unlock(&as->wake_lock);
+}
+
+static inline void allow_suspend(struct audio_stream *as)
+{
+ schedule_work(&as->allow_suspend_work);
+}
+
+#define I2S_I2S_FIFO_TX_BUSY I2S_I2S_STATUS_FIFO1_BSY
+#define I2S_I2S_FIFO_TX_QS I2S_I2S_STATUS_QS_FIFO1
+#define I2S_I2S_FIFO_TX_ERR I2S_I2S_STATUS_FIFO1_ERR
+
+#define I2S_I2S_FIFO_RX_BUSY I2S_I2S_STATUS_FIFO2_BSY
+#define I2S_I2S_FIFO_RX_QS I2S_I2S_STATUS_QS_FIFO2
+#define I2S_I2S_FIFO_RX_ERR I2S_I2S_STATUS_FIFO2_ERR
+
+#define I2S_FIFO_ERR (I2S_I2S_STATUS_FIFO1_ERR | I2S_I2S_STATUS_FIFO2_ERR)
+
+static inline void i2s_writel(unsigned long base, u32 val, u32 reg)
+{
+ writel(val, base + reg);
+}
+
+static inline u32 i2s_readl(unsigned long base, u32 reg)
+{
+ return readl(base + reg);
+}
+
+static inline void i2s_fifo_write(unsigned long base, int fifo, u32 data)
+{
+ i2s_writel(base, data, fifo ? I2S_I2S_FIFO2_0 : I2S_I2S_FIFO1_0);
+}
+
+static inline u32 i2s_fifo_read(unsigned long base, int fifo)
+{
+ return i2s_readl(base, fifo ? I2S_I2S_FIFO2_0 : I2S_I2S_FIFO1_0);
+}
+
+static int i2s_set_channel_bit_count(unsigned long base,
+ int sampling, int bitclk)
+{
+ u32 val;
+ int bitcnt = bitclk / (2 * sampling) - 1;
+
+ if (bitcnt < 0 || bitcnt >= 1<<11) {
+ pr_err("%s: bit count %d is out of bounds\n", __func__,
+ bitcnt);
+ return -EINVAL;
+ }
+
+ val = bitcnt;
+ if (bitclk % (2 * sampling)) {
+ pr_info("%s: enabling non-symmetric mode\n", __func__);
+ val |= I2S_I2S_TIMING_NON_SYM_ENABLE;
+ }
+
+ pr_debug("%s: I2S_I2S_TIMING_0 = %08x\n", __func__, val);
+ i2s_writel(base, val, I2S_I2S_TIMING_0);
+ return 0;
+}
+
+static void i2s_set_fifo_mode(unsigned long base, int fifo, int tx)
+{
+ u32 val = i2s_readl(base, I2S_I2S_CTRL_0);
+ if (fifo == 0) {
+ val &= ~I2S_I2S_CTRL_FIFO1_RX_ENABLE;
+ val |= (!tx) ? I2S_I2S_CTRL_FIFO1_RX_ENABLE : 0;
+ } else {
+ val &= ~I2S_I2S_CTRL_FIFO2_TX_ENABLE;
+ val |= tx ? I2S_I2S_CTRL_FIFO2_TX_ENABLE : 0;
+ }
+ i2s_writel(base, val, I2S_I2S_CTRL_0);
+}
+
+static int i2s_fifo_set_attention_level(unsigned long base,
+ int fifo, unsigned level)
+{
+ u32 val;
+
+ if (level > I2S_FIFO_ATN_LVL_TWELVE_SLOTS) {
+ pr_err("%s: invalid fifo level selector %d\n", __func__,
+ level);
+ return -EINVAL;
+ }
+
+ val = i2s_readl(base, I2S_I2S_FIFO_SCR_0);
+
+ if (!fifo) {
+ val &= ~I2S_I2S_FIFO_SCR_FIFO1_ATN_LVL_MASK;
+ val |= level << I2S_FIFO1_ATN_LVL_SHIFT;
+ } else {
+ val &= ~I2S_I2S_FIFO_SCR_FIFO2_ATN_LVL_MASK;
+ val |= level << I2S_FIFO2_ATN_LVL_SHIFT;
+ }
+
+ i2s_writel(base, val, I2S_I2S_FIFO_SCR_0);
+ return 0;
+}
+
+static void i2s_fifo_enable(unsigned long base, int fifo, int on)
+{
+ u32 val = i2s_readl(base, I2S_I2S_CTRL_0);
+ if (!fifo) {
+ val &= ~I2S_I2S_CTRL_FIFO1_ENABLE;
+ val |= on ? I2S_I2S_CTRL_FIFO1_ENABLE : 0;
+ } else {
+ val &= ~I2S_I2S_CTRL_FIFO2_ENABLE;
+ val |= on ? I2S_I2S_CTRL_FIFO2_ENABLE : 0;
+ }
+
+ i2s_writel(base, val, I2S_I2S_CTRL_0);
+}
+
+#if 0
+static bool i2s_is_fifo_enabled(unsigned long base, int fifo)
+{
+ u32 val = i2s_readl(base, I2S_I2S_CTRL_0);
+ if (!fifo)
+ return !!(val & I2S_I2S_CTRL_FIFO1_ENABLE);
+ return !!(val & I2S_I2S_CTRL_FIFO2_ENABLE);
+}
+#endif
+
+static void i2s_fifo_clear(unsigned long base, int fifo)
+{
+ u32 val = i2s_readl(base, I2S_I2S_FIFO_SCR_0);
+ if (!fifo) {
+ val &= ~I2S_I2S_FIFO_SCR_FIFO1_CLR;
+ val |= I2S_I2S_FIFO_SCR_FIFO1_CLR;
+#if 0
+ /* Per Nvidia, reduces pop on the next run. */
+ if (!(val & I2S_I2S_CTRL_FIFO1_RX_ENABLE)) {
+ int cnt = 16;
+ while (cnt--)
+ i2s_writel(base, 0, I2S_I2S_FIFO1_0);
+ }
+#endif
+ } else {
+ val &= ~I2S_I2S_FIFO_SCR_FIFO2_CLR;
+ val |= I2S_I2S_FIFO_SCR_FIFO2_CLR;
+ }
+
+ i2s_writel(base, val, I2S_I2S_FIFO_SCR_0);
+}
+
+static void i2s_set_master(unsigned long base, int master)
+{
+ u32 val = i2s_readl(base, I2S_I2S_CTRL_0);
+ val &= ~I2S_I2S_CTRL_MASTER_ENABLE;
+ val |= master ? I2S_I2S_CTRL_MASTER_ENABLE : 0;
+ i2s_writel(base, val, I2S_I2S_CTRL_0);
+}
+
+static int i2s_set_dsp_mode(unsigned long base, unsigned int mode)
+{
+ u32 val;
+ if (mode > TEGRA_AUDIO_DSP_TDM) {
+ pr_err("%s: invalid mode %d.\n", __func__, mode);
+ return -EINVAL;
+ }
+ if (mode == TEGRA_AUDIO_DSP_TDM) {
+ pr_err("TEGRA_AUDIO_DSP_TDM not implemented.\n");
+ return -EINVAL;
+ }
+
+ /* Disable unused modes */
+ if (mode != TEGRA_AUDIO_DSP_PCM) {
+ /* Disable PCM mode */
+ val = i2s_readl(base, I2S_I2S_PCM_CTRL_0);
+ val &= ~(I2S_I2S_PCM_CTRL_TRM_MODE |
+ I2S_I2S_PCM_CTRL_RCV_MODE);
+ i2s_writel(base, val, I2S_I2S_PCM_CTRL_0);
+ }
+ if (mode != TEGRA_AUDIO_DSP_NETWORK) {
+ /* Disable Network mode */
+ val = i2s_readl(base, I2S_I2S_NW_CTRL_0);
+ val &= ~(I2S_I2S_NW_CTRL_TRM_TLPHY_MODE |
+ I2S_I2S_NW_CTRL_RCV_TLPHY_MODE);
+ i2s_writel(base, val, I2S_I2S_NW_CTRL_0);
+ }
+
+ /* Enable the selected mode. */
+ switch (mode) {
+ case TEGRA_AUDIO_DSP_NETWORK:
+ /* Set DSP Network (Telephony) Mode */
+ val = i2s_readl(base, I2S_I2S_NW_CTRL_0);
+ val |= I2S_I2S_NW_CTRL_TRM_TLPHY_MODE |
+ I2S_I2S_NW_CTRL_RCV_TLPHY_MODE;
+ i2s_writel(base, val, I2S_I2S_NW_CTRL_0);
+ break;
+ case TEGRA_AUDIO_DSP_PCM:
+ /* Set DSP PCM Mode */
+ val = i2s_readl(base, I2S_I2S_PCM_CTRL_0);
+ val |= I2S_I2S_PCM_CTRL_TRM_MODE |
+ I2S_I2S_PCM_CTRL_RCV_MODE;
+ i2s_writel(base, val, I2S_I2S_PCM_CTRL_0);
+ break;
+ }
+
+ return 0;
+}
+
+static int i2s_set_bit_format(unsigned long base, unsigned fmt)
+{
+ u32 val;
+
+ if (fmt > I2S_BIT_FORMAT_DSP) {
+ pr_err("%s: invalid bit-format selector %d\n", __func__, fmt);
+ return -EINVAL;
+ }
+
+ val = i2s_readl(base, I2S_I2S_CTRL_0);
+ val &= ~I2S_I2S_CTRL_BIT_FORMAT_MASK;
+ val |= fmt << I2S_BIT_FORMAT_SHIFT;
+ i2s_writel(base, val, I2S_I2S_CTRL_0);
+ /* For DSP format, select DSP PCM mode. */
+ /* PCM mode and Network Mode slot 0 are effectively identical. */
+ if (fmt == I2S_BIT_FORMAT_DSP)
+ i2s_set_dsp_mode(base, TEGRA_AUDIO_DSP_PCM);
+ else
+ i2s_set_dsp_mode(base, TEGRA_AUDIO_DSP_NONE);
+
+ return 0;
+}
+
+static int i2s_set_bit_size(unsigned long base, unsigned bit_size)
+{
+ u32 val = i2s_readl(base, I2S_I2S_CTRL_0);
+ val &= ~I2S_I2S_CTRL_BIT_SIZE_MASK;
+
+ if (bit_size > I2S_BIT_SIZE_32) {
+ pr_err("%s: invalid bit_size selector %d\n", __func__,
+ bit_size);
+ return -EINVAL;
+ }
+
+ val |= bit_size << I2S_BIT_SIZE_SHIFT;
+
+ i2s_writel(base, val, I2S_I2S_CTRL_0);
+ return 0;
+}
+
+static int i2s_set_fifo_format(unsigned long base, unsigned fmt)
+{
+ u32 val = i2s_readl(base, I2S_I2S_CTRL_0);
+ val &= ~I2S_I2S_CTRL_FIFO_FORMAT_MASK;
+
+ if (fmt > I2S_FIFO_32 && fmt != I2S_FIFO_PACKED) {
+ pr_err("%s: invalid fmt selector %d\n", __func__, fmt);
+ return -EINVAL;
+ }
+
+ val |= fmt << I2S_FIFO_SHIFT;
+
+ i2s_writel(base, val, I2S_I2S_CTRL_0);
+ return 0;
+}
+
+static void i2s_set_left_right_control_polarity(unsigned long base,
+ int high_low)
+{
+ u32 val = i2s_readl(base, I2S_I2S_CTRL_0);
+ val &= ~I2S_I2S_CTRL_L_R_CTRL;
+ val |= high_low ? I2S_I2S_CTRL_L_R_CTRL : 0;
+ i2s_writel(base, val, I2S_I2S_CTRL_0);
+}
+
+#if 0
+static void i2s_set_fifo_irq_on_err(unsigned long base, int fifo, int on)
+{
+ u32 val = i2s_readl(base, I2S_I2S_CTRL_0);
+ if (!fifo) {
+ val &= ~I2S_I2S_IE_FIFO1_ERR;
+ val |= on ? I2S_I2S_IE_FIFO1_ERR : 0;
+ } else {
+ val &= ~I2S_I2S_IE_FIFO2_ERR;
+ val |= on ? I2S_I2S_IE_FIFO2_ERR : 0;
+ }
+ i2s_writel(base, val, I2S_I2S_CTRL_0);
+}
+
+static void i2s_set_fifo_irq_on_qe(unsigned long base, int fifo, int on)
+{
+ u32 val = i2s_readl(base, I2S_I2S_CTRL_0);
+ if (!fifo) {
+ val &= ~I2S_I2S_QE_FIFO1;
+ val |= on ? I2S_I2S_QE_FIFO1 : 0;
+ } else {
+ val &= ~I2S_I2S_QE_FIFO2;
+ val |= on ? I2S_I2S_QE_FIFO2 : 0;
+ }
+ i2s_writel(base, val, I2S_I2S_CTRL_0);
+}
+#endif
+
+static void i2s_enable_fifos(unsigned long base, int on)
+{
+ u32 val = i2s_readl(base, I2S_I2S_CTRL_0);
+ if (on)
+ val |= I2S_I2S_QE_FIFO1 | I2S_I2S_QE_FIFO2 |
+ I2S_I2S_IE_FIFO1_ERR | I2S_I2S_IE_FIFO2_ERR;
+ else
+ val &= ~(I2S_I2S_QE_FIFO1 | I2S_I2S_QE_FIFO2 |
+ I2S_I2S_IE_FIFO1_ERR | I2S_I2S_IE_FIFO2_ERR);
+
+ i2s_writel(base, val, I2S_I2S_CTRL_0);
+}
+
+static inline u32 i2s_get_status(unsigned long base)
+{
+ return i2s_readl(base, I2S_I2S_STATUS_0);
+}
+
+static inline u32 i2s_get_control(unsigned long base)
+{
+ return i2s_readl(base, I2S_I2S_CTRL_0);
+}
+
+static inline void i2s_ack_status(unsigned long base)
+{
+ return i2s_writel(base, i2s_readl(base, I2S_I2S_STATUS_0),
+ I2S_I2S_STATUS_0);
+}
+
+static inline u32 i2s_get_fifo_scr(unsigned long base)
+{
+ return i2s_readl(base, I2S_I2S_FIFO_SCR_0);
+}
+
+static inline phys_addr_t i2s_get_fifo_phy_base(unsigned long phy_base,
+ int fifo)
+{
+ return phy_base + (fifo ? I2S_I2S_FIFO2_0 : I2S_I2S_FIFO1_0);
+}
+
+static inline u32 i2s_get_fifo_full_empty_count(unsigned long base, int fifo)
+{
+ u32 val = i2s_readl(base, I2S_I2S_FIFO_SCR_0);
+
+ if (!fifo)
+ val = val >> I2S_I2S_FIFO_SCR_FIFO1_FULL_EMPTY_COUNT_SHIFT;
+ else
+ val = val >> I2S_I2S_FIFO_SCR_FIFO2_FULL_EMPTY_COUNT_SHIFT;
+
+ return val & I2S_I2S_FIFO_SCR_FIFO_FULL_EMPTY_COUNT_MASK;
+}
+
+static int i2s_configure(struct platform_device *pdev)
+{
+ struct tegra_audio_platform_data *pdata = pdev->dev.platform_data;
+ struct audio_driver_state *state = pdata->driver_data;
+ bool master;
+ struct clk *i2s_clk;
+ int master_clk;
+
+ /* dev_info(&pdev->dev, "%s\n", __func__); */
+
+ if (!state)
+ return -ENOMEM;
+
+ /* disable interrupts from I2S */
+ i2s_enable_fifos(state->i2s_base, 0);
+ i2s_fifo_clear(state->i2s_base, I2S_FIFO_TX);
+ i2s_fifo_clear(state->i2s_base, I2S_FIFO_RX);
+ i2s_set_left_right_control_polarity(state->i2s_base, 0); /* default */
+
+ i2s_clk = clk_get(&pdev->dev, NULL);
+ if (!i2s_clk) {
+ dev_err(&pdev->dev, "%s: could not get i2s clock\n",
+ __func__);
+ return -EIO;
+ }
+
+ master = state->bit_format == TEGRA_AUDIO_BIT_FORMAT_DSP ?
+ state->pdata->dsp_master : state->pdata->i2s_master;
+
+
+ master_clk = state->bit_format == TEGRA_AUDIO_BIT_FORMAT_DSP ?
+ state->pdata->dsp_master_clk :
+ state->pdata->i2s_master_clk;
+#define I2S_CLK_TO_BITCLK_RATIO 2 /* Todo, Bitclk based on 2X clock? */
+ if (master)
+ i2s_set_channel_bit_count(state->i2s_base, master_clk,
+ clk_get_rate(i2s_clk)*I2S_CLK_TO_BITCLK_RATIO);
+ i2s_set_master(state->i2s_base, master);
+
+ i2s_set_fifo_mode(state->i2s_base, I2S_FIFO_TX, 1);
+ i2s_set_fifo_mode(state->i2s_base, I2S_FIFO_RX, 0);
+
+ if (state->bit_format == TEGRA_AUDIO_BIT_FORMAT_DSP)
+ i2s_set_bit_format(state->i2s_base, I2S_BIT_FORMAT_DSP);
+ else
+ i2s_set_bit_format(state->i2s_base, state->pdata->mode);
+ i2s_set_bit_size(state->i2s_base, state->pdata->bit_size);
+ i2s_set_fifo_format(state->i2s_base, state->pdata->fifo_fmt);
+
+ return 0;
+}
+
+static int init_stream_buffer(struct audio_stream *, int);
+
+static int setup_dma(struct audio_driver_state *, int);
+static void tear_down_dma(struct audio_driver_state *, int);
+static void stop_dma_playback(struct audio_stream *);
+static int start_dma_recording(struct audio_stream *, int);
+static void stop_dma_recording(struct audio_stream *);
+
+struct sound_ops {
+ int (*setup)(struct audio_driver_state *, int);
+ void (*tear_down)(struct audio_driver_state *, int);
+ void (*stop_playback)(struct audio_stream *);
+ int (*start_recording)(struct audio_stream *, int);
+ void (*stop_recording)(struct audio_stream *);
+};
+
+static const struct sound_ops dma_sound_ops = {
+ .setup = setup_dma,
+ .tear_down = tear_down_dma,
+ .stop_playback = stop_dma_playback,
+ .start_recording = start_dma_recording,
+ .stop_recording = stop_dma_recording,
+};
+
+static const struct sound_ops *sound_ops = &dma_sound_ops;
+
+static int start_recording_if_necessary(struct audio_stream *ais, int size)
+{
+ int rc = 0;
+ unsigned long flags;
+ prevent_suspend(ais);
+ spin_lock_irqsave(&ais->dma_req_lock, flags);
+ if (!ais->stop && !pending_buffer_requests(ais)) {
+ /* pr_debug("%s: starting recording\n", __func__); */
+ rc = sound_ops->start_recording(ais, size);
+ if (rc) {
+ pr_err("%s start_recording() failed\n", __func__);
+ allow_suspend(ais);
+ }
+ }
+ spin_unlock_irqrestore(&ais->dma_req_lock, flags);
+ return rc;
+}
+
+static bool stop_playback_if_necessary(struct audio_stream *aos)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&aos->dma_req_lock, flags);
+ pr_debug("%s\n", __func__);
+ if (!pending_buffer_requests(aos)) {
+ pr_debug("%s: no more data to play back\n", __func__);
+ sound_ops->stop_playback(aos);
+ spin_unlock_irqrestore(&aos->dma_req_lock, flags);
+ allow_suspend(aos);
+ return true;
+ }
+ spin_unlock_irqrestore(&aos->dma_req_lock, flags);
+
+ return false;
+}
+
+/* playback and recording */
+static bool wait_till_stopped(struct audio_stream *as)
+{
+ int rc;
+ pr_debug("%s: wait for completion\n", __func__);
+ rc = wait_for_completion_timeout(
+ &as->stop_completion, HZ);
+ if (!rc)
+ pr_err("%s: wait timed out", __func__);
+ if (rc < 0)
+ pr_err("%s: wait error %d\n", __func__, rc);
+ allow_suspend(as);
+ pr_debug("%s: done: %d\n", __func__, rc);
+ return true;
+}
+
+/* Ask for playback and recording to stop. The _nosync means that
+ * as->lock has to be locked by the caller.
+ */
+static void request_stop_nosync(struct audio_stream *as)
+{
+ int i;
+ pr_debug("%s\n", __func__);
+ if (!as->stop) {
+ as->stop = true;
+ if (pending_buffer_requests(as))
+ wait_till_stopped(as);
+ for (i = 0; i < as->num_bufs; i++) {
+ init_completion(&as->comp[i]);
+ complete(&as->comp[i]);
+ }
+ }
+ if (!tegra_dma_is_empty(as->dma_chan))
+ pr_err("%s: DMA not empty!\n", __func__);
+ /* Stop the DMA then dequeue anything that's in progress. */
+ tegra_dma_cancel(as->dma_chan);
+ as->active = false; /* applies to recording only */
+ pr_debug("%s: done\n", __func__);
+}
+
+static void setup_dma_tx_request(struct tegra_dma_req *req,
+ struct audio_stream *aos);
+
+static void setup_dma_rx_request(struct tegra_dma_req *req,
+ struct audio_stream *ais);
+
+static int setup_dma(struct audio_driver_state *ads, int mask)
+{
+ int rc, i;
+ pr_info("%s\n", __func__);
+
+ if (mask & TEGRA_AUDIO_ENABLE_TX) {
+ /* setup audio playback */
+ for (i = 0; i < ads->out.num_bufs; i++) {
+ ads->out.buf_phy[i] = dma_map_single(&ads->pdev->dev,
+ ads->out.buffer[i],
+ 1 << PCM_BUFFER_MAX_SIZE_ORDER,
+ DMA_TO_DEVICE);
+ BUG_ON(!ads->out.buf_phy[i]);
+ setup_dma_tx_request(&ads->out.dma_req[i], &ads->out);
+ ads->out.dma_req[i].source_addr = ads->out.buf_phy[i];
+ }
+ ads->out.dma_chan = tegra_dma_allocate_channel(
+ TEGRA_DMA_MODE_CONTINUOUS_SINGLE,
+ "i2s_tx_req_%d", ads->dma_req_sel);
+ if (!ads->out.dma_chan) {
+ pr_err("%s: error alloc output DMA channel: %ld\n",
+ __func__, PTR_ERR(ads->out.dma_chan));
+ rc = -ENODEV;
+ goto fail_tx;
+ }
+ }
+
+ if (mask & TEGRA_AUDIO_ENABLE_RX) {
+ /* setup audio recording */
+ for (i = 0; i < ads->in.num_bufs; i++) {
+ ads->in.buf_phy[i] = dma_map_single(&ads->pdev->dev,
+ ads->in.buffer[i],
+ 1 << PCM_BUFFER_MAX_SIZE_ORDER,
+ DMA_FROM_DEVICE);
+ BUG_ON(!ads->in.buf_phy[i]);
+ setup_dma_rx_request(&ads->in.dma_req[i], &ads->in);
+ ads->in.dma_req[i].dest_addr = ads->in.buf_phy[i];
+ }
+ ads->in.dma_chan = tegra_dma_allocate_channel(
+ TEGRA_DMA_MODE_CONTINUOUS_SINGLE,
+ "i2s_rx_req_%d", ads->dma_req_sel);
+ if (!ads->in.dma_chan) {
+ pr_err("%s: error allocating input DMA channel: %ld\n",
+ __func__, PTR_ERR(ads->in.dma_chan));
+ rc = -ENODEV;
+ goto fail_rx;
+ }
+ }
+
+ return 0;
+
+fail_rx:
+ if (mask & TEGRA_AUDIO_ENABLE_RX) {
+ for (i = 0; i < ads->in.num_bufs; i++) {
+ dma_unmap_single(&ads->pdev->dev, ads->in.buf_phy[i],
+ 1 << PCM_BUFFER_MAX_SIZE_ORDER,
+ DMA_FROM_DEVICE);
+ ads->in.buf_phy[i] = 0;
+ }
+ tegra_dma_free_channel(ads->in.dma_chan);
+ ads->in.dma_chan = 0;
+ }
+fail_tx:
+ if (mask & TEGRA_AUDIO_ENABLE_TX) {
+ for (i = 0; i < ads->out.num_bufs; i++) {
+ dma_unmap_single(&ads->pdev->dev, ads->out.buf_phy[i],
+ 1 << PCM_BUFFER_MAX_SIZE_ORDER,
+ DMA_TO_DEVICE);
+ ads->out.buf_phy[i] = 0;
+ }
+ tegra_dma_free_channel(ads->out.dma_chan);
+ ads->out.dma_chan = 0;
+ }
+
+ return rc;
+}
+
+static void tear_down_dma(struct audio_driver_state *ads, int mask)
+{
+ int i;
+ pr_info("%s\n", __func__);
+
+ if (mask & TEGRA_AUDIO_ENABLE_TX) {
+ tegra_dma_free_channel(ads->out.dma_chan);
+ for (i = 0; i < ads->out.num_bufs; i++) {
+ dma_unmap_single(&ads->pdev->dev, ads->out.buf_phy[i],
+ buf_size(&ads->out),
+ DMA_TO_DEVICE);
+ ads->out.buf_phy[i] = 0;
+ }
+ }
+ ads->out.dma_chan = NULL;
+
+ if (mask & TEGRA_AUDIO_ENABLE_RX) {
+ tegra_dma_free_channel(ads->in.dma_chan);
+ for (i = 0; i < ads->in.num_bufs; i++) {
+ dma_unmap_single(&ads->pdev->dev, ads->in.buf_phy[i],
+ buf_size(&ads->in),
+ DMA_FROM_DEVICE);
+ ads->in.buf_phy[i] = 0;
+ }
+ }
+ ads->in.dma_chan = NULL;
+}
+
+static void dma_tx_complete_callback(struct tegra_dma_req *req)
+{
+ unsigned long flags;
+ struct audio_stream *aos = req->dev;
+ unsigned req_num;
+
+ spin_lock_irqsave(&aos->dma_req_lock, flags);
+
+ req_num = req - aos->dma_req;
+ pr_debug("%s: completed buffer %d size %d\n", __func__,
+ req_num, req->bytes_transferred);
+ BUG_ON(req_num >= aos->num_bufs);
+
+ complete(&aos->comp[req_num]);
+
+ if (!pending_buffer_requests(aos)) {
+ pr_debug("%s: Playback underflow\n", __func__);
+ complete(&aos->stop_completion);
+ }
+
+ spin_unlock_irqrestore(&aos->dma_req_lock, flags);
+}
+
+static void dma_rx_complete_callback(struct tegra_dma_req *req)
+{
+ unsigned long flags;
+ struct audio_stream *ais = req->dev;
+ unsigned req_num;
+
+ spin_lock_irqsave(&ais->dma_req_lock, flags);
+
+ req_num = req - ais->dma_req;
+ pr_debug("%s: completed buffer %d size %d\n", __func__,
+ req_num, req->bytes_transferred);
+ BUG_ON(req_num >= ais->num_bufs);
+
+ complete(&ais->comp[req_num]);
+
+ if (!pending_buffer_requests(ais))
+ pr_debug("%s: Capture overflow\n", __func__);
+
+ spin_unlock_irqrestore(&ais->dma_req_lock, flags);
+}
+
+static void setup_dma_tx_request(struct tegra_dma_req *req,
+ struct audio_stream *aos)
+{
+ struct audio_driver_state *ads = ads_from_out(aos);
+
+ memset(req, 0, sizeof(*req));
+
+ req->complete = dma_tx_complete_callback;
+ req->dev = aos;
+ req->to_memory = false;
+ req->dest_addr = i2s_get_fifo_phy_base(ads->i2s_phys, I2S_FIFO_TX);
+ req->dest_wrap = 4;
+ if (ads->bit_format == TEGRA_AUDIO_BIT_FORMAT_DSP)
+ req->dest_bus_width = ads->pdata->dsp_bus_width;
+ else
+ req->dest_bus_width = ads->pdata->i2s_bus_width;
+ req->source_bus_width = 32;
+ req->source_wrap = 0;
+ req->req_sel = ads->dma_req_sel;
+}
+
+static void setup_dma_rx_request(struct tegra_dma_req *req,
+ struct audio_stream *ais)
+{
+ struct audio_driver_state *ads = ads_from_in(ais);
+
+ memset(req, 0, sizeof(*req));
+
+ req->complete = dma_rx_complete_callback;
+ req->dev = ais;
+ req->to_memory = true;
+ req->source_addr = i2s_get_fifo_phy_base(ads->i2s_phys, I2S_FIFO_RX);
+ req->source_wrap = 4;
+ if (ads->bit_format == TEGRA_AUDIO_BIT_FORMAT_DSP)
+ req->source_bus_width = ads->pdata->dsp_bus_width;
+ else
+ req->source_bus_width = ads->pdata->i2s_bus_width;
+ req->dest_bus_width = 32;
+ req->dest_wrap = 0;
+ req->req_sel = ads->dma_req_sel;
+}
+
+static int start_playback(struct audio_stream *aos,
+ struct tegra_dma_req *req)
+{
+ int rc;
+ unsigned long flags;
+ struct audio_driver_state *ads = ads_from_out(aos);
+
+ pr_debug("%s: (writing %d)\n",
+ __func__, req->size);
+
+ spin_lock_irqsave(&aos->dma_req_lock, flags);
+#if 0
+ i2s_fifo_clear(ads->i2s_base, I2S_FIFO_TX);
+#endif
+ i2s_fifo_set_attention_level(ads->i2s_base,
+ I2S_FIFO_TX, aos->i2s_fifo_atn_level);
+
+ i2s_fifo_enable(ads->i2s_base, I2S_FIFO_TX, 1);
+
+ rc = tegra_dma_enqueue_req(aos->dma_chan, req);
+ spin_unlock_irqrestore(&aos->dma_req_lock, flags);
+
+ if (rc)
+ pr_err("%s: could not enqueue TX DMA req\n", __func__);
+ return rc;
+}
+
+/* Called with aos->dma_req_lock taken. */
+static void stop_dma_playback(struct audio_stream *aos)
+{
+ int spin = 0;
+ struct audio_driver_state *ads = ads_from_out(aos);
+ pr_debug("%s\n", __func__);
+ i2s_fifo_enable(ads->i2s_base, I2S_FIFO_TX, 0);
+ while ((i2s_get_status(ads->i2s_base) & I2S_I2S_FIFO_TX_BUSY) &&
+ spin < 100) {
+ udelay(10);
+ if (spin++ > 50)
+ pr_info("%s: spin %d\n", __func__, spin);
+ }
+ if (spin == 100)
+ pr_warn("%s: spinny\n", __func__);
+}
+
+/* This function may be called from either interrupt or process context. */
+/* Called with ais->dma_req_lock taken. */
+static int start_dma_recording(struct audio_stream *ais, int size)
+{
+ int i;
+ struct audio_driver_state *ads = ads_from_in(ais);
+
+ pr_debug("%s\n", __func__);
+
+ BUG_ON(pending_buffer_requests(ais));
+
+ for (i = 0; i < ais->num_bufs; i++) {
+ init_completion(&ais->comp[i]);
+ ais->dma_req[i].dest_addr = ais->buf_phy[i];
+ ais->dma_req[i].size = size;
+ tegra_dma_enqueue_req(ais->dma_chan, &ais->dma_req[i]);
+ }
+
+ ais->last_queued = ais->num_bufs - 1;
+
+#if 0
+ i2s_fifo_clear(ads->i2s_base, I2S_FIFO_RX);
+#endif
+ i2s_fifo_set_attention_level(ads->i2s_base,
+ I2S_FIFO_RX, ais->i2s_fifo_atn_level);
+ i2s_fifo_enable(ads->i2s_base, I2S_FIFO_RX, 1);
+ return 0;
+}
+
+static void stop_dma_recording(struct audio_stream *ais)
+{
+ int spin = 0;
+ struct audio_driver_state *ads = ads_from_in(ais);
+ pr_debug("%s\n", __func__);
+ tegra_dma_cancel(ais->dma_chan);
+ i2s_fifo_enable(ads->i2s_base, I2S_FIFO_RX, 0);
+ i2s_fifo_clear(ads->i2s_base, I2S_FIFO_RX);
+ while ((i2s_get_status(ads->i2s_base) & I2S_I2S_FIFO_RX_BUSY) &&
+ spin < 100) {
+ udelay(10);
+ if (spin++ > 50)
+ pr_info("%s: spin %d\n", __func__, spin);
+ }
+ if (spin == 100)
+ pr_warn("%s: spinny\n", __func__);
+}
+
+static irqreturn_t i2s_interrupt(int irq, void *data)
+{
+ struct audio_driver_state *ads = data;
+ u32 status = i2s_get_status(ads->i2s_base);
+
+ pr_debug("%s: %08x\n", __func__, status);
+
+ if (status & I2S_FIFO_ERR)
+ i2s_ack_status(ads->i2s_base);
+
+ pr_debug("%s: done %08x\n", __func__, i2s_get_status(ads->i2s_base));
+ return IRQ_HANDLED;
+}
+
+static ssize_t tegra_audio_write(struct file *file,
+ const char __user *buf, size_t size, loff_t *off)
+{
+ ssize_t rc = 0;
+ int out_buf;
+ struct tegra_dma_req *req;
+ struct audio_driver_state *ads = ads_from_misc_out(file);
+
+ mutex_lock(&ads->out.lock);
+
+ if (!IS_ALIGNED(size, 4) || size < 4 || size > buf_size(&ads->out)) {
+ pr_err("%s: invalid user size %d\n", __func__, size);
+ rc = -EINVAL;
+ goto done;
+ }
+
+ pr_debug("%s: write %d bytes\n", __func__, size);
+
+ if (ads->out.stop) {
+ pr_debug("%s: playback has been cancelled\n", __func__);
+ goto done;
+ }
+
+ /* Decide which buf is next. */
+ out_buf = (ads->out.last_queued + 1) % ads->out.num_bufs;
+ req = &ads->out.dma_req[out_buf];
+
+ /* Wait for the buffer to be emptied (complete). The maximum timeout
+ * value could be calculated dynamically based on buf_size(&ads->out).
+ * For a buffer size of 16k, at 44.1kHz/stereo/16-bit PCM, you would
+ * have ~93ms.
+ */
+ pr_debug("%s: waiting for buffer %d\n", __func__, out_buf);
+ rc = wait_for_completion_interruptible_timeout(
+ &ads->out.comp[out_buf], HZ);
+ if (!rc) {
+ pr_err("%s: timeout", __func__);
+ rc = -ETIMEDOUT;
+ goto done;
+ } else if (rc < 0) {
+ pr_err("%s: wait error %d", __func__, rc);
+ goto done;
+ }
+
+ /* Fill the buffer and enqueue it. */
+ pr_debug("%s: acquired buffer %d, copying data\n", __func__, out_buf);
+ rc = copy_from_user(ads->out.buffer[out_buf], buf, size);
+ if (rc) {
+ rc = -EFAULT;
+ goto done;
+ }
+
+ prevent_suspend(&ads->out);
+
+ req->size = size;
+ dma_sync_single_for_device(NULL,
+ req->source_addr, req->size, DMA_TO_DEVICE);
+ ads->out.last_queued = out_buf;
+ init_completion(&ads->out.stop_completion);
+
+ rc = start_playback(&ads->out, req);
+ if (!rc)
+ rc = size;
+ else
+ allow_suspend(&ads->out);
+
+done:
+ mutex_unlock(&ads->out.lock);
+ return rc;
+}
+
+static long tegra_audio_out_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ int rc = 0;
+ struct audio_driver_state *ads = ads_from_misc_out_ctl(file);
+ struct audio_stream *aos = &ads->out;
+
+ mutex_lock(&aos->lock);
+
+ switch (cmd) {
+ case TEGRA_AUDIO_OUT_FLUSH:
+ if (pending_buffer_requests(aos)) {
+ pr_debug("%s: flushing\n", __func__);
+ request_stop_nosync(aos);
+ pr_debug("%s: flushed\n", __func__);
+ }
+ if (stop_playback_if_necessary(aos))
+ pr_debug("%s: done (stopped)\n", __func__);
+ aos->stop = false;
+ break;
+ case TEGRA_AUDIO_OUT_SET_NUM_BUFS: {
+ unsigned int num;
+ if (copy_from_user(&num, (const void __user *)arg,
+ sizeof(num))) {
+ rc = -EFAULT;
+ break;
+ }
+ if (!num || num > I2S_MAX_NUM_BUFS) {
+ pr_err("%s: invalid buffer count %d\n", __func__, num);
+ rc = -EINVAL;
+ break;
+ }
+ if (pending_buffer_requests(aos)) {
+ pr_err("%s: playback in progress\n", __func__);
+ rc = -EBUSY;
+ break;
+ }
+ rc = init_stream_buffer(aos, num);
+ if (rc < 0)
+ break;
+ aos->num_bufs = num;
+ sound_ops->tear_down(ads, TEGRA_AUDIO_ENABLE_TX);
+ sound_ops->setup(ads, TEGRA_AUDIO_ENABLE_TX);
+ pr_debug("%s: num buf set to %d\n", __func__, num);
+ }
+ break;
+ case TEGRA_AUDIO_OUT_GET_NUM_BUFS:
+ if (copy_to_user((void __user *)arg,
+ &aos->num_bufs, sizeof(aos->num_bufs)))
+ rc = -EFAULT;
+ break;
+ default:
+ rc = -EINVAL;
+ }
+
+ mutex_unlock(&aos->lock);
+ return rc;
+}
+
+static long tegra_audio_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ int rc = 0;
+ struct audio_driver_state *ads = ads_from_misc_ctl(file);
+ unsigned int mode;
+ bool dma_restart = false;
+
+ mutex_lock(&ads->out.lock);
+ mutex_lock(&ads->in.lock);
+
+ switch (cmd) {
+ case TEGRA_AUDIO_SET_BIT_FORMAT:
+ if (copy_from_user(&mode, (const void __user *)arg,
+ sizeof(mode))) {
+ rc = -EFAULT;
+ goto done;
+ }
+ dma_restart = (mode != ads->bit_format);
+ switch (mode) {
+ case TEGRA_AUDIO_BIT_FORMAT_DEFAULT:
+ i2s_set_bit_format(ads->i2s_base, ads->pdata->mode);
+ ads->bit_format = mode;
+ break;
+ case TEGRA_AUDIO_BIT_FORMAT_DSP:
+ i2s_set_bit_format(ads->i2s_base, I2S_BIT_FORMAT_DSP);
+ ads->bit_format = mode;
+ break;
+ default:
+ pr_err("%s: Invald PCM mode %d", __func__, mode);
+ rc = -EINVAL;
+ goto done;
+ }
+ break;
+ case TEGRA_AUDIO_GET_BIT_FORMAT:
+ if (copy_to_user((void __user *)arg, &ads->bit_format,
+ sizeof(mode)))
+ rc = -EFAULT;
+ goto done;
+ }
+
+ if (dma_restart) {
+ pr_debug("%s: Restarting DMA due to configuration change.\n",
+ __func__);
+ if (pending_buffer_requests(&ads->out) || ads->in.active) {
+ pr_err("%s: dma busy, cannot restart.\n", __func__);
+ rc = -EBUSY;
+ goto done;
+ }
+ sound_ops->tear_down(ads, ads->pdata->mask);
+ i2s_configure(ads->pdev);
+ sound_ops->setup(ads, ads->pdata->mask);
+ }
+
+done:
+ mutex_unlock(&ads->in.lock);
+ mutex_unlock(&ads->out.lock);
+ return rc;
+}
+
+static long tegra_audio_in_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ int rc = 0;
+ struct audio_driver_state *ads = ads_from_misc_in_ctl(file);
+ struct audio_stream *ais = &ads->in;
+
+ mutex_lock(&ais->lock);
+
+ switch (cmd) {
+ case TEGRA_AUDIO_IN_START:
+ pr_debug("%s: start recording\n", __func__);
+ ais->stop = false;
+ break;
+ case TEGRA_AUDIO_IN_STOP:
+ pr_debug("%s: stop recording\n", __func__);
+ if (ais->active) {
+ /* Clean up DMA/I2S, and complete the completion */
+ sound_ops->stop_recording(ais);
+ complete(&ais->stop_completion);
+ /* Set stop flag and allow suspend. */
+ request_stop_nosync(ais);
+ }
+ break;
+ case TEGRA_AUDIO_IN_SET_CONFIG: {
+ struct tegra_audio_in_config cfg;
+
+ if (ais->active) {
+ pr_err("%s: recording in progress\n", __func__);
+ rc = -EBUSY;
+ break;
+ }
+ if (copy_from_user(&cfg, (const void __user *)arg,
+ sizeof(cfg))) {
+ rc = -EFAULT;
+ break;
+ }
+
+ if (cfg.stereo && !ads->pdata->stereo_capture) {
+ pr_err("%s: not capable of stereo capture.",
+ __func__);
+ rc = -EINVAL;
+ }
+ if (!rc) {
+ pr_info("%s: setting input sampling rate to %d, %s\n",
+ __func__, cfg.rate,
+ cfg.stereo ? "stereo" : "mono");
+ ads->in_config = cfg;
+ ads->in_config.stereo = !!ads->in_config.stereo;
+ }
+ }
+ break;
+ case TEGRA_AUDIO_IN_GET_CONFIG:
+ if (copy_to_user((void __user *)arg, &ads->in_config,
+ sizeof(ads->in_config)))
+ rc = -EFAULT;
+ break;
+ case TEGRA_AUDIO_IN_SET_NUM_BUFS: {
+ unsigned int num;
+ if (copy_from_user(&num, (const void __user *)arg,
+ sizeof(num))) {
+ rc = -EFAULT;
+ break;
+ }
+ if (!num || num > I2S_MAX_NUM_BUFS) {
+ pr_err("%s: invalid buffer count %d\n", __func__,
+ num);
+ rc = -EINVAL;
+ break;
+ }
+ if (ais->active || pending_buffer_requests(ais)) {
+ pr_err("%s: recording in progress\n", __func__);
+ rc = -EBUSY;
+ break;
+ }
+ rc = init_stream_buffer(ais, num);
+ if (rc < 0)
+ break;
+ ais->num_bufs = num;
+ sound_ops->tear_down(ads, TEGRA_AUDIO_ENABLE_RX);
+ sound_ops->setup(ads, TEGRA_AUDIO_ENABLE_RX);
+ }
+ break;
+ case TEGRA_AUDIO_IN_GET_NUM_BUFS:
+ if (copy_to_user((void __user *)arg,
+ &ais->num_bufs, sizeof(ais->num_bufs)))
+ rc = -EFAULT;
+ break;
+ default:
+ rc = -EINVAL;
+ }
+
+ mutex_unlock(&ais->lock);
+ return rc;
+}
+
+static ssize_t tegra_audio_read(struct file *file, char __user *buf,
+ size_t size, loff_t *off)
+{
+ ssize_t rc;
+ ssize_t nr = 0;
+ int in_buf;
+ struct tegra_dma_req *req;
+ struct audio_driver_state *ads = ads_from_misc_in(file);
+
+ mutex_lock(&ads->in.lock);
+
+ if (!IS_ALIGNED(size, 4) || size < 4 || size > buf_size(&ads->in)) {
+ pr_err("%s: invalid size %d.\n", __func__, size);
+ rc = -EINVAL;
+ goto done;
+ }
+
+ pr_debug("%s: size %d\n", __func__, size);
+
+ /* If we want recording to stop immediately after it gets cancelled,
+ * then we do not want to wait for the fifo to get drained.
+ */
+ if (ads->in.stop) {
+ pr_debug("%s: recording has been cancelled\n", __func__);
+ rc = 0;
+ goto done;
+ }
+
+ /* This function calls prevent_suspend() internally */
+ rc = start_recording_if_necessary(&ads->in, size);
+ if (rc < 0 && rc != -EALREADY) {
+ pr_err("%s: could not start recording\n", __func__);
+ goto done;
+ }
+
+ ads->in.active = true;
+
+ /* Note that when tegra_audio_read() is called for the first time (or
+ * when all the buffers are empty), then it queues up all
+ * ads->in.num_bufs buffers, and in_buf is set to zero below.
+ */
+ in_buf = (ads->in.last_queued + 1) % ads->in.num_bufs;
+
+ /* Wait for the buffer to be filled (complete). The maximum timeout
+ * value could be calculated dynamically based on buf_size(&ads->in).
+ * For a buffer size of 16k, at 44.1kHz/stereo/16-bit PCM, you would
+ * have ~93ms.
+ */
+ rc = wait_for_completion_interruptible_timeout(
+ &ads->in.comp[in_buf], HZ);
+ if (!rc) {
+ pr_err("%s: timeout", __func__);
+ rc = -ETIMEDOUT;
+ goto done;
+ } else if (rc < 0) {
+ pr_err("%s: wait error %d", __func__, rc);
+ goto done;
+ }
+
+ req = &ads->in.dma_req[in_buf];
+
+ nr = size > req->size ? req->size : size;
+ req->size = size;
+ dma_sync_single_for_cpu(NULL, ads->in.dma_req[in_buf].dest_addr,
+ ads->in.dma_req[in_buf].size, DMA_FROM_DEVICE);
+ rc = copy_to_user(buf, ads->in.buffer[in_buf], nr);
+ if (rc) {
+ rc = -EFAULT;
+ goto done;
+ }
+
+ init_completion(&ads->in.stop_completion);
+
+ ads->in.last_queued = in_buf;
+ rc = tegra_dma_enqueue_req(ads->in.dma_chan, req);
+ /* We've successfully enqueued this request before. */
+ BUG_ON(rc);
+
+ rc = nr;
+ *off += nr;
+done:
+ mutex_unlock(&ads->in.lock);
+ pr_debug("%s: done %d\n", __func__, rc);
+ return rc;
+}
+
+static int tegra_audio_out_open(struct inode *inode, struct file *file)
+{
+ int rc = 0;
+ int i;
+ struct audio_driver_state *ads = ads_from_misc_out(file);
+
+ pr_debug("%s\n", __func__);
+
+ mutex_lock(&ads->out.lock);
+
+ if (ads->out.opened) {
+ rc = -EBUSY;
+ goto done;
+ }
+
+ ads->out.opened = 1;
+ ads->out.stop = false;
+
+ for (i = 0; i < I2S_MAX_NUM_BUFS; i++) {
+ init_completion(&ads->out.comp[i]);
+ /* TX buf rest state is unqueued, complete. */
+ complete(&ads->out.comp[i]);
+ }
+
+done:
+ mutex_unlock(&ads->out.lock);
+ return rc;
+}
+
+static int tegra_audio_out_release(struct inode *inode, struct file *file)
+{
+ struct audio_driver_state *ads = ads_from_misc_out(file);
+
+ pr_debug("%s\n", __func__);
+
+ mutex_lock(&ads->out.lock);
+ ads->out.opened = 0;
+ request_stop_nosync(&ads->out);
+ if (stop_playback_if_necessary(&ads->out))
+ pr_debug("%s: done (stopped)\n", __func__);
+ allow_suspend(&ads->out);
+ mutex_unlock(&ads->out.lock);
+ pr_debug("%s: done\n", __func__);
+ return 0;
+}
+
+static int tegra_audio_in_open(struct inode *inode, struct file *file)
+{
+ int rc = 0;
+ int i;
+ struct audio_driver_state *ads = ads_from_misc_in(file);
+
+ pr_debug("%s\n", __func__);
+
+ mutex_lock(&ads->in.lock);
+ if (ads->in.opened) {
+ rc = -EBUSY;
+ goto done;
+ }
+
+ ads->in.opened = 1;
+ ads->in.stop = false;
+
+ for (i = 0; i < I2S_MAX_NUM_BUFS; i++) {
+ init_completion(&ads->in.comp[i]);
+ /* RX buf rest state is unqueued, complete. */
+ complete(&ads->in.comp[i]);
+ }
+
+done:
+ mutex_unlock(&ads->in.lock);
+ return rc;
+}
+
+static int tegra_audio_in_release(struct inode *inode, struct file *file)
+{
+ struct audio_driver_state *ads = ads_from_misc_in(file);
+
+ pr_debug("%s\n", __func__);
+
+ mutex_lock(&ads->in.lock);
+ ads->in.opened = 0;
+ if (ads->in.active) {
+ sound_ops->stop_recording(&ads->in);
+ complete(&ads->in.stop_completion);
+ request_stop_nosync(&ads->in);
+ }
+ allow_suspend(&ads->in);
+ mutex_unlock(&ads->in.lock);
+ pr_debug("%s: done\n", __func__);
+ return 0;
+}
+
+static const struct file_operations tegra_audio_out_fops = {
+ .owner = THIS_MODULE,
+ .open = tegra_audio_out_open,
+ .release = tegra_audio_out_release,
+ .write = tegra_audio_write,
+};
+
+static const struct file_operations tegra_audio_in_fops = {
+ .owner = THIS_MODULE,
+ .open = tegra_audio_in_open,
+ .read = tegra_audio_read,
+ .release = tegra_audio_in_release,
+};
+
+static int tegra_audio_ctl_open(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+static int tegra_audio_ctl_release(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+static const struct file_operations tegra_audio_out_ctl_fops = {
+ .owner = THIS_MODULE,
+ .open = tegra_audio_ctl_open,
+ .release = tegra_audio_ctl_release,
+ .unlocked_ioctl = tegra_audio_out_ioctl,
+};
+
+static const struct file_operations tegra_audio_in_ctl_fops = {
+ .owner = THIS_MODULE,
+ .open = tegra_audio_ctl_open,
+ .release = tegra_audio_ctl_release,
+ .unlocked_ioctl = tegra_audio_in_ioctl,
+};
+
+static const struct file_operations tegra_audio_ctl_fops = {
+ .owner = THIS_MODULE,
+ .open = tegra_audio_ctl_open,
+ .release = tegra_audio_ctl_release,
+ .unlocked_ioctl = tegra_audio_ioctl,
+};
+
+static int init_stream_buffer(struct audio_stream *s, int num)
+{
+ int i, j;
+ pr_debug("%s (num %d)\n", __func__, num);
+
+ for (i = 0; i < num; i++) {
+ kfree(s->buffer[i]);
+ s->buffer[i] =
+ kmalloc((1 << PCM_BUFFER_MAX_SIZE_ORDER),
+ GFP_KERNEL | GFP_DMA);
+ if (!s->buffer[i]) {
+ pr_err("%s: could not allocate buffer\n", __func__);
+ for (j = i - 1; j >= 0; j--) {
+ kfree(s->buffer[j]);
+ s->buffer[j] = 0;
+ }
+ return -ENOMEM;
+ }
+ }
+ return 0;
+}
+
+
+static int setup_misc_device(struct miscdevice *misc,
+ const struct file_operations *fops,
+ const char *fmt, ...)
+{
+ int rc = 0;
+ va_list args;
+ const int sz = 64;
+
+ va_start(args, fmt);
+
+ memset(misc, 0, sizeof(*misc));
+ misc->minor = MISC_DYNAMIC_MINOR;
+ misc->name = kmalloc(sz, GFP_KERNEL);
+ if (!misc->name) {
+ rc = -ENOMEM;
+ goto done;
+ }
+
+ vsnprintf((char *)misc->name, sz, fmt, args);
+ misc->fops = fops;
+ if (misc_register(misc)) {
+ pr_err("%s: could not register %s\n", __func__, misc->name);
+ kfree(misc->name);
+ rc = -EIO;
+ goto done;
+ }
+
+done:
+ va_end(args);
+ return rc;
+}
+
+static ssize_t dma_toggle_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "dma\n");
+}
+
+static ssize_t dma_toggle_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ pr_err("%s: Not implemented.", __func__);
+ return 0;
+}
+
+static DEVICE_ATTR(dma_toggle, 0644, dma_toggle_show, dma_toggle_store);
+
+static ssize_t __attr_fifo_atn_read(char *buf, int atn_lvl)
+{
+ switch (atn_lvl) {
+ case I2S_FIFO_ATN_LVL_ONE_SLOT:
+ strncpy(buf, "1\n", 2);
+ return 2;
+ case I2S_FIFO_ATN_LVL_FOUR_SLOTS:
+ strncpy(buf, "4\n", 2);
+ return 2;
+ case I2S_FIFO_ATN_LVL_EIGHT_SLOTS:
+ strncpy(buf, "8\n", 2);
+ return 2;
+ case I2S_FIFO_ATN_LVL_TWELVE_SLOTS:
+ strncpy(buf, "12\n", 3);
+ return 3;
+ default:
+ BUG_ON(1);
+ return -EIO;
+ }
+}
+
+static ssize_t __attr_fifo_atn_write(struct audio_driver_state *ads,
+ struct audio_stream *as,
+ int *fifo_lvl,
+ const char *buf, size_t size)
+{
+ int lvl;
+
+ if (size > 3) {
+ pr_err("%s: buffer size %d too big\n", __func__, size);
+ return -EINVAL;
+ }
+
+ if (sscanf(buf, "%d", &lvl) != 1) {
+ pr_err("%s: invalid input string [%s]\n", __func__, buf);
+ return -EINVAL;
+ }
+
+ switch (lvl) {
+ case 1:
+ lvl = I2S_FIFO_ATN_LVL_ONE_SLOT;
+ break;
+ case 4:
+ lvl = I2S_FIFO_ATN_LVL_FOUR_SLOTS;
+ break;
+ case 8:
+ lvl = I2S_FIFO_ATN_LVL_EIGHT_SLOTS;
+ break;
+ case 12:
+ lvl = I2S_FIFO_ATN_LVL_TWELVE_SLOTS;
+ break;
+ default:
+ pr_err("%s: invalid attention level %d\n", __func__, lvl);
+ return -EINVAL;
+ }
+
+ *fifo_lvl = lvl;
+ pr_info("%s: fifo level %d\n", __func__, *fifo_lvl);
+
+ return size;
+}
+
+static ssize_t tx_fifo_atn_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct tegra_audio_platform_data *pdata = dev->platform_data;
+ struct audio_driver_state *ads = pdata->driver_data;
+ return __attr_fifo_atn_read(buf, ads->out.i2s_fifo_atn_level);
+}
+
+static ssize_t tx_fifo_atn_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ ssize_t rc;
+ struct tegra_audio_platform_data *pdata = dev->platform_data;
+ struct audio_driver_state *ads = pdata->driver_data;
+ mutex_lock(&ads->out.lock);
+ if (pending_buffer_requests(&ads->out)) {
+ pr_err("%s: playback in progress.\n", __func__);
+ rc = -EBUSY;
+ goto done;
+ }
+ rc = __attr_fifo_atn_write(ads, &ads->out,
+ &ads->out.i2s_fifo_atn_level,
+ buf, count);
+done:
+ mutex_unlock(&ads->out.lock);
+ return rc;
+}
+
+static DEVICE_ATTR(tx_fifo_atn, 0644, tx_fifo_atn_show, tx_fifo_atn_store);
+
+static ssize_t rx_fifo_atn_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct tegra_audio_platform_data *pdata = dev->platform_data;
+ struct audio_driver_state *ads = pdata->driver_data;
+ return __attr_fifo_atn_read(buf, ads->in.i2s_fifo_atn_level);
+}
+
+static ssize_t rx_fifo_atn_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ ssize_t rc;
+ struct tegra_audio_platform_data *pdata = dev->platform_data;
+ struct audio_driver_state *ads = pdata->driver_data;
+ mutex_lock(&ads->in.lock);
+ if (ads->in.active) {
+ pr_err("%s: recording in progress.\n", __func__);
+ rc = -EBUSY;
+ goto done;
+ }
+ rc = __attr_fifo_atn_write(ads, &ads->in,
+ &ads->in.i2s_fifo_atn_level,
+ buf, count);
+done:
+ mutex_unlock(&ads->in.lock);
+ return rc;
+}
+
+static DEVICE_ATTR(rx_fifo_atn, 0644, rx_fifo_atn_show, rx_fifo_atn_store);
+
+static int tegra_audio_probe(struct platform_device *pdev)
+{
+ int rc, i;
+ struct resource *res;
+ struct clk *i2s_clk, *dap_mclk;
+ struct audio_driver_state *state;
+
+ pr_info("%s\n", __func__);
+
+ state = kzalloc(sizeof(*state), GFP_KERNEL);
+ if (!state)
+ return -ENOMEM;
+
+ state->pdev = pdev;
+ state->pdata = pdev->dev.platform_data;
+ state->pdata->driver_data = state;
+ BUG_ON(!state->pdata);
+
+ if (!(state->pdata->mask &
+ (TEGRA_AUDIO_ENABLE_TX | TEGRA_AUDIO_ENABLE_RX))) {
+ dev_err(&pdev->dev, "neither tx nor rx is enabled!\n");
+ return -EIO;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "no mem resource!\n");
+ return -ENODEV;
+ }
+
+ if (!request_mem_region(res->start, resource_size(res), pdev->name)) {
+ dev_err(&pdev->dev, "memory region already claimed!\n");
+ return -ENOMEM;
+ }
+
+ state->i2s_phys = res->start;
+ state->i2s_base = (unsigned long)ioremap(res->start,
+ res->end - res->start + 1);
+ if (!state->i2s_base) {
+ dev_err(&pdev->dev, "cannot remap iomem!\n");
+ return -EIO;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "no dma resource!\n");
+ return -ENODEV;
+ }
+ state->dma_req_sel = res->start;
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "no irq resource!\n");
+ return -ENODEV;
+ }
+ state->irq = res->start;
+
+ i2s_clk = clk_get(&pdev->dev, NULL);
+ if (!i2s_clk) {
+ dev_err(&pdev->dev, "%s: could not get i2s clock\n",
+ __func__);
+ return -EIO;
+ }
+
+ clk_set_rate(i2s_clk, state->pdata->i2s_clk_rate);
+ if (clk_enable(i2s_clk)) {
+ dev_err(&pdev->dev, "%s: failed to enable i2s clock\n",
+ __func__);
+ return -EIO;
+ }
+ pr_info("%s: i2s_clk rate %ld\n", __func__, clk_get_rate(i2s_clk));
+
+ dap_mclk = tegra_get_clock_by_name(state->pdata->dap_clk);
+ if (!dap_mclk) {
+ dev_err(&pdev->dev, "%s: could not get DAP clock\n",
+ __func__);
+ return -EIO;
+ }
+ clk_enable(dap_mclk);
+
+ rc = i2s_configure(pdev);
+ if (rc < 0)
+ return rc;
+
+ if ((state->pdata->mask & TEGRA_AUDIO_ENABLE_TX)) {
+ state->out.opened = 0;
+ state->out.active = false;
+ mutex_init(&state->out.lock);
+ init_completion(&state->out.stop_completion);
+ spin_lock_init(&state->out.dma_req_lock);
+ state->out.dma_chan = NULL;
+ state->out.i2s_fifo_atn_level = I2S_FIFO_ATN_LVL_FOUR_SLOTS;
+ state->out.num_bufs = I2S_DEFAULT_TX_NUM_BUFS;
+ for (i = 0; i < I2S_MAX_NUM_BUFS; i++) {
+ init_completion(&state->out.comp[i]);
+ /* TX buf rest state is unqueued, complete. */
+ complete(&state->out.comp[i]);
+ state->out.buffer[i] = 0;
+ state->out.buf_phy[i] = 0;
+ }
+ state->out.last_queued = 0;
+ rc = init_stream_buffer(&state->out, state->out.num_bufs);
+ if (rc < 0)
+ return rc;
+
+ INIT_WORK(&state->out.allow_suspend_work, allow_suspend_worker);
+
+ snprintf(state->out.wake_lock_name,
+ sizeof(state->out.wake_lock_name),
+ "i2s.%d-audio-out", state->pdev->id);
+ wake_lock_init(&state->out.wake_lock, WAKE_LOCK_SUSPEND,
+ state->out.wake_lock_name);
+
+ rc = setup_misc_device(&state->misc_out,
+ &tegra_audio_out_fops,
+ "audio%d_out", state->pdev->id);
+ if (rc < 0)
+ return rc;
+
+ rc = setup_misc_device(&state->misc_out_ctl,
+ &tegra_audio_out_ctl_fops,
+ "audio%d_out_ctl", state->pdev->id);
+ if (rc < 0)
+ return rc;
+ }
+
+ if ((state->pdata->mask & TEGRA_AUDIO_ENABLE_RX)) {
+ state->in.opened = 0;
+ state->in.active = false;
+ mutex_init(&state->in.lock);
+ init_completion(&state->in.stop_completion);
+ spin_lock_init(&state->in.dma_req_lock);
+ state->in.dma_chan = NULL;
+ state->in.i2s_fifo_atn_level = I2S_FIFO_ATN_LVL_FOUR_SLOTS;
+ state->in.num_bufs = I2S_DEFAULT_RX_NUM_BUFS;
+ for (i = 0; i < I2S_MAX_NUM_BUFS; i++) {
+ init_completion(&state->in.comp[i]);
+ /* RX buf rest state is unqueued, complete. */
+ complete(&state->in.comp[i]);
+ state->in.buffer[i] = 0;
+ state->in.buf_phy[i] = 0;
+ }
+ state->in.last_queued = 0;
+ rc = init_stream_buffer(&state->in, state->in.num_bufs);
+ if (rc < 0)
+ return rc;
+
+ INIT_WORK(&state->in.allow_suspend_work, allow_suspend_worker);
+
+ snprintf(state->in.wake_lock_name,
+ sizeof(state->in.wake_lock_name),
+ "i2s.%d-audio-in", state->pdev->id);
+ wake_lock_init(&state->in.wake_lock, WAKE_LOCK_SUSPEND,
+ state->in.wake_lock_name);
+
+ rc = setup_misc_device(&state->misc_in,
+ &tegra_audio_in_fops,
+ "audio%d_in", state->pdev->id);
+ if (rc < 0)
+ return rc;
+
+ rc = setup_misc_device(&state->misc_in_ctl,
+ &tegra_audio_in_ctl_fops,
+ "audio%d_in_ctl", state->pdev->id);
+ if (rc < 0)
+ return rc;
+ }
+
+ if (request_irq(state->irq, i2s_interrupt,
+ IRQF_DISABLED, state->pdev->name, state) < 0) {
+ dev_err(&pdev->dev,
+ "%s: could not register handler for irq %d\n",
+ __func__, state->irq);
+ return -EIO;
+ }
+
+ rc = setup_misc_device(&state->misc_ctl,
+ &tegra_audio_ctl_fops,
+ "audio%d_ctl", state->pdev->id);
+ if (rc < 0)
+ return rc;
+
+ sound_ops->setup(state, state->pdata->mask);
+
+ rc = device_create_file(&pdev->dev, &dev_attr_dma_toggle);
+ if (rc < 0) {
+ dev_err(&pdev->dev, "%s: could not create sysfs entry %s: %d\n",
+ __func__, dev_attr_dma_toggle.attr.name, rc);
+ return rc;
+ }
+
+ rc = device_create_file(&pdev->dev, &dev_attr_tx_fifo_atn);
+ if (rc < 0) {
+ dev_err(&pdev->dev, "%s: could not create sysfs entry %s: %d\n",
+ __func__, dev_attr_tx_fifo_atn.attr.name, rc);
+ return rc;
+ }
+
+ rc = device_create_file(&pdev->dev, &dev_attr_rx_fifo_atn);
+ if (rc < 0) {
+ dev_err(&pdev->dev, "%s: could not create sysfs entry %s: %d\n",
+ __func__, dev_attr_rx_fifo_atn.attr.name, rc);
+ return rc;
+ }
+
+ state->in_config.rate = 11025;
+ state->in_config.stereo = false;
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int tegra_audio_suspend(struct platform_device *pdev, pm_message_t mesg)
+{
+ /* dev_info(&pdev->dev, "%s\n", __func__); */
+ return 0;
+}
+
+static int tegra_audio_resume(struct platform_device *pdev)
+{
+ return i2s_configure(pdev);
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static struct platform_driver tegra_audio_driver = {
+ .driver = {
+ .name = "tegra-i2s",
+ .owner = THIS_MODULE,
+ },
+ .probe = tegra_audio_probe,
+#ifdef CONFIG_PM_SLEEP
+ .suspend = tegra_audio_suspend,
+ .resume = tegra_audio_resume,
+#endif
+};
+
+static int __init tegra_audio_init(void)
+{
+ return platform_driver_register(&tegra_audio_driver);
+}
+
+module_init(tegra_audio_init);
+MODULE_LICENSE("GPL");
diff --git a/arch/arm/mach-tegra/tegra_odm_fuses.c b/arch/arm/mach-tegra/tegra_odm_fuses.c
new file mode 100644
index 000000000000..6f6d22362fd1
--- /dev/null
+++ b/arch/arm/mach-tegra/tegra_odm_fuses.c
@@ -0,0 +1,951 @@
+/*
+ * arch/arm/mach-tegra/tegra_odm_fuses.c
+ *
+ * Copyright (c) 2010-2011, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/*
+ * Fuses are one time programmable bits on the chip which are used by
+ * the chip manufacturer and device manufacturers to store chip/device
+ * configurations. The fuse bits are encapsulated in a 32 x 64 array.
+ * If a fuse bit is programmed to 1, it cannot be reverted to 0. Either
+ * another fuse bit has to be used for the same purpose or a new chip
+ * needs to be used.
+ *
+ * Each and every fuse word has its own shadow word which resides adjacent to
+ * a particular fuse word. e.g. Fuse words 0-1 form a fuse-shadow pair.
+ * So in theory we have only 32 fuse words to work with.
+ * The shadow fuse word is a mirror of the actual fuse word at all times
+ * and this is maintained while programming a particular fuse.
+ */
+
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/mutex.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/kobject.h>
+#include <linux/regulator/consumer.h>
+#include <linux/ctype.h>
+#include <linux/wakelock.h>
+#include <linux/clk.h>
+
+#include <mach/tegra_odm_fuses.h>
+#include <mach/iomap.h>
+
+#include "fuse.h"
+
+#define NFUSES 64
+#define STATE_IDLE (0x4 << 16)
+
+/* since fuse burning is irreversible, use this for testing */
+#define ENABLE_FUSE_BURNING 1
+
+/* fuse registers */
+#define FUSE_CTRL 0x000
+#define FUSE_REG_ADDR 0x004
+#define FUSE_REG_READ 0x008
+#define FUSE_REG_WRITE 0x00C
+#define FUSE_TIME_PGM 0x01C
+#define FUSE_PRIV2INTFC 0x020
+#define FUSE_DIS_PGM 0x02C
+#define FUSE_WRITE_ACCESS 0x030
+#define FUSE_PWR_GOOD_SW 0x034
+
+static struct kobject *fuse_kobj;
+
+static ssize_t fuse_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf);
+static ssize_t fuse_store(struct kobject *kobj, struct kobj_attribute *attr,
+ const char *buf, size_t count);
+
+static struct kobj_attribute devkey_attr =
+ __ATTR(device_key, 0440, fuse_show, fuse_store);
+
+static struct kobj_attribute jtagdis_attr =
+ __ATTR(jtag_disable, 0440, fuse_show, fuse_store);
+
+static struct kobj_attribute odm_prod_mode_attr =
+ __ATTR(odm_production_mode, 0440, fuse_show, fuse_store);
+
+static struct kobj_attribute sec_boot_dev_cfg_attr =
+ __ATTR(sec_boot_dev_cfg, 0440, fuse_show, fuse_store);
+
+static struct kobj_attribute sec_boot_dev_sel_attr =
+ __ATTR(sec_boot_dev_sel, 0440, fuse_show, fuse_store);
+
+static struct kobj_attribute sbk_attr =
+ __ATTR(secure_boot_key, 0440, fuse_show, fuse_store);
+
+static struct kobj_attribute sw_rsvd_attr =
+ __ATTR(sw_reserved, 0440, fuse_show, fuse_store);
+
+static struct kobj_attribute ignore_dev_sel_straps_attr =
+ __ATTR(ignore_dev_sel_straps, 0440, fuse_show, fuse_store);
+
+static struct kobj_attribute odm_rsvd_attr =
+ __ATTR(odm_reserved, 0440, fuse_show, fuse_store);
+
+static u32 fuse_pgm_data[NFUSES / 2];
+static u32 fuse_pgm_mask[NFUSES / 2];
+static u32 tmp_fuse_pgm_data[NFUSES / 2];
+
+DEFINE_MUTEX(fuse_lock);
+
+static struct fuse_data fuse_info;
+struct regulator *vdd_fuse;
+struct clk *clk_fuse;
+
+#define FUSE_NAME_LEN 30
+
+struct param_info {
+ u32 *addr;
+ int sz;
+ u32 start_off;
+ int start_bit;
+ int nbits;
+ int data_offset;
+ char sysfs_name[FUSE_NAME_LEN];
+};
+
+static struct param_info fuse_info_tbl[] = {
+ [DEVKEY] = {
+ .addr = &fuse_info.devkey,
+ .sz = sizeof(fuse_info.devkey),
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
+ .start_off = 0x12,
+ .start_bit = 8,
+#else
+ .start_off = 0x16,
+ .start_bit = 22,
+#endif
+ .nbits = 32,
+ .data_offset = 0,
+ .sysfs_name = "device_key",
+ },
+ [JTAG_DIS] = {
+ .addr = &fuse_info.jtag_dis,
+ .sz = sizeof(fuse_info.jtag_dis),
+ .start_off = 0x0,
+ .start_bit = 24,
+ .nbits = 1,
+ .data_offset = 1,
+ .sysfs_name = "jtag_disable",
+ },
+ [ODM_PROD_MODE] = {
+ .addr = &fuse_info.odm_prod_mode,
+ .sz = sizeof(fuse_info.odm_prod_mode),
+ .start_off = 0x0,
+ .start_bit = 23,
+ .nbits = 1,
+ .data_offset = 2,
+ .sysfs_name = "odm_production_mode",
+ },
+ [SEC_BOOT_DEV_CFG] = {
+ .addr = &fuse_info.bootdev_cfg,
+ .sz = sizeof(fuse_info.bootdev_cfg),
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
+ .start_off = 0x14,
+ .start_bit = 8,
+#else
+ .start_off = 0x18,
+ .start_bit = 22,
+#endif
+ .nbits = 16,
+ .data_offset = 3,
+ .sysfs_name = "sec_boot_dev_cfg",
+ },
+ [SEC_BOOT_DEV_SEL] = {
+ .addr = &fuse_info.bootdev_sel,
+ .sz = sizeof(fuse_info.bootdev_sel),
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
+ .start_off = 0x14,
+ .start_bit = 24,
+#else
+ .start_off = 0x1A,
+ .start_bit = 6,
+#endif
+ .nbits = 3,
+ .data_offset = 4,
+ .sysfs_name = "sec_boot_dev_sel",
+ },
+ [SBK] = {
+ .addr = fuse_info.sbk,
+ .sz = sizeof(fuse_info.sbk),
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
+ .start_off = 0x0A,
+ .start_bit = 8,
+#else
+ .start_off = 0x0E,
+ .start_bit = 22,
+#endif
+ .nbits = 128,
+ .data_offset = 5,
+ .sysfs_name = "secure_boot_key",
+ },
+ [SW_RSVD] = {
+ .addr = &fuse_info.sw_rsvd,
+ .sz = sizeof(fuse_info.sw_rsvd),
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
+ .start_off = 0x14,
+ .start_bit = 28,
+#else
+ .start_off = 0x1A,
+ .start_bit = 10,
+#endif
+ .nbits = 4,
+ .data_offset = 9,
+ .sysfs_name = "sw_reserved",
+ },
+ [IGNORE_DEV_SEL_STRAPS] = {
+ .addr = &fuse_info.ignore_devsel_straps,
+ .sz = sizeof(fuse_info.ignore_devsel_straps),
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
+ .start_off = 0x14,
+ .start_bit = 27,
+#else
+ .start_off = 0x1A,
+ .start_bit = 9,
+#endif
+ .nbits = 1,
+ .data_offset = 10,
+ .sysfs_name = "ignore_dev_sel_straps",
+ },
+ [ODM_RSVD] = {
+ .addr = fuse_info.odm_rsvd,
+ .sz = sizeof(fuse_info.odm_rsvd),
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
+ .start_off = 0x16,
+ .start_bit = 4,
+#else
+ .start_off = 0x1A,
+ .start_bit = 14,
+#endif
+ .nbits = 256,
+ .data_offset = 11,
+ .sysfs_name = "odm_reserved",
+ },
+ [SBK_DEVKEY_STATUS] = {
+ .sz = SBK_DEVKEY_STATUS_SZ,
+ },
+};
+
+static void wait_for_idle(void)
+{
+ u32 reg;
+
+ do {
+ udelay(1);
+ reg = tegra_fuse_readl(FUSE_CTRL);
+ } while ((reg & (0xF << 16)) != STATE_IDLE);
+}
+
+#define FUSE_READ 0x1
+#define FUSE_WRITE 0x2
+#define FUSE_SENSE 0x3
+#define FUSE_CMD_MASK 0x3
+
+static u32 fuse_cmd_read(u32 addr)
+{
+ u32 reg;
+
+ wait_for_idle();
+ tegra_fuse_writel(addr, FUSE_REG_ADDR);
+ reg = tegra_fuse_readl(FUSE_CTRL);
+ reg &= ~FUSE_CMD_MASK;
+ reg |= FUSE_READ;
+ tegra_fuse_writel(reg, FUSE_CTRL);
+ wait_for_idle();
+
+ reg = tegra_fuse_readl(FUSE_REG_READ);
+ return reg;
+}
+
+static void fuse_cmd_write(u32 value, u32 addr)
+{
+ u32 reg;
+
+ wait_for_idle();
+ tegra_fuse_writel(addr, FUSE_REG_ADDR);
+ tegra_fuse_writel(value, FUSE_REG_WRITE);
+
+ reg = tegra_fuse_readl(FUSE_CTRL);
+ reg &= ~FUSE_CMD_MASK;
+ reg |= FUSE_WRITE;
+ tegra_fuse_writel(reg, FUSE_CTRL);
+ wait_for_idle();
+}
+
+static void fuse_cmd_sense(void)
+{
+ u32 reg;
+
+ wait_for_idle();
+ reg = tegra_fuse_readl(FUSE_CTRL);
+ reg &= ~FUSE_CMD_MASK;
+ reg |= FUSE_SENSE;
+ tegra_fuse_writel(reg, FUSE_CTRL);
+ wait_for_idle();
+}
+
+static void fuse_reg_hide(void)
+{
+ u32 reg = tegra_fuse_readl(0x48);
+ reg &= ~(1 << 28);
+ tegra_fuse_writel(reg, 0x48);
+}
+
+static void fuse_reg_unhide(void)
+{
+ u32 reg = tegra_fuse_readl(0x48);
+ reg |= (1 << 28);
+ tegra_fuse_writel(reg, 0x48);
+}
+
+static void get_fuse(enum fuse_io_param io_param, u32 *out)
+{
+ int start_bit = fuse_info_tbl[io_param].start_bit;
+ int nbits = fuse_info_tbl[io_param].nbits;
+ int offset = fuse_info_tbl[io_param].start_off;
+ u32 *dst = fuse_info_tbl[io_param].addr;
+ int dst_bit = 0;
+ int i;
+ u32 val;
+ int loops;
+
+ if (out)
+ dst = out;
+
+ do {
+ val = fuse_cmd_read(offset);
+ loops = min(nbits, 32 - start_bit);
+ for (i = 0; i < loops; i++) {
+ if (val & (BIT(start_bit + i)))
+ *dst |= BIT(dst_bit);
+ else
+ *dst &= ~BIT(dst_bit);
+ dst_bit++;
+ if (dst_bit == 32) {
+ dst++;
+ dst_bit = 0;
+ }
+ }
+ nbits -= loops;
+ offset += 2;
+ start_bit = 0;
+ } while (nbits > 0);
+}
+
+int tegra_fuse_read(enum fuse_io_param io_param, u32 *data, int size)
+{
+ int nbits;
+ u32 sbk[4], devkey = 0;
+
+ if (IS_ERR_OR_NULL(clk_fuse)) {
+ pr_err("fuse read disabled");
+ return -ENODEV;
+ }
+
+ if (!data)
+ return -EINVAL;
+
+ if (size != fuse_info_tbl[io_param].sz) {
+ pr_err("%s: size mismatch(%d), %d vs %d\n", __func__,
+ (int)io_param, size, fuse_info_tbl[io_param].sz);
+ return -EINVAL;
+ }
+
+ mutex_lock(&fuse_lock);
+
+ clk_enable(clk_fuse);
+ fuse_reg_unhide();
+ fuse_cmd_sense();
+
+ if (io_param == SBK_DEVKEY_STATUS) {
+ *data = 0;
+
+ get_fuse(SBK, sbk);
+ get_fuse(DEVKEY, &devkey);
+ nbits = sizeof(sbk) * BITS_PER_BYTE;
+ if (find_first_bit((unsigned long *)sbk, nbits) != nbits)
+ *data = 1;
+ else if (devkey)
+ *data = 1;
+ } else {
+ get_fuse(io_param, data);
+ }
+
+ fuse_reg_hide();
+ clk_disable(clk_fuse);
+ mutex_unlock(&fuse_lock);
+
+ return 0;
+}
+
+static bool fuse_odm_prod_mode(void)
+{
+ u32 odm_prod_mode = 0;
+
+ clk_enable(clk_fuse);
+ get_fuse(ODM_PROD_MODE, &odm_prod_mode);
+ clk_disable(clk_fuse);
+ return (odm_prod_mode ? true : false);
+}
+
+static void set_fuse(enum fuse_io_param io_param, u32 *data)
+{
+ int i, start_bit = fuse_info_tbl[io_param].start_bit;
+ int nbits = fuse_info_tbl[io_param].nbits, loops;
+ int offset = fuse_info_tbl[io_param].start_off >> 1;
+ int src_bit = 0;
+ u32 val;
+
+ do {
+ val = *data;
+ loops = min(nbits, 32 - start_bit);
+ for (i = 0; i < loops; i++) {
+ fuse_pgm_mask[offset] |= BIT(start_bit + i);
+ if (val & BIT(src_bit))
+ fuse_pgm_data[offset] |= BIT(start_bit + i);
+ else
+ fuse_pgm_data[offset] &= ~BIT(start_bit + i);
+ src_bit++;
+ if (src_bit == 32) {
+ data++;
+ val = *data;
+ src_bit = 0;
+ }
+ }
+ nbits -= loops;
+ offset++;
+ start_bit = 0;
+ } while (nbits > 0);
+}
+
+static void populate_fuse_arrs(struct fuse_data *info, u32 flags)
+{
+ u32 *src = (u32 *)info;
+ int i;
+
+ memset(fuse_pgm_data, 0, sizeof(fuse_pgm_data));
+ memset(fuse_pgm_mask, 0, sizeof(fuse_pgm_mask));
+
+ if ((flags & FLAGS_ODMRSVD)) {
+ set_fuse(ODM_RSVD, info->odm_rsvd);
+ flags &= ~FLAGS_ODMRSVD;
+ }
+
+ /* do not burn any more if secure mode is set */
+ if (fuse_odm_prod_mode())
+ goto out;
+
+ for_each_set_bit(i, (unsigned long *)&flags, MAX_PARAMS)
+ set_fuse(i, src + fuse_info_tbl[i].data_offset);
+
+out:
+ pr_debug("ready to program");
+}
+
+static void fuse_power_enable(void)
+{
+#if ENABLE_FUSE_BURNING
+ tegra_fuse_writel(0x1, FUSE_PWR_GOOD_SW);
+ udelay(1);
+#endif
+}
+
+static void fuse_power_disable(void)
+{
+#if ENABLE_FUSE_BURNING
+ tegra_fuse_writel(0, FUSE_PWR_GOOD_SW);
+ udelay(1);
+#endif
+}
+
+static void fuse_program_array(int pgm_cycles)
+{
+ u32 reg, fuse_val[2];
+ u32 *data = tmp_fuse_pgm_data, addr = 0, *mask = fuse_pgm_mask;
+ int i = 0;
+
+ fuse_cmd_sense();
+
+ /* get the first 2 fuse bytes */
+ fuse_val[0] = fuse_cmd_read(0);
+ fuse_val[1] = fuse_cmd_read(1);
+
+ fuse_power_enable();
+
+ /*
+ * The fuse macro is a high density macro. Fuses are
+ * burned using an addressing mechanism, so no need to prepare
+ * the full list, but more write to control registers are needed.
+ * The only bit that can be written at first is bit 0, a special write
+ * protection bit by assumptions all other bits are at 0
+ *
+ * The programming pulse must have a precise width of
+ * [9000, 11000] ns.
+ */
+ if (pgm_cycles > 0) {
+ reg = pgm_cycles;
+ tegra_fuse_writel(reg, FUSE_TIME_PGM);
+ }
+ fuse_val[0] = (0x1 & ~fuse_val[0]);
+ fuse_val[1] = (0x1 & ~fuse_val[1]);
+ fuse_cmd_write(fuse_val[0], 0);
+ fuse_cmd_write(fuse_val[1], 1);
+
+ fuse_power_disable();
+
+ /*
+ * this will allow programming of other fuses
+ * and the reading of the existing fuse values
+ */
+ fuse_cmd_sense();
+
+ /* Clear out all bits that have already been burned or masked out */
+ memcpy(data, fuse_pgm_data, sizeof(fuse_pgm_data));
+
+ for (addr = 0; addr < NFUSES; addr += 2, data++, mask++) {
+ reg = fuse_cmd_read(addr);
+ pr_debug("%d: 0x%x 0x%x 0x%x\n", addr, (u32)(*data),
+ ~reg, (u32)(*mask));
+ *data = (*data & ~reg) & *mask;
+ }
+
+ fuse_power_enable();
+
+ /*
+ * Finally loop on all fuses, program the non zero ones.
+ * Words 0 and 1 are written last and they contain control fuses. We
+ * need to invalidate after writing to a control word (with the exception
+ * of the master enable). This is also the reason we write them last.
+ */
+ for (i = ARRAY_SIZE(fuse_pgm_data) - 1; i >= 0; i--) {
+ if (tmp_fuse_pgm_data[i]) {
+ fuse_cmd_write(tmp_fuse_pgm_data[i], i * 2);
+ fuse_cmd_write(tmp_fuse_pgm_data[i], (i * 2) + 1);
+ }
+
+ if (i < 2) {
+ wait_for_idle();
+ fuse_power_disable();
+ fuse_cmd_sense();
+ fuse_power_enable();
+ }
+ }
+
+ fuse_power_disable();
+}
+
+static int fuse_set(enum fuse_io_param io_param, u32 *param, int size)
+{
+ int i, nwords = size / sizeof(u32);
+ u32 *data;
+
+ if (io_param > MAX_PARAMS)
+ return -EINVAL;
+
+ data = (u32*)kzalloc(size, GFP_KERNEL);
+ if (!data) {
+ pr_err("failed to alloc %d bytes\n", size);
+ return -ENOMEM;
+ }
+
+ get_fuse(io_param, data);
+
+ /* set only new fuse bits */
+ for (i = 0; i < nwords; i++) {
+ param[i] = (~data[i] & param[i]);
+ }
+
+ kfree(data);
+ return 0;
+}
+
+/*
+ * Function pointer to optional board specific function
+ */
+int (*tegra_fuse_regulator_en)(int);
+EXPORT_SYMBOL(tegra_fuse_regulator_en);
+
+#define CAR_OSC_CTRL 0x50
+#define PMC_PLLP_OVERRIDE 0xF8
+#define PMC_OSC_OVERRIDE BIT(0)
+#define PMC_OSC_FREQ_MASK (BIT(2) | BIT(3))
+#define PMC_OSC_FREQ_SHIFT 2
+#define CAR_OSC_FREQ_SHIFT 30
+
+#define FUSE_SENSE_DONE_BIT BIT(30)
+#define START_DATA BIT(0)
+#define SKIP_RAMREPAIR BIT(1)
+#define FUSE_PGM_TIMEOUT_MS 50
+
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
+/* cycles corresponding to 13MHz, 19.2MHz, 12MHz, 26MHz */
+static int fuse_pgm_cycles[] = {130, 192, 120, 260};
+#else
+/* cycles corresponding to 13MHz, 16.8MHz, 19.2MHz, 38.4MHz, 12MHz, 48MHz, 26MHz */
+static int fuse_pgm_cycles[] = {130, 168, 0, 0, 192, 384, 0, 0, 120, 480, 0, 0, 260};
+#endif
+
+int tegra_fuse_program(struct fuse_data *pgm_data, u32 flags)
+{
+ u32 reg;
+ int i = 0;
+ int index;
+ int ret;
+ int delay = FUSE_PGM_TIMEOUT_MS;
+
+ if (!pgm_data || !flags) {
+ pr_err("invalid parameter");
+ return -EINVAL;
+ }
+
+ if (IS_ERR_OR_NULL(clk_fuse) ||
+ (!tegra_fuse_regulator_en && IS_ERR_OR_NULL(vdd_fuse))) {
+ pr_err("fuse write disabled");
+ return -ENODEV;
+ }
+
+ if (fuse_odm_prod_mode() && (flags != FLAGS_ODMRSVD)) {
+ pr_err("reserved odm fuses aren't allowed in secure mode");
+ return -EPERM;
+ }
+
+ if ((flags & FLAGS_ODM_PROD_MODE) &&
+ (flags & (FLAGS_SBK | FLAGS_DEVKEY))) {
+ pr_err("odm production mode and sbk/devkey not allowed");
+ return -EPERM;
+ }
+
+ clk_enable(clk_fuse);
+
+ /* make all the fuse registers visible */
+ fuse_reg_unhide();
+
+ /* check that fuse options write access hasn't been disabled */
+ mutex_lock(&fuse_lock);
+ reg = tegra_fuse_readl(FUSE_DIS_PGM);
+ mutex_unlock(&fuse_lock);
+ if (reg) {
+ pr_err("fuse programming disabled");
+ fuse_reg_hide();
+ clk_disable(clk_fuse);
+ return -EACCES;
+ }
+
+ /* enable software writes to the fuse registers */
+ tegra_fuse_writel(0, FUSE_WRITE_ACCESS);
+
+ mutex_lock(&fuse_lock);
+ memcpy(&fuse_info, pgm_data, sizeof(fuse_info));
+ for_each_set_bit(i, (unsigned long *)&flags, MAX_PARAMS) {
+ fuse_set((u32)i, fuse_info_tbl[i].addr,
+ fuse_info_tbl[i].sz);
+ }
+
+#if ENABLE_FUSE_BURNING
+ if (tegra_fuse_regulator_en)
+ ret = tegra_fuse_regulator_en(1);
+ else
+ ret = regulator_enable(vdd_fuse);
+
+ if (ret)
+ BUG_ON("regulator enable fail\n");
+
+ populate_fuse_arrs(&fuse_info, flags);
+
+ /* calculate the number of program cycles from the oscillator freq */
+ reg = readl(IO_ADDRESS(TEGRA_PMC_BASE) + PMC_PLLP_OVERRIDE);
+ if (reg & PMC_OSC_OVERRIDE) {
+ index = (reg & PMC_OSC_FREQ_MASK) >> PMC_OSC_FREQ_SHIFT;
+ } else {
+ reg = readl(IO_ADDRESS(TEGRA_CLK_RESET_BASE) + CAR_OSC_CTRL);
+ index = reg >> CAR_OSC_FREQ_SHIFT;
+ }
+
+ pr_debug("%s: use %d programming cycles\n", __func__, fuse_pgm_cycles[index]);
+ fuse_program_array(fuse_pgm_cycles[index]);
+
+ memset(&fuse_info, 0, sizeof(fuse_info));
+
+ if (tegra_fuse_regulator_en)
+ tegra_fuse_regulator_en(0);
+ else
+ regulator_disable(vdd_fuse);
+#endif
+
+ mutex_unlock(&fuse_lock);
+
+ /* disable software writes to the fuse registers */
+ tegra_fuse_writel(1, FUSE_WRITE_ACCESS);
+
+ /* make all the fuse registers invisible */
+ fuse_reg_hide();
+
+ /* apply the fuse values immediately instead of resetting the chip */
+ fuse_cmd_sense();
+
+ tegra_fuse_writel(START_DATA | SKIP_RAMREPAIR, FUSE_PRIV2INTFC);
+
+ /* check sense and shift done in addition to IDLE */
+ do {
+ mdelay(1);
+ reg = tegra_fuse_readl(FUSE_CTRL);
+ reg &= (FUSE_SENSE_DONE_BIT | STATE_IDLE);
+ } while ((reg != (FUSE_SENSE_DONE_BIT | STATE_IDLE)) && (--delay > 0));
+
+ clk_disable(clk_fuse);
+
+ return ((delay > 0) ? 0 : -ETIMEDOUT);
+}
+
+static int fuse_name_to_param(const char *str)
+{
+ int i;
+
+ for (i = DEVKEY; i < ARRAY_SIZE(fuse_info_tbl); i++) {
+ if (!strcmp(str, fuse_info_tbl[i].sysfs_name))
+ return i;
+ }
+
+ return -ENODATA;
+}
+
+static int char_to_xdigit(char c)
+{
+ return (c>='0' && c<='9') ? c - '0' :
+ (c>='a' && c<='f') ? c - 'a' + 10 :
+ (c>='A' && c<='F') ? c - 'A' + 10 : -1;
+}
+
+#define CHK_ERR(x) \
+{ \
+ if (x) \
+ { \
+ pr_err("%s: sysfs_create_file fail(%d)!", __func__, x); \
+ return x; \
+ } \
+}
+
+static ssize_t fuse_store(struct kobject *kobj, struct kobj_attribute *attr,
+ const char *buf, size_t count)
+{
+ enum fuse_io_param param = fuse_name_to_param(attr->attr.name);
+ int ret, i = 0;
+ int orig_count = count;
+ struct fuse_data data = {0};
+ u32 *raw_data = ((u32 *)&data) + fuse_info_tbl[param].data_offset;
+ u8 *raw_byte_data = (u8 *)raw_data;
+ struct wake_lock fuse_wk_lock;
+
+ if ((param == -1) || (param == -ENODATA)) {
+ pr_err("%s: invalid fuse\n", __func__);
+ return -EINVAL;
+ }
+
+ if (fuse_odm_prod_mode()) {
+ pr_err("%s: device locked. odm fuse already blown\n", __func__);
+ return -EPERM;
+ }
+
+ count--;
+ if (DIV_ROUND_UP(count, 2) > fuse_info_tbl[param].sz) {
+ pr_err("%s: fuse parameter too long, should be %d character(s)\n",
+ __func__, fuse_info_tbl[param].sz * 2);
+ return -EINVAL;
+ }
+
+ /* see if the string has 0x/x at the start */
+ if (*buf == 'x') {
+ count -= 1;
+ buf++;
+ } else if (*(buf + 1) == 'x') {
+ count -= 2;
+ buf += 2;
+ }
+
+ /* wakelock to avoid device powering down while programming */
+ wake_lock_init(&fuse_wk_lock, WAKE_LOCK_SUSPEND, "fuse_wk_lock");
+ wake_lock(&fuse_wk_lock);
+
+ /* we need to fit each character into a single nibble */
+ raw_byte_data += DIV_ROUND_UP(count, 2) - 1;
+
+ /* in case of odd number of writes, write the first one here */
+ if (count & BIT(0)) {
+ *raw_byte_data = char_to_xdigit(*buf);
+ buf++;
+ raw_byte_data--;
+ count--;
+ }
+
+ for (i = 1; i <= count; i++, buf++) {
+ if (i & BIT(0)) {
+ *raw_byte_data = char_to_xdigit(*buf);
+ } else {
+ *raw_byte_data <<= 4;
+ *raw_byte_data |= char_to_xdigit(*buf);
+ raw_byte_data--;
+ }
+ }
+
+ ret = tegra_fuse_program(&data, BIT(param));
+ if (ret) {
+ pr_err("%s: fuse program fail(%d)\n", __func__, ret);
+ orig_count = ret;
+ goto done;
+ }
+
+ /* if odm prodn mode fuse is burnt, change file permissions to 0440 */
+ if (param == ODM_PROD_MODE) {
+ CHK_ERR(sysfs_chmod_file(kobj, &attr->attr, 0440));
+ CHK_ERR(sysfs_chmod_file(kobj, &devkey_attr.attr, 0440));
+ CHK_ERR(sysfs_chmod_file(kobj, &jtagdis_attr.attr, 0440));
+ CHK_ERR(sysfs_chmod_file(kobj, &sec_boot_dev_cfg_attr.attr, 0440));
+ CHK_ERR(sysfs_chmod_file(kobj, &sec_boot_dev_sel_attr.attr, 0440));
+ CHK_ERR(sysfs_chmod_file(kobj, &sbk_attr.attr, 0440));
+ CHK_ERR(sysfs_chmod_file(kobj, &sw_rsvd_attr.attr, 0440));
+ CHK_ERR(sysfs_chmod_file(kobj, &ignore_dev_sel_straps_attr.attr, 0440));
+ CHK_ERR(sysfs_chmod_file(kobj, &odm_rsvd_attr.attr, 0440));
+ }
+
+done:
+ wake_unlock(&fuse_wk_lock);
+ wake_lock_destroy(&fuse_wk_lock);
+ return orig_count;
+}
+
+static ssize_t fuse_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+ enum fuse_io_param param = fuse_name_to_param(attr->attr.name);
+ u32 data[8];
+ char str[8];
+ int ret, i;
+
+ if ((param == -1) || (param == -ENODATA)) {
+ pr_err("%s: invalid fuse\n", __func__);
+ return -EINVAL;
+ }
+
+ if ((param == SBK) && fuse_odm_prod_mode()) {
+ pr_err("device locked. sbk read not allowed\n");
+ return 0;
+ }
+
+ memset(data, 0, sizeof(data));
+ ret = tegra_fuse_read(param, data, fuse_info_tbl[param].sz);
+ if (ret) {
+ pr_err("%s: read fail(%d)\n", __func__, ret);
+ return ret;
+ }
+
+ strcpy(buf, "0x");
+ for (i = (fuse_info_tbl[param].sz/sizeof(u32)) - 1; i >= 0 ; i--) {
+ sprintf(str, "%08x", data[i]);
+ strcat(buf, str);
+ }
+
+ strcat(buf, "\n");
+ return strlen(buf);
+}
+
+static int __init tegra_fuse_program_init(void)
+{
+ if (!tegra_fuse_regulator_en) {
+ /* get vdd_fuse regulator */
+ vdd_fuse = regulator_get(NULL, "vdd_fuse");
+ if (IS_ERR_OR_NULL(vdd_fuse))
+ pr_err("%s: no vdd_fuse. fuse write disabled\n", __func__);
+ }
+
+ clk_fuse = clk_get_sys("fuse-tegra", "fuse_burn");
+ if (IS_ERR_OR_NULL(clk_fuse)) {
+ pr_err("%s: no clk_fuse. fuse read/write disabled\n", __func__);
+ if (!IS_ERR_OR_NULL(vdd_fuse)) {
+ regulator_put(vdd_fuse);
+ vdd_fuse = NULL;
+ }
+ return -ENODEV;
+ }
+
+ fuse_kobj = kobject_create_and_add("fuse", firmware_kobj);
+ if (!fuse_kobj) {
+ pr_err("%s: fuse_kobj create fail\n", __func__);
+ regulator_put(vdd_fuse);
+ clk_put(clk_fuse);
+ return -ENODEV;
+ }
+
+ mutex_init(&fuse_lock);
+
+ /* change fuse file permissions, if ODM production fuse is not blown */
+ if (!fuse_odm_prod_mode())
+ {
+ devkey_attr.attr.mode = 0640;
+ jtagdis_attr.attr.mode = 0640;
+ odm_prod_mode_attr.attr.mode = 0640;
+ sec_boot_dev_cfg_attr.attr.mode = 0640;
+ sec_boot_dev_sel_attr.attr.mode = 0640;
+ sbk_attr.attr.mode = 0640;
+ sw_rsvd_attr.attr.mode = 0640;
+ ignore_dev_sel_straps_attr.attr.mode = 0640;
+ odm_rsvd_attr.attr.mode = 0640;
+ odm_prod_mode_attr.attr.mode = 0640;
+ }
+
+ CHK_ERR(sysfs_create_file(fuse_kobj, &odm_prod_mode_attr.attr));
+ CHK_ERR(sysfs_create_file(fuse_kobj, &devkey_attr.attr));
+ CHK_ERR(sysfs_create_file(fuse_kobj, &jtagdis_attr.attr));
+ CHK_ERR(sysfs_create_file(fuse_kobj, &sec_boot_dev_cfg_attr.attr));
+ CHK_ERR(sysfs_create_file(fuse_kobj, &sec_boot_dev_sel_attr.attr));
+ CHK_ERR(sysfs_create_file(fuse_kobj, &sbk_attr.attr));
+ CHK_ERR(sysfs_create_file(fuse_kobj, &sw_rsvd_attr.attr));
+ CHK_ERR(sysfs_create_file(fuse_kobj, &ignore_dev_sel_straps_attr.attr));
+ CHK_ERR(sysfs_create_file(fuse_kobj, &odm_rsvd_attr.attr));
+
+ return 0;
+}
+
+static void __exit tegra_fuse_program_exit(void)
+{
+
+ fuse_power_disable();
+ fuse_reg_hide();
+
+ if (!IS_ERR_OR_NULL(vdd_fuse))
+ regulator_put(vdd_fuse);
+
+ if (!IS_ERR_OR_NULL(clk_fuse))
+ clk_put(clk_fuse);
+
+ sysfs_remove_file(fuse_kobj, &odm_prod_mode_attr.attr);
+ sysfs_remove_file(fuse_kobj, &devkey_attr.attr);
+ sysfs_remove_file(fuse_kobj, &jtagdis_attr.attr);
+ sysfs_remove_file(fuse_kobj, &sec_boot_dev_cfg_attr.attr);
+ sysfs_remove_file(fuse_kobj, &sec_boot_dev_sel_attr.attr);
+ sysfs_remove_file(fuse_kobj, &sbk_attr.attr);
+ sysfs_remove_file(fuse_kobj, &sw_rsvd_attr.attr);
+ sysfs_remove_file(fuse_kobj, &ignore_dev_sel_straps_attr.attr);
+ sysfs_remove_file(fuse_kobj, &odm_rsvd_attr.attr);
+ kobject_del(fuse_kobj);
+}
+
+late_initcall(tegra_fuse_program_init);
+module_exit(tegra_fuse_program_exit);
diff --git a/arch/arm/mach-tegra/tegra_smmu.h b/arch/arm/mach-tegra/tegra_smmu.h
new file mode 100644
index 000000000000..b46ae11c6f2e
--- /dev/null
+++ b/arch/arm/mach-tegra/tegra_smmu.h
@@ -0,0 +1,24 @@
+/*
+ * arch/arm/mach-tegra/tegra_smmu.h
+ *
+ * Copyright (C) 2011 NVIDIA Corporation.
+ *
+ * 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.
+ *
+ */
+#ifdef CONFIG_TEGRA_IOVMM_SMMU
+struct tegra_smmu_window {
+ unsigned long start;
+ unsigned long end;
+};
+
+extern struct tegra_smmu_window *tegra_smmu_window(int wnum);
+extern int tegra_smmu_window_count(void);
+#endif
diff --git a/arch/arm/mach-tegra/tegra_spdif_audio.c b/arch/arm/mach-tegra/tegra_spdif_audio.c
new file mode 100644
index 000000000000..3765633606cd
--- /dev/null
+++ b/arch/arm/mach-tegra/tegra_spdif_audio.c
@@ -0,0 +1,1187 @@
+/*
+ * arch/arm/mach-tegra/tegra_spdif_audio.c
+ *
+ * S/PDIF audio driver for NVIDIA Tegra SoCs
+ *
+ * Copyright (c) 2008-2009, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/fs.h>
+#include <linux/mutex.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/uaccess.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmapool.h>
+#include <linux/err.h>
+#include <linux/spi/spi.h>
+#include <linux/kfifo.h>
+#include <linux/debugfs.h>
+#include <linux/completion.h>
+#include <linux/platform_device.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/ktime.h>
+#include <linux/sysfs.h>
+#include <linux/wakelock.h>
+#include <linux/delay.h>
+#include <linux/tegra_audio.h>
+#include <linux/pm.h>
+#include <linux/workqueue.h>
+
+#include <mach/dma.h>
+#include <mach/iomap.h>
+#include <mach/spdif.h>
+#include <mach/audio.h>
+#include <mach/irqs.h>
+
+#include "clock.h"
+
+#define PCM_BUFFER_MAX_SIZE_ORDER (PAGE_SHIFT)
+
+#define SPDIF_MAX_NUM_BUFS 4
+/* Todo: Add IOCTL to configure the number of buffers. */
+#define SPDIF_DEFAULT_TX_NUM_BUFS 2
+#define SPDIF_DEFAULT_RX_NUM_BUFS 2
+/* per stream (input/output) */
+struct audio_stream {
+ int opened;
+ struct mutex lock;
+
+ bool active; /* is DMA in progress? */
+ int num_bufs;
+ void *buffer[SPDIF_MAX_NUM_BUFS];
+ dma_addr_t buf_phy[SPDIF_MAX_NUM_BUFS];
+ struct completion comp[SPDIF_MAX_NUM_BUFS];
+ struct tegra_dma_req dma_req[SPDIF_MAX_NUM_BUFS];
+ int last_queued;
+
+ int spdif_fifo_atn_level;
+
+ struct tegra_dma_channel *dma_chan;
+ bool stop;
+ struct completion stop_completion;
+ spinlock_t dma_req_lock;
+
+ struct work_struct allow_suspend_work;
+ struct wake_lock wake_lock;
+ char wake_lock_name[100];
+};
+
+struct audio_driver_state {
+ struct list_head next;
+
+ struct platform_device *pdev;
+ struct tegra_audio_platform_data *pdata;
+ phys_addr_t spdif_phys;
+ unsigned long spdif_base;
+
+ unsigned long dma_req_sel;
+ bool fifo_init;
+
+ int irq;
+
+ struct miscdevice misc_out;
+ struct miscdevice misc_out_ctl;
+ struct audio_stream out;
+};
+
+static inline bool pending_buffer_requests(struct audio_stream *stream)
+{
+ int i;
+ for (i = 0; i < stream->num_bufs; i++)
+ if (!completion_done(&stream->comp[i]))
+ return true;
+ return false;
+}
+
+static inline int buf_size(struct audio_stream *s __attribute__((unused)))
+{
+ return 1 << PCM_BUFFER_MAX_SIZE_ORDER;
+}
+
+static inline struct audio_driver_state *ads_from_misc_out(struct file *file)
+{
+ struct miscdevice *m = file->private_data;
+ struct audio_driver_state *ads =
+ container_of(m, struct audio_driver_state, misc_out);
+ BUG_ON(!ads);
+ return ads;
+}
+
+static inline struct audio_driver_state *ads_from_misc_out_ctl(
+ struct file *file)
+{
+ struct miscdevice *m = file->private_data;
+ struct audio_driver_state *ads =
+ container_of(m, struct audio_driver_state,
+ misc_out_ctl);
+ BUG_ON(!ads);
+ return ads;
+}
+
+static inline struct audio_driver_state *ads_from_out(
+ struct audio_stream *aos)
+{
+ return container_of(aos, struct audio_driver_state, out);
+}
+
+static inline void prevent_suspend(struct audio_stream *as)
+{
+ pr_debug("%s\n", __func__);
+ cancel_work_sync(&as->allow_suspend_work);
+ wake_lock(&as->wake_lock);
+}
+
+static void allow_suspend_worker(struct work_struct *w)
+{
+ struct audio_stream *as = container_of(w,
+ struct audio_stream, allow_suspend_work);
+ pr_debug("%s\n", __func__);
+ wake_unlock(&as->wake_lock);
+}
+
+static inline void allow_suspend(struct audio_stream *as)
+{
+ schedule_work(&as->allow_suspend_work);
+}
+
+#define I2S_I2S_FIFO_TX_BUSY I2S_I2S_STATUS_FIFO1_BSY
+#define I2S_I2S_FIFO_TX_QS I2S_I2S_STATUS_QS_FIFO1
+#define I2S_I2S_FIFO_TX_ERR I2S_I2S_STATUS_FIFO1_ERR
+
+#define I2S_I2S_FIFO_RX_BUSY I2S_I2S_STATUS_FIFO2_BSY
+#define I2S_I2S_FIFO_RX_QS I2S_I2S_STATUS_QS_FIFO2
+#define I2S_I2S_FIFO_RX_ERR I2S_I2S_STATUS_FIFO2_ERR
+
+#define I2S_FIFO_ERR (I2S_I2S_STATUS_FIFO1_ERR | I2S_I2S_STATUS_FIFO2_ERR)
+
+
+static inline void spdif_writel(unsigned long base, u32 val, u32 reg)
+{
+ writel(val, base + reg);
+}
+
+static inline u32 spdif_readl(unsigned long base, u32 reg)
+{
+ return readl(base + reg);
+}
+
+static inline void spdif_fifo_write(unsigned long base, u32 data)
+{
+ spdif_writel(base, data, SPDIF_DATA_OUT_0);
+}
+
+static int spdif_fifo_set_attention_level(unsigned long base,
+ unsigned level)
+{
+ u32 val;
+
+ if (level > SPDIF_FIFO_ATN_LVL_TWELVE_SLOTS) {
+ pr_err("%s: invalid fifo level selector %d\n", __func__,
+ level);
+ return -EINVAL;
+ }
+
+ val = spdif_readl(base, SPDIF_DATA_FIFO_CSR_0);
+
+ val &= ~SPDIF_DATA_FIFO_CSR_0_TX_ATN_LVL_MASK;
+ val |= level << SPDIF_DATA_FIFO_CSR_0_TX_ATN_LVL_SHIFT;
+
+
+ spdif_writel(base, val, SPDIF_DATA_FIFO_CSR_0);
+ return 0;
+}
+
+static void spdif_fifo_enable(unsigned long base, int on)
+{
+ u32 val = spdif_readl(base, SPDIF_CTRL_0);
+ val &= ~(SPDIF_CTRL_0_TX_EN | SPDIF_CTRL_0_TC_EN | SPDIF_CTRL_0_TU_EN);
+ val |= on ? (SPDIF_CTRL_0_TX_EN) : 0;
+ val |= on ? (SPDIF_CTRL_0_TC_EN) : 0;
+
+ spdif_writel(base, val, SPDIF_CTRL_0);
+}
+#if 0
+static bool spdif_is_fifo_enabled(unsigned long base)
+{
+ u32 val = spdif_readl(base, SPDIF_CTRL_0);
+ return !!(val & SPDIF_CTRL_0_TX_EN);
+}
+#endif
+
+static void spdif_fifo_clear(unsigned long base)
+{
+ u32 val = spdif_readl(base, SPDIF_DATA_FIFO_CSR_0);
+ val &= ~(SPDIF_DATA_FIFO_CSR_0_TX_CLR | SPDIF_DATA_FIFO_CSR_0_TU_CLR);
+ val |= SPDIF_DATA_FIFO_CSR_0_TX_CLR | SPDIF_DATA_FIFO_CSR_0_TU_CLR;
+ spdif_writel(base, val, SPDIF_DATA_FIFO_CSR_0);
+}
+
+
+static int spdif_set_bit_mode(unsigned long base, unsigned mode)
+{
+ u32 val = spdif_readl(base, SPDIF_CTRL_0);
+ val &= ~SPDIF_CTRL_0_BIT_MODE_MASK;
+
+ if (mode > SPDIF_BIT_MODE_MODERAW) {
+ pr_err("%s: invalid bit_size selector %d\n", __func__,
+ mode);
+ return -EINVAL;
+ }
+
+ val |= mode << SPDIF_CTRL_0_BIT_MODE_SHIFT;
+
+ spdif_writel(base, val, SPDIF_CTRL_0);
+ return 0;
+}
+
+static int spdif_set_fifo_packed(unsigned long base, unsigned on)
+{
+ u32 val = spdif_readl(base, SPDIF_CTRL_0);
+ val &= ~SPDIF_CTRL_0_PACK;
+ val |= on ? SPDIF_CTRL_0_PACK : 0;
+ spdif_writel(base, val, SPDIF_CTRL_0);
+ return 0;
+}
+
+#if 0
+static void spdif_set_fifo_irq_on_err(unsigned long base, int on)
+{
+ u32 val = spdif_readl(base, SPDIF_CTRL_0);
+ val &= ~SPDIF_CTRL_0_IE_TXE;
+ val |= on ? SPDIF_CTRL_0_IE_TXE : 0;
+ spdif_writel(base, val, SPDIF_CTRL_0);
+}
+#endif
+
+
+static void spdif_enable_fifos(unsigned long base, int on)
+{
+ u32 val = spdif_readl(base, SPDIF_CTRL_0);
+ if (on)
+ val |= SPDIF_CTRL_0_TX_EN | SPDIF_CTRL_0_TC_EN |
+ SPDIF_CTRL_0_IE_TXE;
+ else
+ val &= ~(SPDIF_CTRL_0_TX_EN | SPDIF_CTRL_0_TC_EN |
+ SPDIF_CTRL_0_IE_TXE);
+
+ spdif_writel(base, val, SPDIF_CTRL_0);
+}
+
+static inline u32 spdif_get_status(unsigned long base)
+{
+ return spdif_readl(base, SPDIF_STATUS_0);
+}
+
+static inline u32 spdif_get_control(unsigned long base)
+{
+ return spdif_readl(base, SPDIF_CTRL_0);
+}
+
+static inline void spdif_ack_status(unsigned long base)
+{
+ return spdif_writel(base, spdif_readl(base, SPDIF_STATUS_0),
+ SPDIF_STATUS_0);
+}
+
+static inline u32 spdif_get_fifo_scr(unsigned long base)
+{
+ return spdif_readl(base, SPDIF_DATA_FIFO_CSR_0);
+}
+
+static inline phys_addr_t spdif_get_fifo_phy_base(unsigned long phy_base)
+{
+ return phy_base + SPDIF_DATA_OUT_0;
+}
+
+static inline u32 spdif_get_fifo_full_empty_count(unsigned long base)
+{
+ u32 val = spdif_readl(base, SPDIF_DATA_FIFO_CSR_0);
+ val = val >> SPDIF_DATA_FIFO_CSR_0_TD_EMPTY_COUNT_SHIFT;
+ return val & SPDIF_DATA_FIFO_CSR_0_TD_EMPTY_COUNT_MASK;
+}
+
+
+static int spdif_set_sample_rate(struct audio_driver_state *state,
+ unsigned int sample_rate)
+{
+ unsigned int clock_freq = 0;
+ struct clk *spdif_clk;
+
+ unsigned int ch_sta[] = {
+ 0x0, /* 44.1, default values */
+ 0x0,
+ 0x0,
+ 0x0,
+ 0x0,
+ 0x0,
+ };
+
+ switch (sample_rate) {
+ case 32000:
+ clock_freq = 4096000; /* 4.0960 MHz */
+ ch_sta[0] = 0x3 << 24;
+ ch_sta[1] = 0xC << 4;
+ break;
+ case 44100:
+ clock_freq = 5644800; /* 5.6448 MHz */
+ ch_sta[0] = 0x0;
+ ch_sta[1] = 0xF << 4;
+ break;
+ case 48000:
+ clock_freq = 6144000; /* 6.1440MHz */
+ ch_sta[0] = 0x2 << 24;
+ ch_sta[1] = 0xD << 4;
+ break;
+ case 88200:
+ clock_freq = 11289600; /* 11.2896 MHz */
+ break;
+ case 96000:
+ clock_freq = 12288000; /* 12.288 MHz */
+ break;
+ case 176400:
+ clock_freq = 22579200; /* 22.5792 MHz */
+ break;
+ case 192000:
+ clock_freq = 24576000; /* 24.5760 MHz */
+ break;
+ default:
+ return -1;
+ }
+
+ spdif_clk = clk_get(&state->pdev->dev, NULL);
+ if (!spdif_clk) {
+ dev_err(&state->pdev->dev, "%s: could not get spdif clock\n",
+ __func__);
+ return -EIO;
+ }
+
+ clk_set_rate(spdif_clk, clock_freq);
+ if (clk_enable(spdif_clk)) {
+ dev_err(&state->pdev->dev,
+ "%s: failed to enable spdif_clk clock\n", __func__);
+ return -EIO;
+ }
+ pr_info("%s: spdif_clk rate %ld\n", __func__, clk_get_rate(spdif_clk));
+
+ spdif_writel(state->spdif_base, ch_sta[0], SPDIF_CH_STA_TX_A_0);
+ spdif_writel(state->spdif_base, ch_sta[1], SPDIF_CH_STA_TX_B_0);
+ spdif_writel(state->spdif_base, ch_sta[2], SPDIF_CH_STA_TX_C_0);
+ spdif_writel(state->spdif_base, ch_sta[3], SPDIF_CH_STA_TX_D_0);
+ spdif_writel(state->spdif_base, ch_sta[4], SPDIF_CH_STA_TX_E_0);
+ spdif_writel(state->spdif_base, ch_sta[5], SPDIF_CH_STA_TX_F_0);
+
+ return 0;
+}
+
+static int init_stream_buffer(struct audio_stream *, int);
+
+static int setup_dma(struct audio_driver_state *);
+static void tear_down_dma(struct audio_driver_state *);
+static void stop_dma_playback(struct audio_stream *);
+
+
+struct sound_ops {
+ int (*setup)(struct audio_driver_state *);
+ void (*tear_down)(struct audio_driver_state *);
+ void (*stop_playback)(struct audio_stream *);
+};
+
+static const struct sound_ops dma_sound_ops = {
+ .setup = setup_dma,
+ .tear_down = tear_down_dma,
+ .stop_playback = stop_dma_playback,
+};
+
+static const struct sound_ops *sound_ops = &dma_sound_ops;
+
+
+
+static bool stop_playback_if_necessary(struct audio_stream *aos)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&aos->dma_req_lock, flags);
+ pr_debug("%s\n", __func__);
+ if (!pending_buffer_requests(aos)) {
+ pr_debug("%s: no more data to play back\n", __func__);
+ sound_ops->stop_playback(aos);
+ spin_unlock_irqrestore(&aos->dma_req_lock, flags);
+ allow_suspend(aos);
+ return true;
+ }
+ spin_unlock_irqrestore(&aos->dma_req_lock, flags);
+
+ return false;
+}
+
+/* playback */
+static bool wait_till_stopped(struct audio_stream *as)
+{
+ int rc;
+ pr_debug("%s: wait for completion\n", __func__);
+ rc = wait_for_completion_timeout(
+ &as->stop_completion, HZ);
+ if (!rc)
+ pr_err("%s: wait timed out", __func__);
+ if (rc < 0)
+ pr_err("%s: wait error %d\n", __func__, rc);
+ allow_suspend(as);
+ pr_debug("%s: done: %d\n", __func__, rc);
+ return true;
+}
+
+/* Ask for playback to stop. The _nosync means that
+ * as->lock has to be locked by the caller.
+ */
+static void request_stop_nosync(struct audio_stream *as)
+{
+ int i;
+ pr_debug("%s\n", __func__);
+ if (!as->stop) {
+ as->stop = true;
+ if (pending_buffer_requests(as))
+ wait_till_stopped(as);
+ for (i = 0; i < as->num_bufs; i++) {
+ init_completion(&as->comp[i]);
+ complete(&as->comp[i]);
+ }
+ }
+ if (!tegra_dma_is_empty(as->dma_chan))
+ pr_err("%s: DMA not empty!\n", __func__);
+ /* Stop the DMA then dequeue anything that's in progress. */
+ tegra_dma_cancel(as->dma_chan);
+ as->active = false; /* applies to recording only */
+ pr_debug("%s: done\n", __func__);
+}
+
+static void setup_dma_tx_request(struct tegra_dma_req *req,
+ struct audio_stream *aos);
+
+static int setup_dma(struct audio_driver_state *ads)
+{
+ int rc, i;
+ pr_info("%s\n", __func__);
+
+ /* setup audio playback */
+ for (i = 0; i < ads->out.num_bufs; i++) {
+ ads->out.buf_phy[i] = dma_map_single(&ads->pdev->dev,
+ ads->out.buffer[i],
+ buf_size(&ads->out),
+ DMA_TO_DEVICE);
+ BUG_ON(!ads->out.buf_phy[i]);
+ setup_dma_tx_request(&ads->out.dma_req[i], &ads->out);
+ ads->out.dma_req[i].source_addr = ads->out.buf_phy[i];
+ }
+ ads->out.dma_chan =
+ tegra_dma_allocate_channel(TEGRA_DMA_MODE_CONTINUOUS_SINGLE,
+ "spdif_tx_req_%d", ads->dma_req_sel);
+ if (!ads->out.dma_chan) {
+ pr_err("%s: error alloc output DMA channel: %ld\n",
+ __func__, PTR_ERR(ads->out.dma_chan));
+ rc = -ENODEV;
+ goto fail_tx;
+ }
+ return 0;
+
+
+fail_tx:
+
+ for (i = 0; i < ads->out.num_bufs; i++) {
+ dma_unmap_single(&ads->pdev->dev, ads->out.buf_phy[i],
+ buf_size(&ads->out),
+ DMA_TO_DEVICE);
+ ads->out.buf_phy[i] = 0;
+ }
+ tegra_dma_free_channel(ads->out.dma_chan);
+ ads->out.dma_chan = 0;
+
+
+ return rc;
+}
+
+static void tear_down_dma(struct audio_driver_state *ads)
+{
+ int i;
+ pr_info("%s\n", __func__);
+
+
+ tegra_dma_free_channel(ads->out.dma_chan);
+ for (i = 0; i < ads->out.num_bufs; i++) {
+ dma_unmap_single(&ads->pdev->dev, ads->out.buf_phy[i],
+ buf_size(&ads->out),
+ DMA_TO_DEVICE);
+ ads->out.buf_phy[i] = 0;
+ }
+
+ ads->out.dma_chan = NULL;
+}
+
+static void dma_tx_complete_callback(struct tegra_dma_req *req)
+{
+ struct audio_stream *aos = req->dev;
+ unsigned req_num;
+
+ req_num = req - aos->dma_req;
+ pr_debug("%s: completed buffer %d size %d\n", __func__,
+ req_num, req->bytes_transferred);
+ BUG_ON(req_num >= aos->num_bufs);
+
+ complete(&aos->comp[req_num]);
+
+ if (!pending_buffer_requests(aos)) {
+ pr_debug("%s: Playback underflow", __func__);
+ complete(&aos->stop_completion);
+ }
+}
+
+
+static void setup_dma_tx_request(struct tegra_dma_req *req,
+ struct audio_stream *aos)
+{
+ struct audio_driver_state *ads = ads_from_out(aos);
+
+ memset(req, 0, sizeof(*req));
+
+ req->complete = dma_tx_complete_callback;
+ req->dev = aos;
+ req->to_memory = false;
+ req->dest_addr = spdif_get_fifo_phy_base(ads->spdif_phys);
+ req->dest_bus_width = 32;
+ req->dest_wrap = 4;
+ req->source_wrap = 0;
+ req->source_bus_width = 32;
+ req->req_sel = ads->dma_req_sel;
+}
+
+
+static int start_playback(struct audio_stream *aos,
+ struct tegra_dma_req *req)
+{
+ int rc;
+ unsigned long flags;
+ struct audio_driver_state *ads = ads_from_out(aos);
+
+ pr_debug("%s: (writing %d)\n",
+ __func__, req->size);
+
+ spin_lock_irqsave(&aos->dma_req_lock, flags);
+#if 0
+ spdif_fifo_clear(ads->spdif_base);
+#endif
+
+ spdif_fifo_set_attention_level(ads->spdif_base,
+ ads->out.spdif_fifo_atn_level);
+
+ if (ads->fifo_init) {
+ spdif_set_bit_mode(ads->spdif_base, SPDIF_BIT_MODE_MODE16BIT);
+ spdif_set_fifo_packed(ads->spdif_base, 1);
+ ads->fifo_init = false;
+ }
+
+ spdif_fifo_enable(ads->spdif_base, 1);
+
+ rc = tegra_dma_enqueue_req(aos->dma_chan, req);
+ spin_unlock_irqrestore(&aos->dma_req_lock, flags);
+
+ if (rc)
+ pr_err("%s: could not enqueue TX DMA req\n", __func__);
+ return rc;
+}
+
+/* Called with aos->dma_req_lock taken. */
+static void stop_dma_playback(struct audio_stream *aos)
+{
+ int spin = 0;
+ struct audio_driver_state *ads = ads_from_out(aos);
+ pr_debug("%s\n", __func__);
+ spdif_fifo_enable(ads->spdif_base, 0);
+ while ((spdif_get_status(ads->spdif_base) & SPDIF_STATUS_0_TX_BSY) &&
+ spin < 100) {
+ udelay(10);
+ if (spin++ > 50)
+ pr_info("%s: spin %d\n", __func__, spin);
+ }
+ if (spin == 100)
+ pr_warn("%s: spinny\n", __func__);
+ ads->fifo_init = true;
+}
+
+
+
+static irqreturn_t spdif_interrupt(int irq, void *data)
+{
+ struct audio_driver_state *ads = data;
+ u32 status = spdif_get_status(ads->spdif_base);
+
+ pr_debug("%s: %08x\n", __func__, status);
+
+/* if (status & SPDIF_STATUS_0_TX_ERR) */
+ spdif_ack_status(ads->spdif_base);
+
+ pr_debug("%s: done %08x\n", __func__,
+ spdif_get_status(ads->spdif_base));
+ return IRQ_HANDLED;
+}
+
+static ssize_t tegra_spdif_write(struct file *file,
+ const char __user *buf, size_t size, loff_t *off)
+{
+ ssize_t rc = 0;
+ int out_buf;
+ struct tegra_dma_req *req;
+ struct audio_driver_state *ads = ads_from_misc_out(file);
+
+ mutex_lock(&ads->out.lock);
+
+ if (!IS_ALIGNED(size, 4) || size < 4 || size > buf_size(&ads->out)) {
+ pr_err("%s: invalid user size %d\n", __func__, size);
+ rc = -EINVAL;
+ goto done;
+ }
+
+ pr_debug("%s: write %d bytes\n", __func__, size);
+
+ if (ads->out.stop) {
+ pr_debug("%s: playback has been cancelled\n", __func__);
+ goto done;
+ }
+
+ /* Decide which buf is next. */
+ out_buf = (ads->out.last_queued + 1) % ads->out.num_bufs;
+ req = &ads->out.dma_req[out_buf];
+
+ /* Wait for the buffer to be emptied (complete). The maximum timeout
+ * value could be calculated dynamically based on buf_size(&ads->out).
+ * For a buffer size of 16k, at 44.1kHz/stereo/16-bit PCM, you would
+ * have ~93ms.
+ */
+ pr_debug("%s: waiting for buffer %d\n", __func__, out_buf);
+ rc = wait_for_completion_interruptible_timeout(
+ &ads->out.comp[out_buf], HZ);
+ if (!rc) {
+ pr_err("%s: timeout", __func__);
+ rc = -ETIMEDOUT;
+ goto done;
+ } else if (rc < 0) {
+ pr_err("%s: wait error %d", __func__, rc);
+ goto done;
+ }
+
+ /* Fill the buffer and enqueue it. */
+ pr_debug("%s: acquired buffer %d, copying data\n", __func__, out_buf);
+ rc = copy_from_user(ads->out.buffer[out_buf], buf, size);
+ if (rc) {
+ rc = -EFAULT;
+ goto done;
+ }
+
+ prevent_suspend(&ads->out);
+
+ req->size = size;
+ dma_sync_single_for_device(NULL,
+ req->source_addr, req->size, DMA_TO_DEVICE);
+ ads->out.last_queued = out_buf;
+ init_completion(&ads->out.stop_completion);
+
+ rc = start_playback(&ads->out, req);
+ if (!rc)
+ rc = size;
+ else
+ allow_suspend(&ads->out);
+
+done:
+ mutex_unlock(&ads->out.lock);
+ return rc;
+}
+
+static long tegra_spdif_out_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ int rc = 0;
+ struct audio_driver_state *ads = ads_from_misc_out_ctl(file);
+ struct audio_stream *aos = &ads->out;
+
+ mutex_lock(&aos->lock);
+
+ switch (cmd) {
+ case TEGRA_AUDIO_OUT_FLUSH:
+ if (pending_buffer_requests(aos)) {
+ pr_debug("%s: flushing\n", __func__);
+ request_stop_nosync(aos);
+ pr_debug("%s: flushed\n", __func__);
+ }
+ if (stop_playback_if_necessary(aos))
+ pr_debug("%s: done (stopped)\n", __func__);
+ aos->stop = false;
+ break;
+ case TEGRA_AUDIO_OUT_SET_NUM_BUFS: {
+ unsigned int num;
+ if (copy_from_user(&num, (const void __user *)arg,
+ sizeof(num))) {
+ rc = -EFAULT;
+ break;
+ }
+ if (!num || num > SPDIF_MAX_NUM_BUFS) {
+ pr_err("%s: invalid buffer count %d\n", __func__, num);
+ rc = -EINVAL;
+ break;
+ }
+ if (pending_buffer_requests(aos)) {
+ pr_err("%s: playback in progress\n", __func__);
+ rc = -EBUSY;
+ break;
+ }
+ rc = init_stream_buffer(aos, num);
+ if (rc < 0)
+ break;
+ aos->num_bufs = num;
+ sound_ops->setup(ads);
+ }
+ break;
+ case TEGRA_AUDIO_OUT_GET_NUM_BUFS:
+ if (copy_to_user((void __user *)arg,
+ &aos->num_bufs, sizeof(aos->num_bufs)))
+ rc = -EFAULT;
+ break;
+ default:
+ rc = -EINVAL;
+ }
+
+ mutex_unlock(&aos->lock);
+ return rc;
+}
+
+
+static int tegra_spdif_out_open(struct inode *inode, struct file *file)
+{
+ int rc = 0;
+ int i;
+ struct audio_driver_state *ads = ads_from_misc_out(file);
+
+ pr_debug("%s\n", __func__);
+
+ mutex_lock(&ads->out.lock);
+
+ if (ads->out.opened) {
+ rc = -EBUSY;
+ goto done;
+ }
+
+ ads->out.opened = 1;
+ ads->out.stop = false;
+
+ for (i = 0; i < SPDIF_MAX_NUM_BUFS; i++) {
+ init_completion(&ads->out.comp[i]);
+ /* TX buf rest state is unqueued, complete. */
+ complete(&ads->out.comp[i]);
+ }
+
+done:
+ mutex_unlock(&ads->out.lock);
+ return rc;
+}
+
+static int tegra_spdif_out_release(struct inode *inode, struct file *file)
+{
+ struct audio_driver_state *ads = ads_from_misc_out(file);
+
+ pr_debug("%s\n", __func__);
+
+ mutex_lock(&ads->out.lock);
+ ads->out.opened = 0;
+ request_stop_nosync(&ads->out);
+ if (stop_playback_if_necessary(&ads->out))
+ pr_debug("%s: done (stopped)\n", __func__);
+ allow_suspend(&ads->out);
+ mutex_unlock(&ads->out.lock);
+ pr_debug("%s: done\n", __func__);
+ return 0;
+}
+
+
+static const struct file_operations tegra_spdif_out_fops = {
+ .owner = THIS_MODULE,
+ .open = tegra_spdif_out_open,
+ .release = tegra_spdif_out_release,
+ .write = tegra_spdif_write,
+};
+
+static int tegra_spdif_ctl_open(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+static int tegra_spdif_ctl_release(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+static const struct file_operations tegra_spdif_out_ctl_fops = {
+ .owner = THIS_MODULE,
+ .open = tegra_spdif_ctl_open,
+ .release = tegra_spdif_ctl_release,
+ .unlocked_ioctl = tegra_spdif_out_ioctl,
+};
+
+static int init_stream_buffer(struct audio_stream *s, int num)
+{
+ int i, j;
+ pr_debug("%s (num %d)\n", __func__, num);
+
+ for (i = 0; i < num; i++) {
+ kfree(s->buffer[i]);
+ s->buffer[i] =
+ kmalloc(buf_size(s), GFP_KERNEL | GFP_DMA);
+ if (!s->buffer[i]) {
+ pr_err("%s: could not allocate buffer\n", __func__);
+ for (j = i - 1; j >= 0; j--) {
+ kfree(s->buffer[j]);
+ s->buffer[j] = 0;
+ }
+ return -ENOMEM;
+ }
+ }
+ return 0;
+}
+
+
+static int setup_misc_device(struct miscdevice *misc,
+ const struct file_operations *fops,
+ const char *fmt, ...)
+{
+ int rc = 0;
+ va_list args;
+ const int sz = 64;
+
+ va_start(args, fmt);
+
+ memset(misc, 0, sizeof(*misc));
+ misc->minor = MISC_DYNAMIC_MINOR;
+ misc->name = kmalloc(sz, GFP_KERNEL);
+ if (!misc->name) {
+ rc = -ENOMEM;
+ goto done;
+ }
+
+ vsnprintf((char *)misc->name, sz, fmt, args);
+ misc->fops = fops;
+ if (misc_register(misc)) {
+ pr_err("%s: could not register %s\n", __func__, misc->name);
+ kfree(misc->name);
+ rc = -EIO;
+ goto done;
+ }
+
+done:
+ va_end(args);
+ return rc;
+}
+
+static ssize_t dma_toggle_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "dma\n");
+}
+
+static ssize_t dma_toggle_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ pr_err("%s: Not implemented.", __func__);
+ return 0;
+}
+
+static DEVICE_ATTR(dma_toggle, 0644, dma_toggle_show, dma_toggle_store);
+
+static ssize_t __attr_fifo_atn_read(char *buf, int atn_lvl)
+{
+ switch (atn_lvl) {
+ case SPDIF_FIFO_ATN_LVL_ONE_SLOT:
+ strncpy(buf, "1\n", 2);
+ return 2;
+ case SPDIF_FIFO_ATN_LVL_FOUR_SLOTS:
+ strncpy(buf, "4\n", 2);
+ return 2;
+ case SPDIF_FIFO_ATN_LVL_EIGHT_SLOTS:
+ strncpy(buf, "8\n", 2);
+ return 2;
+ case SPDIF_FIFO_ATN_LVL_TWELVE_SLOTS:
+ strncpy(buf, "12\n", 3);
+ return 3;
+ default:
+ BUG_ON(1);
+ return -EIO;
+ }
+}
+
+static ssize_t __attr_fifo_atn_write(struct audio_driver_state *ads,
+ struct audio_stream *as,
+ int *fifo_lvl,
+ const char *buf, size_t size)
+{
+ int lvl;
+
+ if (size > 3) {
+ pr_err("%s: buffer size %d too big\n", __func__, size);
+ return -EINVAL;
+ }
+
+ if (sscanf(buf, "%d", &lvl) != 1) {
+ pr_err("%s: invalid input string [%s]\n", __func__, buf);
+ return -EINVAL;
+ }
+
+ switch (lvl) {
+ case 1:
+ lvl = SPDIF_FIFO_ATN_LVL_ONE_SLOT;
+ break;
+ case 4:
+ lvl = SPDIF_FIFO_ATN_LVL_FOUR_SLOTS;
+ break;
+ case 8:
+ lvl = SPDIF_FIFO_ATN_LVL_EIGHT_SLOTS;
+ break;
+ case 12:
+ lvl = SPDIF_FIFO_ATN_LVL_TWELVE_SLOTS;
+ break;
+ default:
+ pr_err("%s: invalid attention level %d\n", __func__, lvl);
+ return -EINVAL;
+ }
+
+ *fifo_lvl = lvl;
+ pr_info("%s: fifo level %d\n", __func__, *fifo_lvl);
+
+ return size;
+}
+
+static ssize_t tx_fifo_atn_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct tegra_audio_platform_data *pdata = dev->platform_data;
+ struct audio_driver_state *ads = pdata->driver_data;
+ return __attr_fifo_atn_read(buf, ads->out.spdif_fifo_atn_level);
+}
+
+static ssize_t tx_fifo_atn_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ ssize_t rc;
+ struct tegra_audio_platform_data *pdata = dev->platform_data;
+ struct audio_driver_state *ads = pdata->driver_data;
+ mutex_lock(&ads->out.lock);
+ if (pending_buffer_requests(&ads->out)) {
+ pr_err("%s: playback in progress.\n", __func__);
+ rc = -EBUSY;
+ goto done;
+ }
+ rc = __attr_fifo_atn_write(ads, &ads->out,
+ &ads->out.spdif_fifo_atn_level,
+ buf, count);
+done:
+ mutex_unlock(&ads->out.lock);
+ return rc;
+}
+
+static DEVICE_ATTR(tx_fifo_atn, 0644, tx_fifo_atn_show, tx_fifo_atn_store);
+
+
+static int spdif_configure(struct platform_device *pdev)
+{
+ struct tegra_audio_platform_data *pdata = pdev->dev.platform_data;
+ struct audio_driver_state *state = pdata->driver_data;
+
+ if (!state)
+ return -ENOMEM;
+
+ /* disable interrupts from SPDIF */
+ spdif_writel(state->spdif_base, 0x0, SPDIF_CTRL_0);
+ spdif_fifo_clear(state->spdif_base);
+ spdif_enable_fifos(state->spdif_base, 0);
+
+ spdif_set_bit_mode(state->spdif_base, SPDIF_BIT_MODE_MODE16BIT);
+ spdif_set_fifo_packed(state->spdif_base, 1);
+
+ spdif_fifo_set_attention_level(state->spdif_base,
+ state->out.spdif_fifo_atn_level);
+
+ spdif_set_sample_rate(state, 44100);
+
+ state->fifo_init = true;
+ return 0;
+}
+
+static int tegra_spdif_probe(struct platform_device *pdev)
+{
+ int rc, i;
+ struct resource *res;
+ struct audio_driver_state *state;
+
+ pr_info("%s\n", __func__);
+
+ state = kzalloc(sizeof(*state), GFP_KERNEL);
+ if (!state)
+ return -ENOMEM;
+
+ state->pdev = pdev;
+ state->pdata = pdev->dev.platform_data;
+ state->pdata->driver_data = state;
+ BUG_ON(!state->pdata);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "no mem resource!\n");
+ return -ENODEV;
+ }
+
+ if (!request_mem_region(res->start, resource_size(res), pdev->name)) {
+ dev_err(&pdev->dev, "memory region already claimed!\n");
+ return -ENOMEM;
+ }
+
+ state->spdif_phys = res->start;
+ state->spdif_base = (unsigned long)ioremap(res->start,
+ res->end - res->start + 1);
+ if (!state->spdif_base) {
+ dev_err(&pdev->dev, "cannot remap iomem!\n");
+ return -EIO;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "no dma resource!\n");
+ return -ENODEV;
+ }
+ state->dma_req_sel = res->start;
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "no irq resource!\n");
+ return -ENODEV;
+ }
+ state->irq = res->start;
+
+ rc = spdif_configure(pdev);
+ if (rc < 0)
+ return rc;
+
+ state->out.opened = 0;
+ state->out.active = false;
+ mutex_init(&state->out.lock);
+ init_completion(&state->out.stop_completion);
+ spin_lock_init(&state->out.dma_req_lock);
+ state->out.dma_chan = NULL;
+ state->out.num_bufs = SPDIF_DEFAULT_TX_NUM_BUFS;
+ for (i = 0; i < SPDIF_MAX_NUM_BUFS; i++) {
+ init_completion(&state->out.comp[i]);
+ /* TX buf rest state is unqueued, complete. */
+ complete(&state->out.comp[i]);
+ state->out.buffer[i] = 0;
+ state->out.buf_phy[i] = 0;
+ }
+ state->out.last_queued = 0;
+ rc = init_stream_buffer(&state->out, state->out.num_bufs);
+ if (rc < 0)
+ return rc;
+
+ INIT_WORK(&state->out.allow_suspend_work, allow_suspend_worker);
+ snprintf(state->out.wake_lock_name, sizeof(state->out.wake_lock_name),
+ "tegra-audio-spdif");
+ wake_lock_init(&state->out.wake_lock, WAKE_LOCK_SUSPEND,
+ state->out.wake_lock_name);
+
+ if (request_irq(state->irq, spdif_interrupt,
+ IRQF_DISABLED, state->pdev->name, state) < 0) {
+ dev_err(&pdev->dev,
+ "%s: could not register handler for irq %d\n",
+ __func__, state->irq);
+ return -EIO;
+ }
+
+ rc = setup_misc_device(&state->misc_out,
+ &tegra_spdif_out_fops,
+ "spdif_out");
+ if (rc < 0)
+ return rc;
+
+ rc = setup_misc_device(&state->misc_out_ctl,
+ &tegra_spdif_out_ctl_fops,
+ "spdif_out_ctl");
+ if (rc < 0)
+ return rc;
+
+ sound_ops->setup(state);
+
+ rc = device_create_file(&pdev->dev, &dev_attr_dma_toggle);
+ if (rc < 0) {
+ dev_err(&pdev->dev, "%s: could not create sysfs entry %s: %d\n",
+ __func__, dev_attr_dma_toggle.attr.name, rc);
+ return rc;
+ }
+
+ rc = device_create_file(&pdev->dev, &dev_attr_tx_fifo_atn);
+ if (rc < 0) {
+ dev_err(&pdev->dev, "%s: could not create sysfs entry %s: %d\n",
+ __func__, dev_attr_tx_fifo_atn.attr.name, rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int tegra_spdif_suspend(struct platform_device *pdev, pm_message_t mesg)
+{
+ /* dev_info(&pdev->dev, "%s\n", __func__); */
+ return 0;
+}
+
+static int tegra_spdif_resume(struct platform_device *pdev)
+{
+ return spdif_configure(pdev);
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static struct platform_driver tegra_spdif_driver = {
+ .driver = {
+ .name = "spdif_out",
+ .owner = THIS_MODULE,
+ },
+ .probe = tegra_spdif_probe,
+#ifdef CONFIG_PM_SLEEP
+ .suspend = tegra_spdif_suspend,
+ .resume = tegra_spdif_resume,
+#endif
+};
+
+static int __init tegra_spdif_init(void)
+{
+ return platform_driver_register(&tegra_spdif_driver);
+}
+
+module_init(tegra_spdif_init);
+MODULE_LICENSE("GPL");
diff --git a/arch/arm/mach-tegra/tegra_usb_modem_power.c b/arch/arm/mach-tegra/tegra_usb_modem_power.c
new file mode 100644
index 000000000000..de377ac28964
--- /dev/null
+++ b/arch/arm/mach-tegra/tegra_usb_modem_power.c
@@ -0,0 +1,290 @@
+/*
+ * arch/arm/mach-tegra/tegra_usb_modem_power.c
+ *
+ * Copyright (c) 2011, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/workqueue.h>
+#include <linux/gpio.h>
+#include <linux/usb.h>
+#include <linux/err.h>
+#include <linux/wakelock.h>
+#include <mach/tegra_usb_modem_power.h>
+
+struct tegra_usb_modem {
+ unsigned int wake_gpio; /* remote wakeup gpio */
+ unsigned int wake_cnt; /* remote wakeup counter */
+ int irq; /* remote wakeup irq */
+ struct mutex lock;
+ struct wake_lock wake_lock; /* modem wake lock */
+ unsigned int vid; /* modem vendor id */
+ unsigned int pid; /* modem product id */
+ struct usb_device *udev; /* modem usb device */
+ struct usb_interface *intf; /* first modem usb interface */
+ struct workqueue_struct *wq; /* modem workqueue */
+ struct delayed_work recovery_work; /* modem recovery work */
+ const struct tegra_modem_operations *ops; /* modem operations */
+ unsigned int capability; /* modem capability */
+};
+
+static struct tegra_usb_modem tegra_mdm;
+
+/* supported modems */
+static const struct usb_device_id modem_list[] = {
+ {USB_DEVICE(0x1983, 0x0310), /* Icera 450 rev1 */
+ .driver_info = 0,
+ },
+ {USB_DEVICE(0x1983, 0x0321), /* Icera 450 rev2 */
+ .driver_info = 0,
+ },
+ {}
+};
+
+static irqreturn_t tegra_usb_modem_wake_thread(int irq, void *data)
+{
+ struct tegra_usb_modem *modem = (struct tegra_usb_modem *)data;
+
+ wake_lock_timeout(&modem->wake_lock, HZ);
+ mutex_lock(&modem->lock);
+ if (modem->udev) {
+ usb_lock_device(modem->udev);
+ pr_info("modem wake (%u)\n", ++(modem->wake_cnt));
+ if (usb_autopm_get_interface(modem->intf) == 0)
+ usb_autopm_put_interface_async(modem->intf);
+ usb_unlock_device(modem->udev);
+ }
+ mutex_unlock(&modem->lock);
+
+ return IRQ_HANDLED;
+}
+
+static void tegra_usb_modem_recovery(struct work_struct *ws)
+{
+ struct tegra_usb_modem *modem = container_of(ws, struct tegra_usb_modem,
+ recovery_work.work);
+
+ mutex_lock(&modem->lock);
+ if (!modem->udev) { /* assume modem crashed */
+ if (modem->ops && modem->ops->reset)
+ modem->ops->reset();
+ }
+ mutex_unlock(&modem->lock);
+}
+
+static void device_add_handler(struct usb_device *udev)
+{
+ const struct usb_device_descriptor *desc = &udev->descriptor;
+ struct usb_interface *intf = usb_ifnum_to_if(udev, 0);
+ const struct usb_device_id *id = usb_match_id(intf, modem_list);
+
+ if (id) {
+ pr_info("Add device %d <%s %s>\n", udev->devnum,
+ udev->manufacturer, udev->product);
+
+ mutex_lock(&tegra_mdm.lock);
+ tegra_mdm.udev = udev;
+ tegra_mdm.intf = intf;
+ tegra_mdm.vid = desc->idVendor;
+ tegra_mdm.pid = desc->idProduct;
+ tegra_mdm.wake_cnt = 0;
+ tegra_mdm.capability = id->driver_info;
+ mutex_unlock(&tegra_mdm.lock);
+
+ pr_info("persist_enabled: %u\n", udev->persist_enabled);
+
+ if (tegra_mdm.capability & TEGRA_MODEM_AUTOSUSPEND) {
+ usb_enable_autosuspend(udev);
+ pr_info("enable autosuspend for %s %s\n",
+ udev->manufacturer, udev->product);
+ }
+ }
+}
+
+static void device_remove_handler(struct usb_device *udev)
+{
+ const struct usb_device_descriptor *desc = &udev->descriptor;
+
+ if (desc->idVendor == tegra_mdm.vid &&
+ desc->idProduct == tegra_mdm.pid) {
+ pr_info("Remove device %d <%s %s>\n", udev->devnum,
+ udev->manufacturer, udev->product);
+
+ mutex_lock(&tegra_mdm.lock);
+ tegra_mdm.udev = NULL;
+ tegra_mdm.intf = NULL;
+ tegra_mdm.vid = 0;
+ mutex_unlock(&tegra_mdm.lock);
+
+ if (tegra_mdm.capability & TEGRA_MODEM_RECOVERY)
+ queue_delayed_work(tegra_mdm.wq,
+ &tegra_mdm.recovery_work, HZ * 10);
+ }
+}
+
+static int usb_notify(struct notifier_block *self, unsigned long action,
+ void *blob)
+{
+ switch (action) {
+ case USB_DEVICE_ADD:
+ device_add_handler(blob);
+ break;
+ case USB_DEVICE_REMOVE:
+ device_remove_handler(blob);
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block usb_nb = {
+ .notifier_call = usb_notify,
+};
+
+static int tegra_usb_modem_probe(struct platform_device *pdev)
+{
+ struct tegra_usb_modem_power_platform_data *pdata =
+ pdev->dev.platform_data;
+ int ret;
+
+ if (!pdata) {
+ dev_dbg(&pdev->dev, "platform_data not available\n");
+ return -EINVAL;
+ }
+
+ /* get modem operations from platform data */
+ tegra_mdm.ops = (const struct tegra_modem_operations *)pdata->ops;
+
+ if (tegra_mdm.ops) {
+ /* modem init */
+ if (tegra_mdm.ops->init) {
+ ret = tegra_mdm.ops->init();
+ if (ret)
+ return ret;
+ }
+
+ /* start modem */
+ if (tegra_mdm.ops->start)
+ tegra_mdm.ops->start();
+ }
+
+ mutex_init(&(tegra_mdm.lock));
+ wake_lock_init(&(tegra_mdm.wake_lock), WAKE_LOCK_SUSPEND,
+ "tegra_usb_mdm_lock");
+
+ /* create work queue */
+ tegra_mdm.wq = create_workqueue("tegra_usb_mdm_queue");
+ INIT_DELAYED_WORK(&(tegra_mdm.recovery_work), tegra_usb_modem_recovery);
+
+ /* create threaded irq for remote wakeup */
+ if (pdata->wake_gpio) {
+ /* get remote wakeup gpio from platform data */
+ tegra_mdm.wake_gpio = pdata->wake_gpio;
+
+ ret = gpio_request(tegra_mdm.wake_gpio, "usb_mdm_wake");
+ if (ret)
+ return ret;
+
+ tegra_gpio_enable(tegra_mdm.wake_gpio);
+
+ /* enable IRQ for remote wakeup */
+ tegra_mdm.irq = gpio_to_irq(tegra_mdm.wake_gpio);
+
+ ret =
+ request_threaded_irq(tegra_mdm.irq, NULL,
+ tegra_usb_modem_wake_thread,
+ pdata->flags, "tegra_usb_mdm_wake",
+ &tegra_mdm);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "%s: request_threaded_irq error\n",
+ __func__);
+ return ret;
+ }
+
+ ret = enable_irq_wake(tegra_mdm.irq);
+ if (ret) {
+ dev_err(&pdev->dev, "%s: enable_irq_wake error\n",
+ __func__);
+ free_irq(tegra_mdm.irq, &tegra_mdm);
+ return ret;
+ }
+ }
+
+ usb_register_notify(&usb_nb);
+ dev_info(&pdev->dev, "Initialized tegra_usb_modem_power\n");
+
+ return 0;
+}
+
+static int __exit tegra_usb_modem_remove(struct platform_device *pdev)
+{
+ usb_unregister_notify(&usb_nb);
+ free_irq(tegra_mdm.irq, &tegra_mdm);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int tegra_usb_modem_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ /* send L3 hint to modem */
+ if (tegra_mdm.ops && tegra_mdm.ops->suspend)
+ tegra_mdm.ops->suspend();
+ return 0;
+}
+
+static int tegra_usb_modem_resume(struct platform_device *pdev)
+{
+ /* send L3->L0 hint to modem */
+ if (tegra_mdm.ops && tegra_mdm.ops->resume)
+ tegra_mdm.ops->resume();
+ return 0;
+}
+#endif
+
+static struct platform_driver tegra_usb_modem_power_driver = {
+ .driver = {
+ .name = "tegra_usb_modem_power",
+ .owner = THIS_MODULE,
+ },
+ .probe = tegra_usb_modem_probe,
+ .remove = __exit_p(tegra_usb_modem_remove),
+#ifdef CONFIG_PM
+ .suspend = tegra_usb_modem_suspend,
+ .resume = tegra_usb_modem_resume,
+#endif
+};
+
+static int __init tegra_usb_modem_power_init(void)
+{
+ return platform_driver_register(&tegra_usb_modem_power_driver);
+}
+
+subsys_initcall(tegra_usb_modem_power_init);
+
+static void __exit tegra_usb_modem_power_exit(void)
+{
+ platform_driver_unregister(&tegra_usb_modem_power_driver);
+}
+
+module_exit(tegra_usb_modem_power_exit);
+
+MODULE_DESCRIPTION("Tegra usb modem power management driver");
+MODULE_LICENSE("GPL");
diff --git a/arch/arm/mach-tegra/timer-t2.c b/arch/arm/mach-tegra/timer-t2.c
new file mode 100644
index 000000000000..dff9abb76272
--- /dev/null
+++ b/arch/arm/mach-tegra/timer-t2.c
@@ -0,0 +1,128 @@
+/*
+ * arch/arch/mach-tegra/timer.c
+ *
+ * Copyright (C) 2010 Google, Inc.
+ * Copyright (C) 2011 NVIDIA Corporation.
+ *
+ * Author:
+ * Colin Cross <ccross@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/init.h>
+#include <linux/err.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/clockchips.h>
+#include <linux/clocksource.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/syscore_ops.h>
+
+#include <asm/mach/time.h>
+#include <asm/localtimer.h>
+#include <asm/sched_clock.h>
+
+#include <mach/iomap.h>
+#include <mach/irqs.h>
+
+#include "board.h"
+#include "clock.h"
+#include "timer.h"
+
+/*
+ * Timers usage:
+ * TMR1 - Free.
+ * TMR2 - used by AVP.
+ * TMR3 - used as general CPU timer.
+ * TMR4 - used for LP2 wakeup.
+*/
+
+#define TIMER1_OFFSET (TEGRA_TMR1_BASE-TEGRA_TMR1_BASE)
+#define TIMER2_OFFSET (TEGRA_TMR2_BASE-TEGRA_TMR1_BASE)
+#define TIMER3_OFFSET (TEGRA_TMR3_BASE-TEGRA_TMR1_BASE)
+#define TIMER4_OFFSET (TEGRA_TMR4_BASE-TEGRA_TMR1_BASE)
+
+#define timer_writel(value, reg) \
+ __raw_writel(value, (u32)timer_reg_base + (reg))
+#define timer_readl(reg) \
+ __raw_readl((u32)timer_reg_base + (reg))
+
+
+static void __iomem *timer_reg_base = IO_ADDRESS(TEGRA_TMR1_BASE);
+
+#ifdef CONFIG_PM_SLEEP
+static irqreturn_t tegra_lp2wake_interrupt(int irq, void *dev_id)
+{
+ timer_writel(1<<30, TIMER4_OFFSET + TIMER_PCR);
+ return IRQ_HANDLED;
+}
+
+static struct irqaction tegra_lp2wake_irq = {
+ .name = "timer_lp2wake",
+ .flags = IRQF_DISABLED,
+ .handler = tegra_lp2wake_interrupt,
+ .dev_id = NULL,
+ .irq = INT_TMR4,
+};
+
+void tegra2_lp2_set_trigger(unsigned long cycles)
+{
+ timer_writel(0, TIMER4_OFFSET + TIMER_PTV);
+ if (cycles) {
+ u32 reg = 0x80000000ul | min(0x1ffffffful, cycles);
+ timer_writel(reg, TIMER4_OFFSET + TIMER_PTV);
+ }
+}
+EXPORT_SYMBOL(tegra2_lp2_set_trigger);
+
+unsigned long tegra2_lp2_timer_remain(void)
+{
+ return timer_readl(TIMER4_OFFSET + TIMER_PCR) & 0x1ffffffful;
+}
+#endif
+
+void __init tegra2_init_timer(u32 *offset, int *irq)
+{
+ unsigned long rate = clk_measure_input_freq();
+ int ret;
+
+ switch (rate) {
+ case 12000000:
+ timer_writel(0x000b, TIMERUS_USEC_CFG);
+ break;
+ case 13000000:
+ timer_writel(0x000c, TIMERUS_USEC_CFG);
+ break;
+ case 19200000:
+ timer_writel(0x045f, TIMERUS_USEC_CFG);
+ break;
+ case 26000000:
+ timer_writel(0x0019, TIMERUS_USEC_CFG);
+ break;
+ default:
+ WARN(1, "Unknown clock rate");
+ }
+
+#ifdef CONFIG_PM_SLEEP
+ ret = setup_irq(tegra_lp2wake_irq.irq, &tegra_lp2wake_irq);
+ if (ret) {
+ pr_err("Failed to register LP2 timer IRQ: %d\n", ret);
+ BUG();
+ }
+#endif
+
+ *offset = TIMER3_OFFSET;
+ *irq = INT_TMR3;
+}
diff --git a/arch/arm/mach-tegra/timer-t3.c b/arch/arm/mach-tegra/timer-t3.c
new file mode 100644
index 000000000000..cc8d540bcdca
--- /dev/null
+++ b/arch/arm/mach-tegra/timer-t3.c
@@ -0,0 +1,288 @@
+/*
+ * arch/arch/mach-tegra/timer-t3.c
+ *
+ * Copyright (c) 2011, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/clockchips.h>
+#include <linux/clocksource.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/smp.h>
+#include <linux/syscore_ops.h>
+#include <linux/cpu.h>
+
+#include <asm/mach/time.h>
+#include <asm/localtimer.h>
+#include <asm/sched_clock.h>
+
+#include <mach/hardware.h>
+#include <mach/iomap.h>
+#include <mach/irqs.h>
+
+#include "board.h"
+#include "clock.h"
+#include "cpuidle.h"
+#include "timer.h"
+
+#define TEST_LP2_WAKE_TIMERS 0
+
+/*
+ * Timers usage:
+ * TMR1 - used as general CPU timer.
+ * TMR2 - used by AVP.
+ * TMR3 - used by CPU0 for LP2 wakeup.
+ * TMR4 - used by CPU1 for LP2 wakeup.
+ * TMR5 - used by CPU2 for LP2 wakeup.
+ * TMR6 - used by CPU3 for LP2 wakeup.
+ * TMR7 - Free.
+ * TMR8 - Free.
+ * TMR9 - Free.
+ * TMR10 - used as source for watchdog controller 0.
+*/
+
+#define TIMER1_OFFSET (TEGRA_TMR1_BASE-TEGRA_TMR1_BASE)
+#define TIMER2_OFFSET (TEGRA_TMR2_BASE-TEGRA_TMR1_BASE)
+#define TIMER3_OFFSET (TEGRA_TMR3_BASE-TEGRA_TMR1_BASE)
+#define TIMER4_OFFSET (TEGRA_TMR4_BASE-TEGRA_TMR1_BASE)
+#define TIMER5_OFFSET (TEGRA_TMR5_BASE-TEGRA_TMR1_BASE)
+#define TIMER6_OFFSET (TEGRA_TMR6_BASE-TEGRA_TMR1_BASE)
+
+static void __iomem *timer_reg_base = IO_ADDRESS(TEGRA_TMR1_BASE);
+
+#define timer_writel(value, reg) \
+ __raw_writel(value, (u32)timer_reg_base + (reg))
+#define timer_readl(reg) \
+ __raw_readl((u32)timer_reg_base + (reg))
+
+
+#ifdef CONFIG_PM_SLEEP
+static u32 lp2_wake_timers[] = {
+ TIMER3_OFFSET,
+#ifdef CONFIG_SMP
+ TIMER4_OFFSET,
+ TIMER5_OFFSET,
+ TIMER6_OFFSET,
+#endif
+};
+
+static irqreturn_t tegra_lp2wake_interrupt(int irq, void *dev_id)
+{
+ int cpu = (int)dev_id;
+ int base;
+
+ base = lp2_wake_timers[cpu];
+ timer_writel(1<<30, base + TIMER_PCR);
+ return IRQ_HANDLED;
+}
+
+#define LP2_TIMER_IRQ_ACTION(cpu, irqnum) { \
+ .name = "tmr_lp2wake_cpu" __stringify(cpu), \
+ .flags = IRQF_DISABLED, \
+ .handler = tegra_lp2wake_interrupt, \
+ .dev_id = (void*)cpu, \
+ .irq = irqnum }
+
+static struct irqaction tegra_lp2wake_irq[] = {
+ LP2_TIMER_IRQ_ACTION(0, INT_TMR3),
+#ifdef CONFIG_SMP
+ LP2_TIMER_IRQ_ACTION(1, INT_TMR4),
+ LP2_TIMER_IRQ_ACTION(2, INT_TMR5),
+ LP2_TIMER_IRQ_ACTION(3, INT_TMR6),
+#endif
+};
+
+#ifdef CONFIG_SMP
+#define hard_smp_processor_id() \
+ ({ \
+ unsigned int cpunum; \
+ __asm__("\n" \
+ "1: mrc p15, 0, %0, c0, c0, 5\n" \
+ " .pushsection \".alt.smp.init\", \"a\"\n"\
+ " .long 1b\n" \
+ " mov %0, #0\n" \
+ " .popsection" \
+ : "=r" (cpunum)); \
+ cpunum &= 0x0F; \
+ })
+#define cpu_number() hard_smp_processor_id()
+#else
+#define cpu_number() 0
+#endif
+
+/*
+ * To sanity test LP2 timer interrupts for CPU 0-3, enable this flag and check
+ * /proc/interrupts for timer interrupts. CPUs 0-3 should have one interrupt
+ * counted against them for tmr_lp2wake_cpu<n>, where <n> is the CPU number.
+ */
+#if TEST_LP2_WAKE_TIMERS
+static void test_lp2_wake_timer(unsigned int cpu)
+{
+ unsigned long cycles = 50000;
+ unsigned int base = lp2_wake_timers[cpu];
+ static bool tested[4] = {false, false, false, false};
+
+ /* Don't repeat the test process on hotplug restart. */
+ if (!tested[cpu]) {
+ timer_writel(0, base + TIMER_PTV);
+ if (cycles) {
+ u32 reg = 0x80000000ul | min(0x1ffffffful, cycles);
+ timer_writel(reg, base + TIMER_PTV);
+ tested[cpu] = true;
+ }
+ }
+}
+#else
+static inline void test_lp2_wake_timer(unsigned int cpu) {}
+#endif
+
+static void tegra3_register_wake_timer(unsigned int cpu)
+{
+ int ret;
+
+ ret = setup_irq(tegra_lp2wake_irq[cpu].irq, &tegra_lp2wake_irq[cpu]);
+ if (ret) {
+ pr_err("Failed to register LP2 timer IRQ for CPU %d: "
+ "irq=%d, ret=%d\n", cpu,
+ tegra_lp2wake_irq[cpu].irq, ret);
+ goto fail;
+ }
+
+#ifdef CONFIG_SMP
+ ret = irq_set_affinity(tegra_lp2wake_irq[cpu].irq, cpumask_of(cpu));
+ if (ret) {
+ pr_err("Failed to set affinity for LP2 timer IRQ to "
+ "CPU %d: irq=%d, ret=%d\n", cpu,
+ tegra_lp2wake_irq[cpu].irq, ret);
+ goto fail;
+ }
+#endif
+
+ test_lp2_wake_timer(cpu);
+ return;
+fail:
+ tegra_lp2_in_idle(false);
+}
+
+#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_HOTPLUG_CPU)
+static void tegra3_unregister_wake_timer(unsigned int cpu)
+{
+#ifdef CONFIG_SMP
+ /* Reassign the affinity of the wake IRQ to CPU 0. */
+ (void)irq_set_affinity(tegra_lp2wake_irq[cpu].irq, cpumask_of(0));
+#endif
+
+ /* Dispose of this IRQ. */
+ remove_irq(tegra_lp2wake_irq[cpu].irq, &tegra_lp2wake_irq[cpu]);
+}
+#endif
+
+void tegra3_lp2_set_trigger(unsigned long cycles)
+{
+ int cpu = cpu_number();
+ int base;
+
+ base = lp2_wake_timers[cpu];
+ timer_writel(0, base + TIMER_PTV);
+ if (cycles) {
+ u32 reg = 0x80000000ul | min(0x1ffffffful, cycles);
+ timer_writel(reg, base + TIMER_PTV);
+ }
+}
+EXPORT_SYMBOL(tegra3_lp2_set_trigger);
+
+unsigned long tegra3_lp2_timer_remain(void)
+{
+ int cpu = cpu_number();
+
+ return timer_readl(lp2_wake_timers[cpu] + TIMER_PCR) & 0x1ffffffful;
+}
+#endif
+
+void __init tegra3_init_timer(u32 *offset, int *irq)
+{
+ unsigned long rate = clk_measure_input_freq();
+
+ switch (rate) {
+ case 12000000:
+ timer_writel(0x000b, TIMERUS_USEC_CFG);
+ break;
+ case 13000000:
+ timer_writel(0x000c, TIMERUS_USEC_CFG);
+ break;
+ case 19200000:
+ timer_writel(0x045f, TIMERUS_USEC_CFG);
+ break;
+ case 26000000:
+ timer_writel(0x0019, TIMERUS_USEC_CFG);
+ break;
+ case 16800000:
+ timer_writel(0x0453, TIMERUS_USEC_CFG);
+ break;
+ case 38400000:
+ timer_writel(0x04BF, TIMERUS_USEC_CFG);
+ break;
+ case 48000000:
+ timer_writel(0x002F, TIMERUS_USEC_CFG);
+ break;
+ default:
+ WARN(1, "Unknown clock rate");
+ }
+
+#ifdef CONFIG_PM_SLEEP
+#ifdef CONFIG_SMP
+ /* For T30.A01 use INT_TMR_SHARED instead of INT_TMR6 for CPU3. */
+ if ((tegra_get_chipid() == TEGRA_CHIPID_TEGRA3) &&
+ (tegra_get_revision() == TEGRA_REVISION_A01))
+ tegra_lp2wake_irq[3].irq = INT_TMR_SHARED;
+#endif
+
+ tegra3_register_wake_timer(0);
+#endif
+
+ *offset = TIMER1_OFFSET;
+ *irq = INT_TMR1;
+}
+
+#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_HOTPLUG_CPU)
+static int hotplug_notify(struct notifier_block *self,
+ unsigned long action, void *cpu)
+{
+ if (action == CPU_ONLINE)
+ tegra3_register_wake_timer((unsigned int)cpu);
+ else if (action == CPU_DOWN_PREPARE)
+ tegra3_unregister_wake_timer((unsigned int)cpu);
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block __cpuinitdata hotplug_notifier_block = {
+ .notifier_call = hotplug_notify,
+};
+
+static int __init hotplug_cpu_register(void)
+{
+ return register_cpu_notifier(&hotplug_notifier_block);
+}
+early_initcall(hotplug_cpu_register);
+#endif
diff --git a/arch/arm/mach-tegra/timer.c b/arch/arm/mach-tegra/timer.c
index 90350420c4e9..d869ba3a3e2c 100644
--- a/arch/arm/mach-tegra/timer.c
+++ b/arch/arm/mach-tegra/timer.c
@@ -2,10 +2,13 @@
* arch/arch/mach-tegra/timer.c
*
* Copyright (C) 2010 Google, Inc.
+ * Copyright (C) 2011 NVIDIA Corporation.
*
* Author:
* Colin Cross <ccross@google.com>
*
+ * Copyright (C) 2010-2011 NVIDIA Corporation.
+ *
* 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.
@@ -27,39 +30,30 @@
#include <linux/clocksource.h>
#include <linux/clk.h>
#include <linux/io.h>
+#include <linux/syscore_ops.h>
#include <asm/mach/time.h>
#include <asm/localtimer.h>
+#include <asm/smp_twd.h>
#include <asm/sched_clock.h>
#include <mach/iomap.h>
#include <mach/irqs.h>
-#include <mach/suspend.h>
#include "board.h"
#include "clock.h"
-
-#define RTC_SECONDS 0x08
-#define RTC_SHADOW_SECONDS 0x0c
-#define RTC_MILLISECONDS 0x10
-
-#define TIMERUS_CNTR_1US 0x10
-#define TIMERUS_USEC_CFG 0x14
-#define TIMERUS_CNTR_FREEZE 0x4c
-
-#define TIMER1_BASE 0x0
-#define TIMER2_BASE 0x8
-#define TIMER3_BASE 0x50
-#define TIMER4_BASE 0x58
-
-#define TIMER_PTV 0x0
-#define TIMER_PCR 0x4
+#include "timer.h"
static void __iomem *timer_reg_base = IO_ADDRESS(TEGRA_TMR1_BASE);
static void __iomem *rtc_base = IO_ADDRESS(TEGRA_RTC_BASE);
static struct timespec persistent_ts;
static u64 persistent_ms, last_persistent_ms;
+static u32 usec_config;
+static u32 usec_offset;
+static bool usec_suspended;
+
+static u32 system_timer;
#define timer_writel(value, reg) \
__raw_writel(value, (u32)timer_reg_base + (reg))
@@ -72,7 +66,7 @@ static int tegra_timer_set_next_event(unsigned long cycles,
u32 reg;
reg = 0x80000000 | ((cycles > 1) ? (cycles-1) : 0);
- timer_writel(reg, TIMER3_BASE + TIMER_PTV);
+ timer_writel(reg, system_timer + TIMER_PTV);
return 0;
}
@@ -82,12 +76,12 @@ static void tegra_timer_set_mode(enum clock_event_mode mode,
{
u32 reg;
- timer_writel(0, TIMER3_BASE + TIMER_PTV);
+ timer_writel(0, system_timer + TIMER_PTV);
switch (mode) {
case CLOCK_EVT_MODE_PERIODIC:
reg = 0xC0000000 | ((1000000/HZ)-1);
- timer_writel(reg, TIMER3_BASE + TIMER_PTV);
+ timer_writel(reg, system_timer + TIMER_PTV);
break;
case CLOCK_EVT_MODE_ONESHOT:
break;
@@ -115,15 +109,23 @@ static DEFINE_CLOCK_DATA(cd);
#define SC_MULT 4194304000u
#define SC_SHIFT 22
+static u32 notrace tegra_read_usec(void)
+{
+ u32 cyc = usec_offset;
+ if (!usec_suspended)
+ cyc += timer_readl(TIMERUS_CNTR_1US);
+ return cyc;
+}
+
unsigned long long notrace sched_clock(void)
{
- u32 cyc = timer_readl(TIMERUS_CNTR_1US);
+ u32 cyc = tegra_read_usec();
return cyc_to_fixed_sched_clock(&cd, cyc, (u32)~0, SC_MULT, SC_SHIFT);
}
static void notrace tegra_update_sched_clock(void)
{
- u32 cyc = timer_readl(TIMERUS_CNTR_1US);
+ u32 cyc = tegra_read_usec();
update_sched_clock(&cd, cyc, (u32)~0);
}
@@ -133,7 +135,7 @@ static void notrace tegra_update_sched_clock(void)
* tegra_rtc driver could be executing to avoid race conditions
* on the RTC shadow register
*/
-u64 tegra_rtc_read_ms(void)
+static u64 tegra_rtc_read_ms(void)
{
u32 ms = readl(rtc_base + RTC_MILLISECONDS);
u32 s = readl(rtc_base + RTC_SHADOW_SECONDS);
@@ -166,7 +168,7 @@ void read_persistent_clock(struct timespec *ts)
static irqreturn_t tegra_timer_interrupt(int irq, void *dev_id)
{
struct clock_event_device *evt = (struct clock_event_device *)dev_id;
- timer_writel(1<<30, TIMER3_BASE + TIMER_PCR);
+ timer_writel(1<<30, system_timer + TIMER_PCR);
evt->event_handler(evt);
return IRQ_HANDLED;
}
@@ -176,13 +178,60 @@ static struct irqaction tegra_timer_irq = {
.flags = IRQF_DISABLED | IRQF_TIMER | IRQF_TRIGGER_HIGH,
.handler = tegra_timer_interrupt,
.dev_id = &tegra_clockevent,
- .irq = INT_TMR3,
};
+static int tegra_timer_suspend(void)
+{
+ usec_config = timer_readl(TIMERUS_USEC_CFG);
+
+ usec_offset += timer_readl(TIMERUS_CNTR_1US);
+ usec_suspended = true;
+
+ return 0;
+}
+
+static void tegra_timer_resume(void)
+{
+ timer_writel(usec_config, TIMERUS_USEC_CFG);
+
+ usec_offset -= timer_readl(TIMERUS_CNTR_1US);
+ usec_suspended = false;
+}
+
+static struct syscore_ops tegra_timer_syscore_ops = {
+ .suspend = tegra_timer_suspend,
+ .resume = tegra_timer_resume,
+};
+
+#ifdef CONFIG_HAVE_ARM_TWD
+void tegra_twd_suspend(struct tegra_twd_context *context)
+{
+ context->twd_ctrl = readl(twd_base + TWD_TIMER_CONTROL);
+ context->twd_load = readl(twd_base + TWD_TIMER_LOAD);
+ if ((context->twd_load == 0) &&
+ (context->twd_ctrl & TWD_TIMER_CONTROL_PERIODIC) &&
+ (context->twd_ctrl & (TWD_TIMER_CONTROL_ENABLE |
+ TWD_TIMER_CONTROL_IT_ENABLE))) {
+ WARN("%s: TWD enabled but counter was 0\n", __func__);
+ context->twd_load = 1;
+ }
+ __raw_writel(0, twd_base + TWD_TIMER_CONTROL);
+}
+
+void tegra_twd_resume(struct tegra_twd_context *context)
+{
+ BUG_ON((context->twd_load == 0) &&
+ (context->twd_ctrl & TWD_TIMER_CONTROL_PERIODIC) &&
+ (context->twd_ctrl & (TWD_TIMER_CONTROL_ENABLE |
+ TWD_TIMER_CONTROL_IT_ENABLE)));
+ writel(context->twd_load, twd_base + TWD_TIMER_LOAD);
+ writel(context->twd_ctrl, twd_base + TWD_TIMER_CONTROL);
+}
+#endif
+
static void __init tegra_init_timer(void)
{
struct clk *clk;
- unsigned long rate = clk_measure_input_freq();
int ret;
clk = clk_get_sys("timer", NULL);
@@ -201,22 +250,11 @@ static void __init tegra_init_timer(void)
twd_base = IO_ADDRESS(TEGRA_ARM_PERIF_BASE + 0x600);
#endif
- switch (rate) {
- case 12000000:
- timer_writel(0x000b, TIMERUS_USEC_CFG);
- break;
- case 13000000:
- timer_writel(0x000c, TIMERUS_USEC_CFG);
- break;
- case 19200000:
- timer_writel(0x045f, TIMERUS_USEC_CFG);
- break;
- case 26000000:
- timer_writel(0x0019, TIMERUS_USEC_CFG);
- break;
- default:
- WARN(1, "Unknown clock rate");
- }
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+ tegra2_init_timer(&system_timer, &tegra_timer_irq.irq);
+#else
+ tegra3_init_timer(&system_timer, &tegra_timer_irq.irq);
+#endif
init_fixed_sched_clock(&cd, tegra_update_sched_clock, 32,
1000000, SC_MULT, SC_SHIFT);
@@ -241,22 +279,10 @@ static void __init tegra_init_timer(void)
tegra_clockevent.cpumask = cpu_all_mask;
tegra_clockevent.irq = tegra_timer_irq.irq;
clockevents_register_device(&tegra_clockevent);
+
+ register_syscore_ops(&tegra_timer_syscore_ops);
}
struct sys_timer tegra_timer = {
.init = tegra_init_timer,
};
-
-#ifdef CONFIG_PM
-static u32 usec_config;
-
-void tegra_timer_suspend(void)
-{
- usec_config = timer_readl(TIMERUS_USEC_CFG);
-}
-
-void tegra_timer_resume(void)
-{
- timer_writel(usec_config, TIMERUS_USEC_CFG);
-}
-#endif
diff --git a/arch/arm/mach-tegra/timer.h b/arch/arm/mach-tegra/timer.h
new file mode 100644
index 000000000000..04d858fb77ea
--- /dev/null
+++ b/arch/arm/mach-tegra/timer.h
@@ -0,0 +1,51 @@
+/*
+ * arch/arm/mach-tegra/timer.h
+ *
+ * Copyright (C) 2010-2011 NVIDIA Corporation
+ *
+ * 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 _MACH_TEGRA_TIMER_H_
+#define _MACH_TEGRA_TIMER_H_
+
+#define RTC_SECONDS 0x08
+#define RTC_SHADOW_SECONDS 0x0c
+#define RTC_MILLISECONDS 0x10
+
+#define TIMER_PTV 0x0
+#define TIMER_PCR 0x4
+
+#define TIMERUS_CNTR_1US 0x10
+#define TIMERUS_USEC_CFG 0x14
+#define TIMERUS_CNTR_FREEZE 0x4c
+
+
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+void __init tegra2_init_timer(u32 *offset, int *irq);
+#else
+void __init tegra3_init_timer(u32 *offset, int *irq);
+#endif
+
+struct tegra_twd_context {
+ u32 twd_ctrl;
+ u32 twd_load;
+};
+
+#ifdef CONFIG_HAVE_ARM_TWD
+void tegra_twd_suspend(struct tegra_twd_context *context);
+void tegra_twd_resume(struct tegra_twd_context *context);
+#else
+static inline void tegra_twd_suspend(struct tegra_twd_context *context) {}
+static inline void tegra_twd_resume(struct tegra_twd_context *context) {}
+#endif
+
+#endif /* _MACH_TEGRA_TIMER_H_ */
diff --git a/arch/arm/mach-tegra/usb_phy.c b/arch/arm/mach-tegra/usb_phy.c
index 88081bb3ec52..532bf73c10d1 100644
--- a/arch/arm/mach-tegra/usb_phy.c
+++ b/arch/arm/mach-tegra/usb_phy.c
@@ -2,6 +2,7 @@
* arch/arm/mach-tegra/usb_phy.c
*
* Copyright (C) 2010 Google, Inc.
+ * Copyright (C) 2010 - 2011 NVIDIA Corporation
*
* Author:
* Erik Gilling <konkers@google.com>
@@ -20,6 +21,7 @@
#include <linux/resource.h>
#include <linux/delay.h>
+#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/platform_device.h>
@@ -30,8 +32,30 @@
#include <asm/mach-types.h>
#include <mach/usb_phy.h>
#include <mach/iomap.h>
+#include <mach/pinmux.h>
+#include "gpio-names.h"
+#include "fuse.h"
+
+/* Modem hibernate test parameters */
+#define TRIGGER_BY_MDM2AP_ACK 1
+/* ------------------------------- */
+
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+#define USB_USBCMD 0x140
+#define USB_USBCMD_RS (1 << 0)
+
+#define USB_USBSTS 0x144
+#define USB_USBSTS_PCI (1 << 2)
+#define USB_USBSTS_HCH (1 << 12)
+
+#define USB_TXFILLTUNING 0x164
+#define USB_FIFO_TXFILL_THRES(x) (((x) & 0x1f) << 16)
+#define USB_FIFO_TXFILL_MASK 0x1f0000
#define ULPI_VIEWPORT 0x170
+#define ULPI_WAKEUP (1 << 31)
+#define ULPI_RUN (1 << 30)
+#define ULPI_RD_WR (1 << 29)
#define USB_PORTSC1 0x184
#define USB_PORTSC1_PTS(x) (((x) & 0x3) << 30)
@@ -42,6 +66,7 @@
#define USB_PORTSC1_WKCN (1 << 20)
#define USB_PORTSC1_PTC(x) (((x) & 0xf) << 16)
#define USB_PORTSC1_PP (1 << 12)
+#define USB_PORTSC1_LS(x) (((x) & 0x3) << 10)
#define USB_PORTSC1_SUSP (1 << 7)
#define USB_PORTSC1_PE (1 << 2)
#define USB_PORTSC1_CCS (1 << 0)
@@ -50,14 +75,22 @@
#define USB_WAKE_ON_CNNT_EN_DEV (1 << 3)
#define USB_WAKE_ON_DISCON_EN_DEV (1 << 4)
#define USB_SUSP_CLR (1 << 5)
+#define USB_CLKEN (1 << 6)
#define USB_PHY_CLK_VALID (1 << 7)
-#define UTMIP_RESET (1 << 11)
-#define UHSIC_RESET (1 << 11)
-#define UTMIP_PHY_ENABLE (1 << 12)
+#define USB_PHY_CLK_VALID_INT_ENB (1 << 9)
+#define UTMIP_RESET (1 << 11)
+#define UHSIC_RESET (1 << 11)
+#define UTMIP_PHY_ENABLE (1 << 12)
+#define UHSIC_PHY_ENABLE (1 << 12)
#define ULPI_PHY_ENABLE (1 << 13)
#define USB_SUSP_SET (1 << 14)
#define USB_WAKEUP_DEBOUNCE_COUNT(x) (((x) & 0x7) << 16)
+#define USB_PHY_VBUS_WAKEUP_ID 0x408
+#define VDAT_DET_INT_EN (1 << 16)
+#define VDAT_DET_CHG_DET (1 << 17)
+#define VDAT_DET_STS (1 << 18)
+
#define USB1_LEGACY_CTRL 0x410
#define USB1_NO_LEGACY_MODE (1 << 0)
#define USB1_VBUS_SENSE_CTL_MASK (3 << 1)
@@ -67,17 +100,6 @@
#define USB1_VBUS_SENSE_CTL_AB_SESS_VLD (2 << 1)
#define USB1_VBUS_SENSE_CTL_A_SESS_VLD (3 << 1)
-#define ULPI_TIMING_CTRL_0 0x424
-#define ULPI_OUTPUT_PINMUX_BYP (1 << 10)
-#define ULPI_CLKOUT_PINMUX_BYP (1 << 11)
-
-#define ULPI_TIMING_CTRL_1 0x428
-#define ULPI_DATA_TRIMMER_LOAD (1 << 0)
-#define ULPI_DATA_TRIMMER_SEL(x) (((x) & 0x7) << 1)
-#define ULPI_STPDIRNXT_TRIMMER_LOAD (1 << 16)
-#define ULPI_STPDIRNXT_TRIMMER_SEL(x) (((x) & 0x7) << 17)
-#define ULPI_DIR_TRIMMER_LOAD (1 << 24)
-#define ULPI_DIR_TRIMMER_SEL(x) (((x) & 0x7) << 25)
#define UTMIP_PLL_CFG1 0x804
#define UTMIP_XTAL_FREQ_COUNT(x) (((x) & 0xfff) << 0)
@@ -90,8 +112,14 @@
#define UTMIP_FORCE_PD_POWERDOWN (1 << 14)
#define UTMIP_FORCE_PD2_POWERDOWN (1 << 16)
#define UTMIP_FORCE_PDZI_POWERDOWN (1 << 18)
+#define UTMIP_XCVR_LSBIAS_SEL (1 << 21)
+#define UTMIP_XCVR_SETUP_MSB(x) (((x) & 0x7) << 22)
#define UTMIP_XCVR_HSSLEW_MSB(x) (((x) & 0x7f) << 25)
+#define UTMIP_XCVR_MAX_OFFSET 2
+#define UTMIP_XCVR_SETUP_MAX_VALUE 0x7f
+#define XCVR_SETUP_MSB_CALIB(x) ((x) >> 4)
+
#define UTMIP_BIAS_CFG0 0x80c
#define UTMIP_OTGPD (1 << 11)
#define UTMIP_BIASPD (1 << 10)
@@ -107,15 +135,6 @@
#define UTMIP_FS_PREABMLE_J (1 << 19)
#define UTMIP_HS_DISCON_DISABLE (1 << 8)
-#define UTMIP_MISC_CFG0 0x824
-#define UTMIP_DPDM_OBSERVE (1 << 26)
-#define UTMIP_DPDM_OBSERVE_SEL(x) (((x) & 0xf) << 27)
-#define UTMIP_DPDM_OBSERVE_SEL_FS_J UTMIP_DPDM_OBSERVE_SEL(0xf)
-#define UTMIP_DPDM_OBSERVE_SEL_FS_K UTMIP_DPDM_OBSERVE_SEL(0xe)
-#define UTMIP_DPDM_OBSERVE_SEL_FS_SE1 UTMIP_DPDM_OBSERVE_SEL(0xd)
-#define UTMIP_DPDM_OBSERVE_SEL_FS_SE0 UTMIP_DPDM_OBSERVE_SEL(0xc)
-#define UTMIP_SUSPEND_EXIT_ON_EDGE (1 << 22)
-
#define UTMIP_MISC_CFG1 0x828
#define UTMIP_PLL_ACTIVE_DLY_COUNT(x) (((x) & 0x1f) << 18)
#define UTMIP_PLLU_STABLE_COUNT(x) (((x) & 0xfff) << 6)
@@ -125,9 +144,164 @@
#define UTMIP_BAT_CHRG_CFG0 0x830
#define UTMIP_PD_CHRG (1 << 0)
+#define UTMIP_ON_SINK_EN (1 << 2)
+#define UTMIP_OP_SRC_EN (1 << 3)
-#define UTMIP_SPARE_CFG0 0x834
-#define FUSE_SETUP_SEL (1 << 3)
+#define UTMIP_XCVR_CFG1 0x838
+#define UTMIP_FORCE_PDDISC_POWERDOWN (1 << 0)
+#define UTMIP_FORCE_PDCHRP_POWERDOWN (1 << 2)
+#define UTMIP_FORCE_PDDR_POWERDOWN (1 << 4)
+#define UTMIP_XCVR_TERM_RANGE_ADJ(x) (((x) & 0xf) << 18)
+
+#define UTMIP_BIAS_CFG1 0x83c
+#define UTMIP_BIAS_PDTRK_COUNT(x) (((x) & 0x1f) << 3)
+
+#define UHSIC_PLL_CFG1 0x804
+#define UHSIC_XTAL_FREQ_COUNT(x) (((x) & 0xfff) << 0)
+#define UHSIC_PLLU_ENABLE_DLY_COUNT(x) (((x) & 0x1f) << 14)
+
+#define UHSIC_HSRX_CFG0 0x808
+#define UHSIC_ELASTIC_UNDERRUN_LIMIT(x) (((x) & 0x1f) << 2)
+#define UHSIC_ELASTIC_OVERRUN_LIMIT(x) (((x) & 0x1f) << 8)
+#define UHSIC_IDLE_WAIT(x) (((x) & 0x1f) << 13)
+
+#define UHSIC_HSRX_CFG1 0x80c
+#define UHSIC_HS_SYNC_START_DLY(x) (((x) & 0x1f) << 1)
+
+#define UHSIC_MISC_CFG0 0x814
+#define UHSIC_SUSPEND_EXIT_ON_EDGE (1 << 7)
+#define UHSIC_DETECT_SHORT_CONNECT (1 << 8)
+#define UHSIC_FORCE_XCVR_MODE (1 << 15)
+
+#define UHSIC_MISC_CFG1 0X818
+#define UHSIC_PLLU_STABLE_COUNT(x) (((x) & 0xfff) << 2)
+
+#define UHSIC_PADS_CFG0 0x81c
+#define UHSIC_TX_RTUNEN 0xf000
+#define UHSIC_TX_RTUNE(x) (((x) & 0xf) << 12)
+
+#define UHSIC_PADS_CFG1 0x820
+#define UHSIC_PD_BG (1 << 2)
+#define UHSIC_PD_TX (1 << 3)
+#define UHSIC_PD_TRK (1 << 4)
+#define UHSIC_PD_RX (1 << 5)
+#define UHSIC_PD_ZI (1 << 6)
+#define UHSIC_RX_SEL (1 << 7)
+#define UHSIC_RPD_DATA (1 << 9)
+#define UHSIC_RPD_STROBE (1 << 10)
+#define UHSIC_RPU_DATA (1 << 11)
+#define UHSIC_RPU_STROBE (1 << 12)
+
+#define UHSIC_STAT_CFG0 0x828
+#define UHSIC_CONNECT_DETECT (1 << 0)
+
+
+#else
+
+#define USB_USBCMD 0x130
+#define USB_USBCMD_RS (1 << 0)
+
+#define USB_USBSTS 0x134
+#define USB_USBSTS_PCI (1 << 2)
+#define USB_USBSTS_SRI (1 << 7)
+#define USB_USBSTS_HCH (1 << 12)
+
+#define ULPI_VIEWPORT 0x160
+
+#define USB_PORTSC1 0x174
+#define USB_PORTSC1_WKOC (1 << 22)
+#define USB_PORTSC1_WKDS (1 << 21)
+#define USB_PORTSC1_WKCN (1 << 20)
+#define USB_PORTSC1_PTC(x) (((x) & 0xf) << 16)
+#define USB_PORTSC1_PP (1 << 12)
+#define USB_PORTSC1_SUSP (1 << 7)
+#define USB_PORTSC1_RESUME (1 << 6)
+#define USB_PORTSC1_PE (1 << 2)
+#define USB_PORTSC1_CCS (1 << 0)
+
+#define USB_SUSP_CTRL 0x400
+#define USB_WAKE_ON_CNNT_EN_DEV (1 << 3)
+#define USB_WAKE_ON_DISCON_EN_DEV (1 << 4)
+#define USB_SUSP_CLR (1 << 5)
+#define USB_PHY_CLK_VALID (1 << 7)
+#define USB_PHY_CLK_VALID_INT_ENB (1 << 9)
+
+
+#define UTMIP_RESET (1 << 11)
+#define UTMIP_PHY_ENABLE (1 << 12)
+#define ULPI_PHY_ENABLE (1 << 13)
+#define UHSIC_RESET (1 << 14)
+
+#define USB_WAKEUP_DEBOUNCE_COUNT(x) (((x) & 0x7) << 16)
+#define UHSIC_PHY_ENABLE (1 << 19)
+#define ULPIS2S_SLV0_RESET (1 << 20)
+#define ULPIS2S_SLV1_RESET (1 << 21)
+#define ULPIS2S_LINE_RESET (1 << 22)
+#define ULPI_PADS_RESET (1 << 23)
+#define ULPI_PADS_CLKEN_RESET (1 << 24)
+
+#define USB_PHY_VBUS_WAKEUP_ID 0x408
+#define VDAT_DET_INT_EN (1 << 16)
+#define VDAT_DET_CHG_DET (1 << 17)
+#define VDAT_DET_STS (1 << 18)
+
+#define USB1_LEGACY_CTRL 0x410
+#define USB1_NO_LEGACY_MODE (1 << 0)
+#define USB1_VBUS_SENSE_CTL_MASK (3 << 1)
+#define USB1_VBUS_SENSE_CTL_VBUS_WAKEUP (0 << 1)
+#define USB1_VBUS_SENSE_CTL_AB_SESS_VLD_OR_VBUS_WAKEUP \
+ (1 << 1)
+#define USB1_VBUS_SENSE_CTL_AB_SESS_VLD (2 << 1)
+#define USB1_VBUS_SENSE_CTL_A_SESS_VLD (3 << 1)
+
+#define UTMIP_PLL_CFG1 0x804
+#define UTMIP_XTAL_FREQ_COUNT(x) (((x) & 0xfff) << 0)
+#define UTMIP_PLLU_ENABLE_DLY_COUNT(x) (((x) & 0x1f) << 27)
+
+#define UTMIP_XCVR_CFG0 0x808
+#define UTMIP_XCVR_SETUP(x) (((x) & 0xf) << 0)
+#define UTMIP_XCVR_LSRSLEW(x) (((x) & 0x3) << 8)
+#define UTMIP_XCVR_LSFSLEW(x) (((x) & 0x3) << 10)
+#define UTMIP_FORCE_PD_POWERDOWN (1 << 14)
+#define UTMIP_FORCE_PD2_POWERDOWN (1 << 16)
+#define UTMIP_FORCE_PDZI_POWERDOWN (1 << 18)
+#define UTMIP_XCVR_LSBIAS_SEL (1 << 21)
+#define UTMIP_XCVR_SETUP_MSB(x) (((x) & 0x7) << 22)
+#define UTMIP_XCVR_HSSLEW_MSB(x) (((x) & 0x7f) << 25)
+
+#define UTMIP_XCVR_MAX_OFFSET 5
+#define UTMIP_XCVR_SETUP_MAX_VALUE 0x7f
+#define XCVR_SETUP_MSB_CALIB(x) ((x) >> 4)
+
+#define UTMIP_BIAS_CFG0 0x80c
+#define UTMIP_OTGPD (1 << 11)
+#define UTMIP_BIASPD (1 << 10)
+#define UTMIP_HSSQUELCH_LEVEL(x) (((x) & 0x3) << 0)
+#define UTMIP_HSDISCON_LEVEL(x) (((x) & 0x3) << 2)
+#define UTMIP_HSDISCON_LEVEL_MSB (1 << 24)
+
+#define UTMIP_HSRX_CFG0 0x810
+#define UTMIP_ELASTIC_LIMIT(x) (((x) & 0x1f) << 10)
+#define UTMIP_IDLE_WAIT(x) (((x) & 0x1f) << 15)
+
+#define UTMIP_HSRX_CFG1 0x814
+#define UTMIP_HS_SYNC_START_DLY(x) (((x) & 0x1f) << 1)
+
+#define UTMIP_TX_CFG0 0x820
+#define UTMIP_FS_PREABMLE_J (1 << 19)
+#define UTMIP_HS_DISCON_DISABLE (1 << 8)
+
+#define UTMIP_MISC_CFG1 0x828
+#define UTMIP_PLL_ACTIVE_DLY_COUNT(x) (((x) & 0x1f) << 18)
+#define UTMIP_PLLU_STABLE_COUNT(x) (((x) & 0xfff) << 6)
+
+#define UTMIP_DEBOUNCE_CFG0 0x82c
+#define UTMIP_BIAS_DEBOUNCE_A(x) (((x) & 0xffff) << 0)
+
+#define UTMIP_BAT_CHRG_CFG0 0x830
+#define UTMIP_PD_CHRG (1 << 0)
+#define UTMIP_ON_SINK_EN (1 << 2)
+#define UTMIP_OP_SRC_EN (1 << 3)
#define UTMIP_XCVR_CFG1 0x838
#define UTMIP_FORCE_PDDISC_POWERDOWN (1 << 0)
@@ -137,6 +311,270 @@
#define UTMIP_BIAS_CFG1 0x83c
#define UTMIP_BIAS_PDTRK_COUNT(x) (((x) & 0x1f) << 3)
+#define UTMIP_BIAS_PDTRK_POWERDOWN (1 << 0)
+#define UTMIP_BIAS_PDTRK_POWERUP (1 << 1)
+
+#define HOSTPC1_DEVLC 0x1b4
+#define HOSTPC1_DEVLC_PHCD (1 << 22)
+#define HOSTPC1_DEVLC_PTS(x) (((x) & 0x7) << 29)
+#define HOSTPC1_DEVLC_PTS_MASK 7
+#define HOSTPC1_DEVLC_PTS_HSIC 4
+#define HOSTPC1_DEVLC_STS (1 << 28)
+#define HOSTPC1_DEVLC_PSPD(x) (((x) & 0x3) << 25)
+#define HOSTPC1_DEVLC_PSPD_MASK 3
+#define HOSTPC1_DEVLC_PSPD_HIGH_SPEED 2
+
+#define TEGRA_USB_USBMODE_REG_OFFSET 0x1f8
+#define TEGRA_USB_USBMODE_HOST (3 << 0)
+
+#define TEGRA_PMC_USB_AO 0xf0
+#define TEGRA_PMC_USB_AO_VBUS_WAKEUP_PD_P0 (1 << 2)
+#define TEGRA_PMC_USB_AO_ID_PD_P0 (1 << 3)
+#define TEGRA_PMC_USB_AO_PD_P2 (0xf << 8)
+
+#define ICUSB_CTRL 0x15c
+
+#define UHSIC_PLL_CFG1 0xc04
+#define UHSIC_XTAL_FREQ_COUNT(x) (((x) & 0xfff) << 0)
+#define UHSIC_PLLU_ENABLE_DLY_COUNT(x) (((x) & 0x1f) << 14)
+
+#define UHSIC_HSRX_CFG0 0xc08
+#define UHSIC_ELASTIC_UNDERRUN_LIMIT(x) (((x) & 0x1f) << 2)
+#define UHSIC_ELASTIC_OVERRUN_LIMIT(x) (((x) & 0x1f) << 8)
+#define UHSIC_IDLE_WAIT(x) (((x) & 0x1f) << 13)
+
+#define UHSIC_HSRX_CFG1 0xc0c
+#define UHSIC_HS_SYNC_START_DLY(x) (((x) & 0x1f) << 1)
+
+#define UHSIC_MISC_CFG0 0xc14
+#define UHSIC_SUSPEND_EXIT_ON_EDGE (1 << 7)
+#define UHSIC_DETECT_SHORT_CONNECT (1 << 8)
+#define UHSIC_FORCE_XCVR_MODE (1 << 15)
+
+#define UHSIC_MISC_CFG1 0xc18
+#define UHSIC_PLLU_STABLE_COUNT(x) (((x) & 0xfff) << 2)
+
+#define UHSIC_PADS_CFG0 0xc1c
+#define UHSIC_TX_RTUNEN 0xf000
+#define UHSIC_TX_RTUNE(x) (((x) & 0xf) << 12)
+
+#define UHSIC_PADS_CFG1 0xc20
+#define UHSIC_PD_BG (1 << 2)
+#define UHSIC_PD_TX (1 << 3)
+#define UHSIC_PD_TRK (1 << 4)
+#define UHSIC_PD_RX (1 << 5)
+#define UHSIC_PD_ZI (1 << 6)
+#define UHSIC_RX_SEL (1 << 7)
+#define UHSIC_RPD_DATA (1 << 9)
+#define UHSIC_RPD_STROBE (1 << 10)
+#define UHSIC_RPU_DATA (1 << 11)
+#define UHSIC_RPU_STROBE (1 << 12)
+
+#define UHSIC_STAT_CFG0 0xc28
+#define UHSIC_CONNECT_DETECT (1 << 0)
+
+#define PMC_UTMIP_MASTER_CONFIG 0x310
+#define UTMIP_PWR(inst) (1 << (inst))
+
+#define PMC_USB_DEBOUNCE 0xec
+#define UTMIP_LINE_DEB_CNT(x) (((x) & 0xf) << 16)
+
+#define PMC_UTMIP_UHSIC_FAKE 0x218
+#define USBON_VAL(inst) (1 << ((4*(inst))+1))
+#define USBON_VAL_P2 (1 << 9)
+#define USBON_VAL_P1 (1 << 5)
+#define USBON_VAL_P0 (1 << 1)
+#define USBOP_VAL(inst) (1 << (4*(inst)))
+#define USBOP_VAL_P2 (1 << 8)
+#define USBOP_VAL_P1 (1 << 4)
+#define USBOP_VAL_P0 (1 << 0)
+
+#define PMC_SLEEPWALK_CFG 0x200
+#define UTMIP_LINEVAL_WALK_EN(inst) (1 << ((8*(inst))+7))
+#define UTMIP_LINEVAL_WALK_EN_P2 (1 << 23)
+#define UTMIP_LINEVAL_WALK_EN_P1 (1 << 15)
+#define UTMIP_LINEVAL_WALK_EN_P0 (1 << 7)
+#define UTMIP_WAKE_VAL(inst, x) (((x) & 0xf) << ((8*(inst))+4))
+#define UTMIP_WAKE_VAL_P2(x) (((x) & 0xf) << 20)
+#define UTMIP_WAKE_VAL_P1(x) (((x) & 0xf) << 12)
+#define UTMIP_WAKE_VAL_P0(x) (((x) & 0xf) << 4)
+#define WAKE_VAL_NONE 0xc
+#define WAKE_VAL_FSJ 0x2
+#define WAKE_VAL_FSK 0x1
+#define WAKE_VAL_SE0 0x0
+#define WAKE_VAL_ANY 0xf
+
+#define PMC_SLEEP_CFG 0x1fc
+#define UTMIP_TCTRL_USE_PMC(inst) (1 << ((8*(inst))+3))
+#define UTMIP_TCTRL_USE_PMC_P2 (1 << 19)
+#define UTMIP_TCTRL_USE_PMC_P1 (1 << 11)
+#define UTMIP_TCTRL_USE_PMC_P0 (1 << 3)
+#define UTMIP_RCTRL_USE_PMC(inst) (1 << ((8*(inst))+2))
+#define UTMIP_RCTRL_USE_PMC_P2 (1 << 18)
+#define UTMIP_RCTRL_USE_PMC_P1 (1 << 10)
+#define UTMIP_RCTRL_USE_PMC_P0 (1 << 2)
+#define UTMIP_FSLS_USE_PMC(inst) (1 << ((8*(inst))+1))
+#define UTMIP_FSLS_USE_PMC_P2 (1 << 17)
+#define UTMIP_FSLS_USE_PMC_P1 (1 << 9)
+#define UTMIP_FSLS_USE_PMC_P0 (1 << 1)
+#define UTMIP_MASTER_ENABLE(inst) (1 << (8*(inst)))
+#define UTMIP_MASTER_ENABLE_P2 (1 << 16)
+#define UTMIP_MASTER_ENABLE_P1 (1 << 8)
+#define UTMIP_MASTER_ENABLE_P0 (1 << 0)
+
+#define PMC_USB_AO 0xf0
+#define USBON_VAL_PD(inst) (1 << ((4*(inst))+1))
+#define USBON_VAL_PD_P2 (1 << 9)
+#define USBON_VAL_PD_P1 (1 << 5)
+#define USBON_VAL_PD_P0 (1 << 1)
+#define USBOP_VAL_PD(inst) (1 << (4*(inst)))
+#define USBOP_VAL_PD_P2 (1 << 8)
+#define USBOP_VAL_PD_P1 (1 << 4)
+#define USBOP_VAL_PD_P0 (1 << 0)
+
+#define PMC_TRIGGERS 0x1ec
+#define UTMIP_CLR_WALK_PTR(inst) (1 << (inst))
+#define UTMIP_CLR_WALK_PTR_P2 (1 << 2)
+#define UTMIP_CLR_WALK_PTR_P1 (1 << 1)
+#define UTMIP_CLR_WALK_PTR_P0 (1 << 0)
+#define UTMIP_CAP_CFG(inst) (1 << ((inst)+4))
+#define UTMIP_CAP_CFG_P2 (1 << 6)
+#define UTMIP_CAP_CFG_P1 (1 << 5)
+#define UTMIP_CAP_CFG_P0 (1 << 4)
+#define UTMIP_CLR_WAKE_ALARM(inst) (1 << ((inst)+12))
+#define UTMIP_CLR_WAKE_ALARM_P2 (1 << 14)
+
+#define PMC_PAD_CFG (0x1f4)
+
+#define PMC_UTMIP_BIAS_MASTER_CNTRL 0x30c
+#define BIAS_MASTER_PROG_VAL (1 << 1)
+
+#define PMC_SLEEPWALK_REG(inst) (0x204 + (4*(inst)))
+#define PMC_SLEEPWALK_P0 0x204
+#define PMC_SLEEPWALK_P1 0x208
+#define PMC_SLEEPWALK_P2 0x20c
+#define UTMIP_USBOP_RPD_A (1 << 0)
+#define UTMIP_USBON_RPD_A (1 << 1)
+#define UTMIP_AP_A (1 << 4)
+#define UTMIP_AN_A (1 << 5)
+#define UTMIP_HIGHZ_A (1 << 6)
+#define UTMIP_USBOP_RPD_B (1 << 8)
+#define UTMIP_USBON_RPD_B (1 << 9)
+#define UTMIP_AP_B (1 << 12)
+#define UTMIP_AN_B (1 << 13)
+#define UTMIP_HIGHZ_B (1 << 14)
+#define UTMIP_USBOP_RPD_C (1 << 16)
+#define UTMIP_USBON_RPD_C (1 << 17)
+#define UTMIP_AP_C (1 << 20)
+#define UTMIP_AN_C (1 << 21)
+#define UTMIP_HIGHZ_C (1 << 22)
+#define UTMIP_USBOP_RPD_D (1 << 24)
+#define UTMIP_USBON_RPD_D (1 << 25)
+#define UTMIP_AP_D (1 << 28)
+#define UTMIP_AN_D (1 << 29)
+#define UTMIP_HIGHZ_D (1 << 30)
+
+#define UTMIP_PMC_WAKEUP0 0x84c
+#define EVENT_INT_ENB (1 << 0)
+
+#define UTMIP_UHSIC_STATUS 0x214
+#define UTMIP_WALK_PTR_VAL(inst) (0x3 << ((inst)*2))
+#define UTMIP_USBOP_VAL(inst) (1 << ((2*(inst)) + 8))
+#define UTMIP_USBOP_VAL_P2 (1 << 12)
+#define UTMIP_USBOP_VAL_P1 (1 << 10)
+#define UTMIP_USBOP_VAL_P0 (1 << 8)
+#define UTMIP_USBON_VAL(inst) (1 << ((2*(inst)) + 9))
+#define UTMIP_USBON_VAL_P2 (1 << 13)
+#define UTMIP_USBON_VAL_P1 (1 << 11)
+#define UTMIP_USBON_VAL_P0 (1 << 9)
+#define UTMIP_WAKE_ALARM(inst) (1 << ((inst) + 16))
+#define UTMIP_WAKE_ALARM_P2 (1 << 18)
+#define UTMIP_WAKE_ALARM_P1 (1 << 17)
+#define UTMIP_WAKE_ALARM_P0 (1 << 16)
+#define UTMIP_WALK_PTR(inst) (1 << ((inst)*2))
+#define UTMIP_WALK_PTR_P2 (1 << 4)
+#define UTMIP_WALK_PTR_P1 (1 << 2)
+#define UTMIP_WALK_PTR_P0 (1 << 0)
+
+#define UTMIP_BIAS_STS0 0x840
+#define UTMIP_RCTRL_VAL(x) (((x) & 0xffff) << 0)
+#define UTMIP_TCTRL_VAL(x) (((x) & (0xffff << 16)) >> 16)
+
+#define PMC_UTMIP_TERM_PAD_CFG 0x1f8
+#define PMC_TCTRL_VAL(x) (((x) & 0x1f) << 5)
+#define PMC_RCTRL_VAL(x) (((x) & 0x1f) << 0)
+
+static u32 utmip_rctrl_val, utmip_tctrl_val;
+
+#endif
+
+/* Common registers */
+#define UTMIP_MISC_CFG0 0x824
+#define UTMIP_DPDM_OBSERVE (1 << 26)
+#define UTMIP_DPDM_OBSERVE_SEL(x) (((x) & 0xf) << 27)
+#define UTMIP_DPDM_OBSERVE_SEL_FS_J UTMIP_DPDM_OBSERVE_SEL(0xf)
+#define UTMIP_DPDM_OBSERVE_SEL_FS_K UTMIP_DPDM_OBSERVE_SEL(0xe)
+#define UTMIP_DPDM_OBSERVE_SEL_FS_SE1 UTMIP_DPDM_OBSERVE_SEL(0xd)
+#define UTMIP_DPDM_OBSERVE_SEL_FS_SE0 UTMIP_DPDM_OBSERVE_SEL(0xc)
+#define UTMIP_SUSPEND_EXIT_ON_EDGE (1 << 22)
+#define FORCE_PULLDN_DM (1 << 8)
+#define FORCE_PULLDN_DP (1 << 9)
+#define COMB_TERMS (1 << 0)
+#define ALWAYS_FREE_RUNNING_TERMS (1 << 1)
+
+#define ULPIS2S_CTRL 0x418
+#define ULPIS2S_ENA (1 << 0)
+#define ULPIS2S_SUPPORT_DISCONNECT (1 << 2)
+#define ULPIS2S_PLLU_MASTER_BLASTER60 (1 << 3)
+#define ULPIS2S_SPARE(x) (((x) & 0xF) << 8)
+#define ULPIS2S_FORCE_ULPI_CLK_OUT (1 << 12)
+#define ULPIS2S_DISCON_DONT_CHECK_SE0 (1 << 13)
+#define ULPIS2S_SUPPORT_HS_KEEP_ALIVE (1 << 14)
+#define ULPIS2S_DISABLE_STP_PU (1 << 15)
+#define ULPIS2S_SLV0_CLAMP_XMIT (1 << 16)
+
+
+#define ULPI_TIMING_CTRL_0 0x424
+#define ULPI_CLOCK_OUT_DELAY(x) ((x) & 0x1F)
+#define ULPI_OUTPUT_PINMUX_BYP (1 << 10)
+#define ULPI_CLKOUT_PINMUX_BYP (1 << 11)
+#define ULPI_SHADOW_CLK_LOOPBACK_EN (1 << 12)
+#define ULPI_SHADOW_CLK_SEL (1 << 13)
+#define ULPI_CORE_CLK_SEL (1 << 14)
+#define ULPI_SHADOW_CLK_DELAY(x) (((x) & 0x1F) << 16)
+#define ULPI_LBK_PAD_EN (1 << 26)
+#define ULPI_LBK_PAD_E_INPUT_OR (1 << 27)
+#define ULPI_CLK_OUT_ENA (1 << 28)
+#define ULPI_CLK_PADOUT_ENA (1 << 29)
+
+#define ULPI_TIMING_CTRL_1 0x428
+#define ULPI_DATA_TRIMMER_LOAD (1 << 0)
+#define ULPI_DATA_TRIMMER_SEL(x) (((x) & 0x7) << 1)
+#define ULPI_STPDIRNXT_TRIMMER_LOAD (1 << 16)
+#define ULPI_STPDIRNXT_TRIMMER_SEL(x) (((x) & 0x7) << 17)
+#define ULPI_DIR_TRIMMER_LOAD (1 << 24)
+#define ULPI_DIR_TRIMMER_SEL(x) (((x) & 0x7) << 25)
+
+#define UTMIP_SPARE_CFG0 0x834
+#define FUSE_SETUP_SEL (1 << 3)
+#define FUSE_ATERM_SEL (1 << 4)
+
+#define FUSE_USB_CALIB_0 0x1F0
+#define FUSE_USB_CALIB_XCVR_SETUP(x) (((x) & 0x7F) << 0)
+
+#define UHSIC_PLL_CFG0 0x800
+
+#define UHSIC_TX_CFG0 0x810
+#define UHSIC_HS_POSTAMBLE_OUTPUT_ENABLE (1 << 6)
+
+#define UHSIC_CMD_CFG0 0x824
+#define UHSIC_PRETEND_CONNECT_DETECT (1 << 5)
+
+#define UHSIC_SPARE_CFG0 0x82c
+
+/* These values (in milli second) are taken from the battery charging spec */
+#define TDP_SRC_ON_MS 100
+#define TDPSRC_CON_MS 40
static DEFINE_SPINLOCK(utmip_pad_lock);
static int utmip_pad_count;
@@ -146,8 +584,9 @@ struct tegra_xtal_freq {
u8 enable_delay;
u8 stable_count;
u8 active_delay;
- u8 xtal_freq_count;
+ u16 xtal_freq_count;
u16 debounce;
+ u8 pdtrk_count;
};
static const struct tegra_xtal_freq tegra_freq_table[] = {
@@ -158,6 +597,7 @@ static const struct tegra_xtal_freq tegra_freq_table[] = {
.active_delay = 0x04,
.xtal_freq_count = 0x76,
.debounce = 0x7530,
+ .pdtrk_count = 5,
},
{
.freq = 13000000,
@@ -166,6 +606,7 @@ static const struct tegra_xtal_freq tegra_freq_table[] = {
.active_delay = 0x05,
.xtal_freq_count = 0x7F,
.debounce = 0x7EF4,
+ .pdtrk_count = 5,
},
{
.freq = 19200000,
@@ -174,6 +615,7 @@ static const struct tegra_xtal_freq tegra_freq_table[] = {
.active_delay = 0x06,
.xtal_freq_count = 0xBB,
.debounce = 0xBB80,
+ .pdtrk_count = 7,
},
{
.freq = 26000000,
@@ -182,6 +624,38 @@ static const struct tegra_xtal_freq tegra_freq_table[] = {
.active_delay = 0x09,
.xtal_freq_count = 0xFE,
.debounce = 0xFDE8,
+ .pdtrk_count = 9,
+ },
+};
+
+static const struct tegra_xtal_freq tegra_uhsic_freq_table[] = {
+ {
+ .freq = 12000000,
+ .enable_delay = 0x02,
+ .stable_count = 0x2F,
+ .active_delay = 0x0,
+ .xtal_freq_count = 0x1CA,
+ },
+ {
+ .freq = 13000000,
+ .enable_delay = 0x02,
+ .stable_count = 0x33,
+ .active_delay = 0x0,
+ .xtal_freq_count = 0x1F0,
+ },
+ {
+ .freq = 19200000,
+ .enable_delay = 0x03,
+ .stable_count = 0x4B,
+ .active_delay = 0x0,
+ .xtal_freq_count = 0x2DD,
+ },
+ {
+ .freq = 26000000,
+ .enable_delay = 0x04,
+ .stable_count = 0x66,
+ .active_delay = 0x0,
+ .xtal_freq_count = 0x3E0,
},
};
@@ -192,24 +666,29 @@ static struct tegra_utmip_config utmip_default[] = {
.elastic_limit = 16,
.term_range_adj = 6,
.xcvr_setup = 9,
- .xcvr_lsfslew = 1,
- .xcvr_lsrslew = 1,
+ .xcvr_setup_offset = 0,
+ .xcvr_use_fuses = 1,
+ .xcvr_lsfslew = 2,
+ .xcvr_lsrslew = 2,
},
[2] = {
.hssync_start_delay = 9,
.idle_wait_delay = 17,
.elastic_limit = 16,
.term_range_adj = 6,
+ .xcvr_setup_offset = 0,
+ .xcvr_use_fuses = 1,
.xcvr_setup = 9,
.xcvr_lsfslew = 2,
.xcvr_lsrslew = 2,
},
};
-static inline bool phy_is_ulpi(struct tegra_usb_phy *phy)
-{
- return (phy->instance == 1);
-}
+struct usb_phy_plat_data usb_phy_data[] = {
+ { 0, 0, -1, NULL},
+ { 0, 0, -1, NULL},
+ { 0, 0, -1, NULL},
+};
static int utmip_pad_open(struct tegra_usb_phy *phy)
{
@@ -239,7 +718,7 @@ static void utmip_pad_close(struct tegra_usb_phy *phy)
clk_put(phy->pad_clk);
}
-static void utmip_pad_power_on(struct tegra_usb_phy *phy)
+static int utmip_pad_power_on(struct tegra_usb_phy *phy)
{
unsigned long val, flags;
void __iomem *base = phy->pad_regs;
@@ -248,18 +727,23 @@ static void utmip_pad_power_on(struct tegra_usb_phy *phy)
spin_lock_irqsave(&utmip_pad_lock, flags);
- if (utmip_pad_count++ == 0) {
- val = readl(base + UTMIP_BIAS_CFG0);
- val &= ~(UTMIP_OTGPD | UTMIP_BIASPD);
- writel(val, base + UTMIP_BIAS_CFG0);
- }
+ utmip_pad_count++;
+ val = readl(base + UTMIP_BIAS_CFG0);
+ val &= ~(UTMIP_OTGPD | UTMIP_BIASPD);
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+ val |= UTMIP_HSSQUELCH_LEVEL(0x2) | UTMIP_HSDISCON_LEVEL(0x1) |
+ UTMIP_HSDISCON_LEVEL_MSB;
+#endif
+ writel(val, base + UTMIP_BIAS_CFG0);
spin_unlock_irqrestore(&utmip_pad_lock, flags);
clk_disable(phy->pad_clk);
+
+ return 0;
}
-static int utmip_pad_power_off(struct tegra_usb_phy *phy)
+static int utmip_pad_power_off(struct tegra_usb_phy *phy, bool is_dpd)
{
unsigned long val, flags;
void __iomem *base = phy->pad_regs;
@@ -273,9 +757,13 @@ static int utmip_pad_power_off(struct tegra_usb_phy *phy)
spin_lock_irqsave(&utmip_pad_lock, flags);
- if (--utmip_pad_count == 0) {
+ if (--utmip_pad_count == 0 && is_dpd) {
val = readl(base + UTMIP_BIAS_CFG0);
val |= UTMIP_OTGPD | UTMIP_BIASPD;
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+ val &= ~(UTMIP_HSSQUELCH_LEVEL(~0) | UTMIP_HSDISCON_LEVEL(~0) |
+ UTMIP_HSDISCON_LEVEL_MSB);
+#endif
writel(val, base + UTMIP_BIAS_CFG0);
}
@@ -288,7 +776,7 @@ static int utmip_pad_power_off(struct tegra_usb_phy *phy)
static int utmi_wait_register(void __iomem *reg, u32 mask, u32 result)
{
- unsigned long timeout = 2000;
+ unsigned long timeout = 2500;
do {
if ((readl(reg) & mask) == result)
return 0;
@@ -302,7 +790,7 @@ static void utmi_phy_clk_disable(struct tegra_usb_phy *phy)
{
unsigned long val;
void __iomem *base = phy->regs;
-
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
if (phy->instance == 0) {
val = readl(base + USB_SUSP_CTRL);
val |= USB_SUSP_SET;
@@ -320,6 +808,11 @@ static void utmi_phy_clk_disable(struct tegra_usb_phy *phy)
val |= USB_PORTSC1_PHCD;
writel(val, base + USB_PORTSC1);
}
+#else
+ val = readl(base + HOSTPC1_DEVLC);
+ val |= HOSTPC1_DEVLC_PHCD;
+ writel(val, base + HOSTPC1_DEVLC);
+#endif
if (utmi_wait_register(base + USB_SUSP_CTRL, USB_PHY_CLK_VALID, 0) < 0)
pr_err("%s: timeout waiting for phy to stabilize\n", __func__);
@@ -342,35 +835,175 @@ static void utmi_phy_clk_enable(struct tegra_usb_phy *phy)
writel(val, base + USB_SUSP_CTRL);
}
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
if (phy->instance == 2) {
val = readl(base + USB_PORTSC1);
val &= ~USB_PORTSC1_PHCD;
writel(val, base + USB_PORTSC1);
}
+#endif
if (utmi_wait_register(base + USB_SUSP_CTRL, USB_PHY_CLK_VALID,
- USB_PHY_CLK_VALID))
+ USB_PHY_CLK_VALID) < 0)
pr_err("%s: timeout waiting for phy to stabilize\n", __func__);
}
-static int utmi_phy_power_on(struct tegra_usb_phy *phy)
+static void vbus_enable(struct tegra_usb_phy *phy)
+{
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+ int gpio_status;
+ int gpio = usb_phy_data[phy->instance].vbus_gpio;
+
+ if (gpio == -1)
+ return;
+
+ gpio_status = gpio_request(gpio,"VBUS_USB");
+ if (gpio_status < 0) {
+ printk("VBUS_USB request GPIO FAILED\n");
+ WARN_ON(1);
+ return;
+ }
+ if (gpio < TEGRA_NR_GPIOS) tegra_gpio_enable(gpio);
+ gpio_status = gpio_direction_output(gpio, 1);
+ if (gpio_status < 0) {
+ printk("VBUS_USB request GPIO DIRECTION FAILED \n");
+ WARN_ON(1);
+ return;
+ }
+ gpio_set_value_cansleep(gpio, 1);
+#else
+ if (phy->reg_vbus)
+ regulator_enable(phy->reg_vbus);
+#endif
+}
+
+static void vbus_disable(struct tegra_usb_phy *phy)
+{
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+ int gpio = usb_phy_data[phy->instance].vbus_gpio;
+
+ if (gpio == -1)
+ return;
+
+ gpio_set_value_cansleep(gpio, 0);
+ gpio_free(gpio);
+#else
+ if (phy->reg_vbus)
+ regulator_disable(phy->reg_vbus);
+#endif
+}
+
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+static void utmip_phy_enable_trking_data(struct tegra_usb_phy *phy)
+{
+ void __iomem *base = phy->pad_regs;
+ void __iomem *pmc_base = IO_ADDRESS(TEGRA_USB_BASE);
+ static bool init_done = false;
+ u32 val;
+
+ /* Should be done only once after system boot */
+ if (init_done)
+ return;
+
+ clk_enable(phy->pad_clk);
+ /* Bias pad MASTER_ENABLE=1 */
+ val = readl(pmc_base + PMC_UTMIP_BIAS_MASTER_CNTRL);
+ val |= BIAS_MASTER_PROG_VAL;
+ writel(val, pmc_base + PMC_UTMIP_BIAS_MASTER_CNTRL);
+
+ /* Setting the tracking length time */
+ val = readl(base + UTMIP_BIAS_CFG1);
+ val &= ~UTMIP_BIAS_PDTRK_COUNT(~0);
+ val |= UTMIP_BIAS_PDTRK_COUNT(5);
+ writel(val, base + UTMIP_BIAS_CFG1);
+
+ /* Bias PDTRK is Shared and MUST be done from USB1 ONLY, PD_TRK=0 */
+ val = readl(base + UTMIP_BIAS_CFG1);
+ val &= ~ UTMIP_BIAS_PDTRK_POWERDOWN;
+ writel(val, base + UTMIP_BIAS_CFG1);
+
+ val = readl(base + UTMIP_BIAS_CFG1);
+ val |= UTMIP_BIAS_PDTRK_POWERUP;
+ writel(val, base + UTMIP_BIAS_CFG1);
+
+ /* Wait for 25usec */
+ udelay(25);
+
+ /* Bias pad MASTER_ENABLE=0 */
+ val = readl(pmc_base + PMC_UTMIP_BIAS_MASTER_CNTRL);
+ val &= ~BIAS_MASTER_PROG_VAL;
+ writel(val, pmc_base + PMC_UTMIP_BIAS_MASTER_CNTRL);
+
+ /* Wait for 1usec */
+ udelay(1);
+
+ /* Bias pad MASTER_ENABLE=1 */
+ val = readl(pmc_base + PMC_UTMIP_BIAS_MASTER_CNTRL);
+ val |= BIAS_MASTER_PROG_VAL;
+ writel(val, pmc_base + PMC_UTMIP_BIAS_MASTER_CNTRL);
+
+ /* Read RCTRL and TCTRL from UTMIP space */
+ val = readl(base + UTMIP_BIAS_STS0);
+ utmip_rctrl_val = ffz(UTMIP_RCTRL_VAL(val));
+ utmip_tctrl_val = ffz(UTMIP_TCTRL_VAL(val));
+
+ /* PD_TRK=1 */
+ val = readl(base + UTMIP_BIAS_CFG1);
+ val |= UTMIP_BIAS_PDTRK_POWERDOWN;
+ writel(val, base + UTMIP_BIAS_CFG1);
+
+ /* Program thermally encoded RCTRL_VAL, TCTRL_VAL into PMC space */
+ val = readl(pmc_base + PMC_UTMIP_TERM_PAD_CFG);
+ val = PMC_TCTRL_VAL(utmip_tctrl_val) | PMC_RCTRL_VAL(utmip_rctrl_val);
+ writel(val, pmc_base + PMC_UTMIP_TERM_PAD_CFG);
+ clk_disable(phy->pad_clk);
+ init_done = true;
+}
+#endif
+
+static unsigned int tegra_phy_xcvr_setup_value(struct tegra_utmip_config *cfg)
+{
+ unsigned long val;
+
+ if (cfg->xcvr_use_fuses) {
+ val = FUSE_USB_CALIB_XCVR_SETUP(
+ tegra_fuse_readl(FUSE_USB_CALIB_0));
+ if (cfg->xcvr_setup_offset <= UTMIP_XCVR_MAX_OFFSET)
+ val = val + cfg->xcvr_setup_offset;
+
+ if (val > UTMIP_XCVR_SETUP_MAX_VALUE) {
+ val = UTMIP_XCVR_SETUP_MAX_VALUE;
+ pr_info("%s: reset XCVR_SETUP to max value\n",
+ __func__);
+ }
+ } else {
+ val = cfg->xcvr_setup;
+ }
+
+ return val;
+}
+
+static int utmi_phy_power_on(struct tegra_usb_phy *phy, bool is_dpd)
{
unsigned long val;
void __iomem *base = phy->regs;
+ unsigned int xcvr_setup_value;
struct tegra_utmip_config *config = phy->config;
val = readl(base + USB_SUSP_CTRL);
val |= UTMIP_RESET;
writel(val, base + USB_SUSP_CTRL);
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
if (phy->instance == 0) {
val = readl(base + USB1_LEGACY_CTRL);
val |= USB1_NO_LEGACY_MODE;
writel(val, base + USB1_LEGACY_CTRL);
}
+#endif
val = readl(base + UTMIP_TX_CFG0);
- val &= ~UTMIP_FS_PREABMLE_J;
+ val |= UTMIP_FS_PREABMLE_J;
writel(val, base + UTMIP_TX_CFG0);
val = readl(base + UTMIP_HSRX_CFG0);
@@ -393,6 +1026,7 @@ static int utmi_phy_power_on(struct tegra_usb_phy *phy)
val &= ~UTMIP_SUSPEND_EXIT_ON_EDGE;
writel(val, base + UTMIP_MISC_CFG0);
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
val = readl(base + UTMIP_MISC_CFG1);
val &= ~(UTMIP_PLL_ACTIVE_DLY_COUNT(~0) | UTMIP_PLLU_STABLE_COUNT(~0));
val |= UTMIP_PLL_ACTIVE_DLY_COUNT(phy->freq->active_delay) |
@@ -404,6 +1038,7 @@ static int utmi_phy_power_on(struct tegra_usb_phy *phy)
val |= UTMIP_XTAL_FREQ_COUNT(phy->freq->xtal_freq_count) |
UTMIP_PLLU_ENABLE_DLY_COUNT(phy->freq->enable_delay);
writel(val, base + UTMIP_PLL_CFG1);
+#endif
if (phy->mode == TEGRA_USB_PHY_MODE_DEVICE) {
val = readl(base + USB_SUSP_CTRL);
@@ -413,14 +1048,20 @@ static int utmi_phy_power_on(struct tegra_usb_phy *phy)
utmip_pad_power_on(phy);
+ xcvr_setup_value = phy->xcvr_setup_value;
+
val = readl(base + UTMIP_XCVR_CFG0);
- val &= ~(UTMIP_FORCE_PD_POWERDOWN | UTMIP_FORCE_PD2_POWERDOWN |
- UTMIP_FORCE_PDZI_POWERDOWN | UTMIP_XCVR_SETUP(~0) |
- UTMIP_XCVR_LSFSLEW(~0) | UTMIP_XCVR_LSRSLEW(~0) |
- UTMIP_XCVR_HSSLEW_MSB(~0));
- val |= UTMIP_XCVR_SETUP(config->xcvr_setup);
+ val &= ~(UTMIP_XCVR_LSBIAS_SEL | UTMIP_FORCE_PD_POWERDOWN |
+ UTMIP_FORCE_PD2_POWERDOWN | UTMIP_FORCE_PDZI_POWERDOWN |
+ UTMIP_XCVR_SETUP(~0) | UTMIP_XCVR_LSFSLEW(~0) |
+ UTMIP_XCVR_LSRSLEW(~0) | UTMIP_XCVR_HSSLEW_MSB(~0));
+ val |= UTMIP_XCVR_SETUP(xcvr_setup_value);
+ val |= UTMIP_XCVR_SETUP_MSB(XCVR_SETUP_MSB_CALIB(xcvr_setup_value));
val |= UTMIP_XCVR_LSFSLEW(config->xcvr_lsfslew);
val |= UTMIP_XCVR_LSRSLEW(config->xcvr_lsrslew);
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+ val |= UTMIP_XCVR_HSSLEW_MSB(0x8);
+#endif
writel(val, base + UTMIP_XCVR_CFG0);
val = readl(base + UTMIP_XCVR_CFG1);
@@ -430,28 +1071,41 @@ static int utmi_phy_power_on(struct tegra_usb_phy *phy)
writel(val, base + UTMIP_XCVR_CFG1);
val = readl(base + UTMIP_BAT_CHRG_CFG0);
- val &= ~UTMIP_PD_CHRG;
+ if (phy->mode == TEGRA_USB_PHY_MODE_HOST)
+ val |= UTMIP_PD_CHRG;
+ else
+ val &= ~UTMIP_PD_CHRG;
writel(val, base + UTMIP_BAT_CHRG_CFG0);
val = readl(base + UTMIP_BIAS_CFG1);
val &= ~UTMIP_BIAS_PDTRK_COUNT(~0);
- val |= UTMIP_BIAS_PDTRK_COUNT(0x5);
+ val |= UTMIP_BIAS_PDTRK_COUNT(phy->freq->pdtrk_count);
writel(val, base + UTMIP_BIAS_CFG1);
- if (phy->instance == 0) {
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+ val = readl(base + UTMIP_SPARE_CFG0);
+ val &= ~FUSE_SETUP_SEL;
+ writel(val, base + UTMIP_SPARE_CFG0);
+
+ if (phy->instance == 2) {
val = readl(base + UTMIP_SPARE_CFG0);
- if (phy->mode == TEGRA_USB_PHY_MODE_DEVICE)
- val &= ~FUSE_SETUP_SEL;
- else
- val |= FUSE_SETUP_SEL;
+ val |= FUSE_SETUP_SEL;
writel(val, base + UTMIP_SPARE_CFG0);
- }
- if (phy->instance == 2) {
val = readl(base + USB_SUSP_CTRL);
val |= UTMIP_PHY_ENABLE;
writel(val, base + USB_SUSP_CTRL);
}
+#else
+ val = readl(base + UTMIP_SPARE_CFG0);
+ val &= ~FUSE_SETUP_SEL;
+ val |= FUSE_ATERM_SEL;
+ writel(val, base + UTMIP_SPARE_CFG0);
+
+ val = readl(base + USB_SUSP_CTRL);
+ val |= UTMIP_PHY_ENABLE;
+ writel(val, base + USB_SUSP_CTRL);
+#endif
val = readl(base + USB_SUSP_CTRL);
val &= ~UTMIP_RESET;
@@ -463,29 +1117,202 @@ static int utmi_phy_power_on(struct tegra_usb_phy *phy)
val |= USB1_VBUS_SENSE_CTL_A_SESS_VLD;
writel(val, base + USB1_LEGACY_CTRL);
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
val = readl(base + USB_SUSP_CTRL);
val &= ~USB_SUSP_SET;
writel(val, base + USB_SUSP_CTRL);
+#endif
}
utmi_phy_clk_enable(phy);
-
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
if (phy->instance == 2) {
val = readl(base + USB_PORTSC1);
val &= ~USB_PORTSC1_PTS(~0);
writel(val, base + USB_PORTSC1);
}
+#else
+ if (phy->instance == 0)
+ utmip_phy_enable_trking_data(phy);
+
+ if(phy->instance == 2) {
+ writel(0, base + ICUSB_CTRL);
+ }
+
+ if (phy->mode == TEGRA_USB_PHY_MODE_HOST) {
+ val = readl(base + TEGRA_USB_USBMODE_REG_OFFSET);
+ writel((val | TEGRA_USB_USBMODE_HOST),
+ (base + TEGRA_USB_USBMODE_REG_OFFSET));
+ }
+ val = readl(base + HOSTPC1_DEVLC);
+ val &= ~HOSTPC1_DEVLC_PTS(~0);
+ val |= HOSTPC1_DEVLC_STS;
+ writel(val, base + HOSTPC1_DEVLC);
+#endif
return 0;
}
-static void utmi_phy_power_off(struct tegra_usb_phy *phy)
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+static void utmip_setup_pmc_wake_detect(struct tegra_usb_phy *phy)
{
- unsigned long val;
+ unsigned long val, pmc_pad_cfg_val;
+ void __iomem *pmc_base = IO_ADDRESS(TEGRA_PMC_BASE);
+ unsigned int inst = phy->instance;
void __iomem *base = phy->regs;
+ bool port_connected;
+ enum tegra_usb_phy_port_speed port_speed;
- utmi_phy_clk_disable(phy);
+ /* check for port connect status */
+ val = readl(base + USB_PORTSC1);
+ port_connected = val & USB_PORTSC1_CCS;
+
+ if (!port_connected)
+ return;
+
+ port_speed = (readl(base + HOSTPC1_DEVLC) >> 25) &
+ HOSTPC1_DEVLC_PSPD_MASK;
+ /*Set PMC MASTER bits to do the following
+ * a. Take over the UTMI drivers
+ * b. set up such that it will take over resume
+ * if remote wakeup is detected
+ * Prepare PMC to take over suspend-wake detect-drive resume until USB
+ * controller ready
+ */
+
+ /* disable master enable in PMC */
+ val = readl(pmc_base + PMC_SLEEP_CFG);
+ val &= ~UTMIP_MASTER_ENABLE(inst);
+ writel(val, pmc_base + PMC_SLEEP_CFG);
+
+ /* UTMIP_PWR_PX=1 for power savings mode */
+ val = readl(pmc_base + PMC_UTMIP_MASTER_CONFIG);
+ val |= UTMIP_PWR(inst);
+ writel(val, pmc_base + PMC_UTMIP_MASTER_CONFIG);
+
+ /* config debouncer */
+ val = readl(pmc_base + PMC_USB_DEBOUNCE);
+ val &= ~UTMIP_LINE_DEB_CNT(~0);
+ val |= UTMIP_LINE_DEB_CNT(1);
+ writel(val, pmc_base + PMC_USB_DEBOUNCE);
+
+ /* Make sure nothing is happening on the line with respect to PMC */
+ val = readl(pmc_base + PMC_UTMIP_UHSIC_FAKE);
+ val &= ~USBOP_VAL(inst);
+ val &= ~USBON_VAL(inst);
+ writel(val, pmc_base + PMC_UTMIP_UHSIC_FAKE);
+
+ /* Make sure wake value for line is none */
+ val = readl(pmc_base + PMC_SLEEPWALK_CFG);
+ val &= ~UTMIP_LINEVAL_WALK_EN(inst);
+ writel(val, pmc_base + PMC_SLEEPWALK_CFG);
+ val = readl(pmc_base + PMC_SLEEP_CFG);
+ val &= ~UTMIP_WAKE_VAL(inst, ~0);
+ val |= UTMIP_WAKE_VAL(inst, WAKE_VAL_NONE);
+ writel(val, pmc_base + PMC_SLEEP_CFG);
+
+ /* turn off pad detectors */
+ val = readl(pmc_base + PMC_USB_AO);
+ val |= (USBOP_VAL_PD(inst) | USBON_VAL_PD(inst));
+ writel(val, pmc_base + PMC_USB_AO);
+
+ /* Remove fake values and make synchronizers work a bit */
+ val = readl(pmc_base + PMC_UTMIP_UHSIC_FAKE);
+ val &= ~USBOP_VAL(inst);
+ val &= ~USBON_VAL(inst);
+ writel(val, pmc_base + PMC_UTMIP_UHSIC_FAKE);
+
+ /* Enable which type of event can trigger a walk,
+ in this case usb_line_wake */
+ val = readl(pmc_base + PMC_SLEEPWALK_CFG);
+ val |= UTMIP_LINEVAL_WALK_EN(inst);
+ writel(val, pmc_base + PMC_SLEEPWALK_CFG);
+
+ /* Enable which type of event can trigger a walk,
+ * in this case usb_line_wake */
+ val = readl(pmc_base + PMC_SLEEPWALK_CFG);
+ val |= UTMIP_LINEVAL_WALK_EN(inst);
+ writel(val, pmc_base + PMC_SLEEPWALK_CFG);
+
+ /* Clear the walk pointers and wake alarm */
+ val = readl(pmc_base + PMC_TRIGGERS);
+ val |= UTMIP_CLR_WAKE_ALARM(inst) | UTMIP_CLR_WALK_PTR(inst);
+ writel(val, pmc_base + PMC_TRIGGERS);
+
+
+ /* Capture FS/LS pad configurations */
+ pmc_pad_cfg_val = readl(pmc_base + PMC_PAD_CFG);
+ val = readl(pmc_base + PMC_TRIGGERS);
+ val |= UTMIP_CAP_CFG(inst);
+ writel(val, pmc_base + PMC_TRIGGERS);
+ udelay(1);
+ pmc_pad_cfg_val = readl(pmc_base + PMC_PAD_CFG);
+
+ /* BIAS MASTER_ENABLE=0 */
+ val = readl(pmc_base + PMC_UTMIP_BIAS_MASTER_CNTRL);
+ val &= ~BIAS_MASTER_PROG_VAL;
+ writel(val, pmc_base + PMC_UTMIP_BIAS_MASTER_CNTRL);
+
+ /* program walk sequence, maintain a J, followed by a driven K
+ * to signal a resume once an wake event is detected */
+ val = readl(pmc_base + PMC_SLEEPWALK_REG(inst));
+ val &= ~UTMIP_AP_A;
+ val |= UTMIP_USBOP_RPD_A | UTMIP_USBON_RPD_A| UTMIP_AN_A | UTMIP_HIGHZ_A |
+ UTMIP_USBOP_RPD_B | UTMIP_USBON_RPD_B | UTMIP_AP_B |
+ UTMIP_USBOP_RPD_C | UTMIP_USBON_RPD_C | UTMIP_AP_C |
+ UTMIP_USBOP_RPD_D | UTMIP_USBON_RPD_D | UTMIP_AP_D;
+ writel(val, pmc_base + PMC_SLEEPWALK_REG(inst));
+
+ if (port_speed == TEGRA_USB_PHY_PORT_SPEED_LOW) {
+ val = readl(pmc_base + PMC_SLEEPWALK_REG(inst));
+ val &= ~(UTMIP_AN_B | UTMIP_HIGHZ_B | UTMIP_AN_C |
+ UTMIP_HIGHZ_C | UTMIP_AN_D | UTMIP_HIGHZ_D);
+ writel(val, pmc_base + PMC_SLEEPWALK_REG(inst));
+ } else {
+ val = readl(pmc_base + PMC_SLEEPWALK_REG(inst));
+ val &= ~(UTMIP_AP_B | UTMIP_HIGHZ_B | UTMIP_AP_C |
+ UTMIP_HIGHZ_C | UTMIP_AP_D | UTMIP_HIGHZ_D);
+ writel(val, pmc_base + PMC_SLEEPWALK_REG(inst));
+ }
+
+ /* turn on pad detectors */
+ val = readl(pmc_base + PMC_USB_AO);
+ val &= ~(USBOP_VAL_PD(inst) | USBON_VAL_PD(inst));
+ writel(val, pmc_base + PMC_USB_AO);
+
+ /* Add small delay before usb detectors provide stable line values */
+ udelay(1);
+ /* Program thermally encoded RCTRL_VAL, TCTRL_VAL into PMC space */
+ val = readl(pmc_base + PMC_UTMIP_TERM_PAD_CFG);
+ val = PMC_TCTRL_VAL(utmip_tctrl_val) | PMC_RCTRL_VAL(utmip_rctrl_val);
+ writel(val, pmc_base + PMC_UTMIP_TERM_PAD_CFG);
+
+ phy->remote_wakeup = false;
+
+ /* Turn over pad configuration to PMC for line wake events*/
+ val = readl(pmc_base + PMC_SLEEP_CFG);
+ val &= ~UTMIP_WAKE_VAL(inst, ~0);
+ val |= UTMIP_WAKE_VAL(inst, WAKE_VAL_ANY);
+ val |= UTMIP_RCTRL_USE_PMC(inst) | UTMIP_TCTRL_USE_PMC(inst);
+ val |= UTMIP_MASTER_ENABLE(inst) | UTMIP_FSLS_USE_PMC(inst);
+ writel(val, pmc_base + PMC_SLEEP_CFG);
+
+ val = readl(base + UTMIP_PMC_WAKEUP0);
+ val |= EVENT_INT_ENB;
+ writel(val, base + UTMIP_PMC_WAKEUP0);
+}
+#endif
+
+static int utmi_phy_power_off(struct tegra_usb_phy *phy, bool is_dpd)
+{
+ unsigned long val;
+ void __iomem *base = phy->regs;
+
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+ if (phy->mode == TEGRA_USB_PHY_MODE_HOST)
+ utmip_setup_pmc_wake_detect(phy);
+#endif
if (phy->mode == TEGRA_USB_PHY_MODE_DEVICE) {
val = readl(base + USB_SUSP_CTRL);
val &= ~USB_WAKEUP_DEBOUNCE_COUNT(~0);
@@ -493,50 +1320,187 @@ static void utmi_phy_power_off(struct tegra_usb_phy *phy)
writel(val, base + USB_SUSP_CTRL);
}
- val = readl(base + USB_SUSP_CTRL);
- val |= UTMIP_RESET;
- writel(val, base + USB_SUSP_CTRL);
-
- val = readl(base + UTMIP_BAT_CHRG_CFG0);
- val |= UTMIP_PD_CHRG;
- writel(val, base + UTMIP_BAT_CHRG_CFG0);
-
- val = readl(base + UTMIP_XCVR_CFG0);
- val |= UTMIP_FORCE_PD_POWERDOWN | UTMIP_FORCE_PD2_POWERDOWN |
- UTMIP_FORCE_PDZI_POWERDOWN;
- writel(val, base + UTMIP_XCVR_CFG0);
+ if (phy->mode == TEGRA_USB_PHY_MODE_DEVICE) {
+ val = readl(base + UTMIP_BAT_CHRG_CFG0);
+ val |= UTMIP_PD_CHRG;
+ writel(val, base + UTMIP_BAT_CHRG_CFG0);
+ }
+ if (phy->instance != 2) {
+ val = readl(base + UTMIP_XCVR_CFG0);
+ val |= (UTMIP_FORCE_PD_POWERDOWN | UTMIP_FORCE_PD2_POWERDOWN |
+ UTMIP_FORCE_PDZI_POWERDOWN);
+ writel(val, base + UTMIP_XCVR_CFG0);
+ }
val = readl(base + UTMIP_XCVR_CFG1);
val |= UTMIP_FORCE_PDDISC_POWERDOWN | UTMIP_FORCE_PDCHRP_POWERDOWN |
UTMIP_FORCE_PDDR_POWERDOWN;
writel(val, base + UTMIP_XCVR_CFG1);
- utmip_pad_power_off(phy);
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+ val = readl(base + UTMIP_BIAS_CFG1);
+ val |= UTMIP_BIAS_PDTRK_COUNT(0x5);
+ writel(val, base + UTMIP_BIAS_CFG1);
+#endif
+
+ if (phy->hotplug) {
+ val = readl(base + USB_PORTSC1);
+ val |= USB_PORTSC1_WKCN;
+ writel(val, base + USB_PORTSC1);
+ }
+ if (phy->instance != 0) {
+ val = readl(base + UTMIP_BIAS_CFG0);
+ val |= UTMIP_OTGPD;
+ writel(val, base + UTMIP_BIAS_CFG0);
+ }
+
+ utmi_phy_clk_disable(phy);
+
+ if (phy->hotplug) {
+ val = readl(base + USB_SUSP_CTRL);
+ val |= USB_PHY_CLK_VALID_INT_ENB;
+ writel(val, base + USB_SUSP_CTRL);
+ } else {
+ val = readl(base + USB_SUSP_CTRL);
+ val |= UTMIP_RESET;
+ writel(val, base + USB_SUSP_CTRL);
+ }
+ utmip_pad_power_off(phy, true);
+ return 0;
}
-static void utmi_phy_preresume(struct tegra_usb_phy *phy)
+static void utmip_phy_disable_pmc_bus_ctrl(struct tegra_usb_phy *phy)
{
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
unsigned long val;
+ void __iomem *pmc_base = IO_ADDRESS(TEGRA_PMC_BASE);
+ unsigned int inst = phy->instance;
void __iomem *base = phy->regs;
+ val = readl(pmc_base + PMC_SLEEP_CFG);
+ val &= ~UTMIP_WAKE_VAL(inst, 0x0);
+ val |= UTMIP_WAKE_VAL(inst, WAKE_VAL_NONE);
+ writel(val, pmc_base + PMC_SLEEP_CFG);
+
+ val = readl(pmc_base + PMC_TRIGGERS);
+ val |= UTMIP_CLR_WAKE_ALARM(inst) | UTMIP_CLR_WALK_PTR(inst);
+ writel(val, pmc_base + PMC_TRIGGERS);
+
+ val = readl(base + UTMIP_PMC_WAKEUP0);
+ val &= ~EVENT_INT_ENB;
+ writel(val, base + UTMIP_PMC_WAKEUP0);
+
+ /* Disable PMC master mode by clearing MASTER_EN */
+ val = readl(pmc_base + PMC_SLEEP_CFG);
+ val &= ~(UTMIP_RCTRL_USE_PMC(inst) | UTMIP_TCTRL_USE_PMC(inst) |
+ UTMIP_FSLS_USE_PMC(inst) | UTMIP_MASTER_ENABLE(inst));
+ writel(val, pmc_base + PMC_SLEEP_CFG);
+
+ val = readl(pmc_base + PMC_TRIGGERS);
+ val &= ~UTMIP_CAP_CFG(inst);
+ writel(val, pmc_base + PMC_TRIGGERS);
+
+ /* turn off pad detectors */
+ val = readl(pmc_base + PMC_USB_AO);
+ val |= (USBOP_VAL_PD(inst) | USBON_VAL_PD(inst));
+ writel(val, pmc_base + PMC_USB_AO);
+
+ phy->remote_wakeup = false;
+#endif
+}
+
+static int utmi_phy_preresume(struct tegra_usb_phy *phy, bool is_dpd)
+{
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+ unsigned long val;
+ void __iomem *base = phy->regs;
val = readl(base + UTMIP_TX_CFG0);
val |= UTMIP_HS_DISCON_DISABLE;
writel(val, base + UTMIP_TX_CFG0);
+#else
+ utmip_phy_disable_pmc_bus_ctrl(phy);
+#endif
+
+ return 0;
}
-static void utmi_phy_postresume(struct tegra_usb_phy *phy)
+static int utmi_phy_postresume(struct tegra_usb_phy *phy, bool is_dpd)
{
unsigned long val;
void __iomem *base = phy->regs;
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+ /* check if OBS bus is already enabled */
+ val = readl(base + UTMIP_MISC_CFG0);
+ if (val & UTMIP_DPDM_OBSERVE) {
+ /* Change the UTMIP OBS bus to drive SE0 */
+ val = readl(base + UTMIP_MISC_CFG0);
+ val &= ~UTMIP_DPDM_OBSERVE_SEL(~0);
+ val |= UTMIP_DPDM_OBSERVE_SEL_FS_SE0;
+ writel(val, base + UTMIP_MISC_CFG0);
+
+ /* Wait for 3us(2 LS bit times) */
+ udelay (3);
+
+ /* Release UTMIP OBS bus */
+ val = readl(base + UTMIP_MISC_CFG0);
+ val &= ~UTMIP_DPDM_OBSERVE;
+ writel(val, base + UTMIP_MISC_CFG0);
+
+ /* Release DP/DM pulldown for Host mode */
+ val = readl(base + UTMIP_MISC_CFG0);
+ val &= ~(FORCE_PULLDN_DM | FORCE_PULLDN_DP |
+ COMB_TERMS | ALWAYS_FREE_RUNNING_TERMS);
+ writel(val, base + UTMIP_MISC_CFG0);
+ }
+#else
val = readl(base + UTMIP_TX_CFG0);
val &= ~UTMIP_HS_DISCON_DISABLE;
writel(val, base + UTMIP_TX_CFG0);
+#endif
+ return 0;
+}
+
+static int uhsic_phy_postsuspend(struct tegra_usb_phy *phy, bool is_dpd)
+{
+ struct tegra_uhsic_config *uhsic_config = phy->config;
+
+ if (uhsic_config->postsuspend)
+ uhsic_config->postsuspend();
+
+ return 0;
+}
+
+static int uhsic_phy_preresume(struct tegra_usb_phy *phy, bool is_dpd)
+{
+ struct tegra_uhsic_config *uhsic_config = phy->config;
+
+ if (uhsic_config->preresume)
+ uhsic_config->preresume();
+
+ return 0;
+}
+
+static int uhsic_phy_postresume(struct tegra_usb_phy *phy, bool is_dpd)
+{
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+ unsigned long val;
+ void __iomem *base = phy->regs;
+
+ val = readl(base + USB_TXFILLTUNING);
+ if ((val & USB_FIFO_TXFILL_MASK) != USB_FIFO_TXFILL_THRES(0x10)) {
+ val = USB_FIFO_TXFILL_THRES(0x10);
+ writel(val, base + USB_TXFILLTUNING);
+ }
+#endif
+
+ return 0;
}
static void utmi_phy_restore_start(struct tegra_usb_phy *phy,
enum tegra_usb_phy_port_speed port_speed)
{
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
unsigned long val;
void __iomem *base = phy->regs;
@@ -553,10 +1517,56 @@ static void utmi_phy_restore_start(struct tegra_usb_phy *phy,
val |= UTMIP_DPDM_OBSERVE;
writel(val, base + UTMIP_MISC_CFG0);
udelay(10);
+#else
+ unsigned long val;
+ void __iomem *base = phy->regs;
+ void __iomem *pmc_base = IO_ADDRESS(TEGRA_PMC_BASE);
+ int inst = phy->instance;
+
+ val = readl(pmc_base + UTMIP_UHSIC_STATUS);
+ /* check whether we wake up from the remote resume */
+ if (UTMIP_WALK_PTR_VAL(inst) & val) {
+ phy->remote_wakeup = true;
+ } else {
+ if (!((UTMIP_USBON_VAL(phy->instance) |
+ UTMIP_USBOP_VAL(phy->instance)) & val)) {
+ utmip_phy_disable_pmc_bus_ctrl(phy);
+ }
+ }
+
+ /* (2LS WAR)is not required for LS and FS devices and is only for HS */
+ if ((port_speed == TEGRA_USB_PHY_PORT_SPEED_LOW) ||
+ (port_speed == TEGRA_USB_PHY_PORT_SPEED_FULL)) {
+ /* do not enable the OBS bus */
+ val = readl(base + UTMIP_MISC_CFG0);
+ val &= ~UTMIP_DPDM_OBSERVE_SEL(~0);
+ writel(val, base + UTMIP_MISC_CFG0);
+ return;
+ }
+ /* Force DP/DM pulldown active for Host mode */
+ val = readl(base + UTMIP_MISC_CFG0);
+ val |= FORCE_PULLDN_DM | FORCE_PULLDN_DP |
+ COMB_TERMS | ALWAYS_FREE_RUNNING_TERMS;
+ writel(val, base + UTMIP_MISC_CFG0);
+ val = readl(base + UTMIP_MISC_CFG0);
+ val &= ~UTMIP_DPDM_OBSERVE_SEL(~0);
+ if (port_speed == TEGRA_USB_PHY_PORT_SPEED_LOW)
+ val |= UTMIP_DPDM_OBSERVE_SEL_FS_J;
+ else
+ val |= UTMIP_DPDM_OBSERVE_SEL_FS_K;
+ writel(val, base + UTMIP_MISC_CFG0);
+ udelay(1);
+
+ val = readl(base + UTMIP_MISC_CFG0);
+ val |= UTMIP_DPDM_OBSERVE;
+ writel(val, base + UTMIP_MISC_CFG0);
+ udelay(10);
+#endif
}
static void utmi_phy_restore_end(struct tegra_usb_phy *phy)
{
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
unsigned long val;
void __iomem *base = phy->regs;
@@ -564,25 +1574,169 @@ static void utmi_phy_restore_end(struct tegra_usb_phy *phy)
val &= ~UTMIP_DPDM_OBSERVE;
writel(val, base + UTMIP_MISC_CFG0);
udelay(10);
+#else
+ unsigned long val;
+ void __iomem *base = phy->regs;
+ int wait_time_us = 3000; /* FPR should be set by this time */
+
+ /* check whether we wake up from the remote resume */
+ if (phy->remote_wakeup) {
+ /* wait until FPR bit is set automatically on remote resume */
+ do {
+ val = readl(base + USB_PORTSC1);
+ udelay(1);
+ if (wait_time_us == 0) {
+ utmip_phy_disable_pmc_bus_ctrl(phy);
+ tegra_usb_phy_postresume(phy, false);
+ return;
+ }
+ wait_time_us--;
+ } while (!(val & USB_PORTSC1_RESUME));
+ /* disable PMC master control */
+ utmip_phy_disable_pmc_bus_ctrl(phy);
+ /* wait for 25 ms to port resume complete */
+ msleep(25);
+
+ /* Clear PCI and SRI bits to avoid an interrupt upon resume */
+ val = readl(base + USB_USBSTS);
+ writel(val, base + USB_USBSTS);
+ /* wait to avoid SOF if there is any */
+ if (utmi_wait_register(base + USB_USBSTS,
+ USB_USBSTS_SRI, USB_USBSTS_SRI) < 0) {
+ pr_err("%s: timeout waiting for SOF\n", __func__);
+ }
+ tegra_usb_phy_postresume(phy, false);
+ }
+#endif
+}
+
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+static void ulpi_set_tristate(bool enable)
+{
+ int tristate = (enable)? TEGRA_TRI_TRISTATE : TEGRA_TRI_NORMAL;
+
+ tegra_pinmux_set_tristate(TEGRA_PINGROUP_UAA, tristate);
+ tegra_pinmux_set_tristate(TEGRA_PINGROUP_UAB, tristate);
+ tegra_pinmux_set_tristate(TEGRA_PINGROUP_UDA, tristate);
+}
+#endif
+
+static void ulpi_phy_reset(void __iomem *base)
+{
+ unsigned long val;
+
+ val = readl(base + USB_SUSP_CTRL);
+ val |= UHSIC_RESET;
+ writel(val, base + USB_SUSP_CTRL);
+
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+ val = readl(base + USB_SUSP_CTRL);
+ val |= UTMIP_RESET;
+ writel(val, base + USB_SUSP_CTRL);
+#endif
+}
+
+static void ulpi_set_host(void __iomem *base)
+{
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+ unsigned long val;
+
+ val = readl(base + TEGRA_USB_USBMODE_REG_OFFSET);
+ val |= TEGRA_USB_USBMODE_HOST;
+ writel(val, base + TEGRA_USB_USBMODE_REG_OFFSET);
+
+ val = readl(base + HOSTPC1_DEVLC);
+ val |= HOSTPC1_DEVLC_PTS(2);
+ writel(val, base + HOSTPC1_DEVLC);
+#endif
+}
+
+static void ulpi_set_trimmer(void __iomem *base, u8 data, u8 sdn, u8 dir)
+{
+ unsigned long val;
+
+ val = ULPI_DATA_TRIMMER_SEL(data);
+ val |= ULPI_STPDIRNXT_TRIMMER_SEL(sdn);
+ val |= ULPI_DIR_TRIMMER_SEL(dir);
+ writel(val, base + ULPI_TIMING_CTRL_1);
+ udelay(10);
+
+ val |= ULPI_DATA_TRIMMER_LOAD;
+ val |= ULPI_STPDIRNXT_TRIMMER_LOAD;
+ val |= ULPI_DIR_TRIMMER_LOAD;
+ writel(val, base + ULPI_TIMING_CTRL_1);
+}
+
+static inline void ulpi_pinmux_bypass(struct tegra_usb_phy *phy, bool enable)
+{
+ unsigned long val;
+ void __iomem *base = phy->regs;
+
+ val = readl(base + ULPI_TIMING_CTRL_0);
+
+ if (enable)
+ val |= ULPI_OUTPUT_PINMUX_BYP;
+ else
+ val &= ~ULPI_OUTPUT_PINMUX_BYP;
+
+ writel(val, base + ULPI_TIMING_CTRL_0);
}
-static int ulpi_phy_power_on(struct tegra_usb_phy *phy)
+static void ulpi_phy_restore_start(struct tegra_usb_phy *phy,
+ enum tegra_usb_phy_port_speed port_speed)
+{
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+ unsigned long val;
+ void __iomem *base = phy->regs;
+
+ /*Tristate ulpi interface before USB controller resume*/
+ ulpi_set_tristate(true);
+
+ val = readl(base + ULPI_TIMING_CTRL_0);
+ val &= ~ULPI_OUTPUT_PINMUX_BYP;
+ writel(val, base + ULPI_TIMING_CTRL_0);
+#endif
+}
+
+static void ulpi_phy_restore_end(struct tegra_usb_phy *phy)
+{
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+ unsigned long val;
+ void __iomem *base = phy->regs;
+
+ val = readl(base + ULPI_TIMING_CTRL_0);
+ val |= ULPI_OUTPUT_PINMUX_BYP;
+ writel(val, base + ULPI_TIMING_CTRL_0);
+
+ ulpi_set_tristate(false);
+#endif
+}
+
+static int ulpi_phy_power_on(struct tegra_usb_phy *phy, bool is_dpd)
{
int ret;
unsigned long val;
void __iomem *base = phy->regs;
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
struct tegra_ulpi_config *config = phy->config;
+#endif
- gpio_direction_output(config->reset_gpio, 0);
- msleep(5);
- gpio_direction_output(config->reset_gpio, 1);
+ if (phy->clk)
+ clk_enable(phy->clk);
- clk_enable(phy->clk);
msleep(1);
- val = readl(base + USB_SUSP_CTRL);
- val |= UHSIC_RESET;
- writel(val, base + USB_SUSP_CTRL);
+ if (!phy->initialized) {
+ phy->initialized = 1;
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+ gpio_direction_output(config->reset_gpio, 0);
+ msleep(5);
+ gpio_direction_output(config->reset_gpio, 1);
+#endif
+ }
+
+ ulpi_phy_reset(base);
+ ulpi_set_host(base);
val = readl(base + ULPI_TIMING_CTRL_0);
val |= ULPI_OUTPUT_PINMUX_BYP | ULPI_CLKOUT_PINMUX_BYP;
@@ -592,20 +1746,30 @@ static int ulpi_phy_power_on(struct tegra_usb_phy *phy)
val |= ULPI_PHY_ENABLE;
writel(val, base + USB_SUSP_CTRL);
- val = 0;
- writel(val, base + ULPI_TIMING_CTRL_1);
+ val = readl(base + USB_SUSP_CTRL);
+ val |= USB_SUSP_CLR;
+ writel(val, base + USB_SUSP_CTRL);
- val |= ULPI_DATA_TRIMMER_SEL(4);
- val |= ULPI_STPDIRNXT_TRIMMER_SEL(4);
- val |= ULPI_DIR_TRIMMER_SEL(4);
- writel(val, base + ULPI_TIMING_CTRL_1);
- udelay(10);
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+ if (utmi_wait_register(base + USB_SUSP_CTRL, USB_PHY_CLK_VALID,
+ USB_PHY_CLK_VALID) < 0)
+ pr_err("%s: timeout waiting for phy to stabilize\n", __func__);
- val |= ULPI_DATA_TRIMMER_LOAD;
- val |= ULPI_STPDIRNXT_TRIMMER_LOAD;
- val |= ULPI_DIR_TRIMMER_LOAD;
+ if (utmi_wait_register(base + USB_SUSP_CTRL, USB_CLKEN, USB_CLKEN) < 0)
+ pr_err("%s: timeout waiting for AHB clock\n", __func__);
+#else
+ udelay(100);
+#endif
+
+ val = readl(base + USB_SUSP_CTRL);
+ val &= ~USB_SUSP_CLR;
+ writel(val, base + USB_SUSP_CTRL);
+
+ val = 0;
writel(val, base + ULPI_TIMING_CTRL_1);
+ ulpi_set_trimmer(base, 4, 4, 4);
+
/* Fix VbusInvalid due to floating VBUS */
ret = otg_io_write(phy->ulpi, 0x40, 0x08);
if (ret) {
@@ -623,45 +1787,463 @@ static int ulpi_phy_power_on(struct tegra_usb_phy *phy)
val |= USB_PORTSC1_WKOC | USB_PORTSC1_WKDS | USB_PORTSC1_WKCN;
writel(val, base + USB_PORTSC1);
+ return 0;
+}
+
+static int ulpi_phy_power_off(struct tegra_usb_phy *phy, bool is_dpd)
+{
+ unsigned long val;
+ void __iomem *base = phy->regs;
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+ int ret;
+
+ /* Disable VbusValid, SessEnd comparators */
+ ret = otg_io_write(phy->ulpi, 0x00, 0x0D);
+ if (ret)
+ pr_err("%s: ulpi write 0x0D failed\n", __func__);
+
+ ret = otg_io_write(phy->ulpi, 0x00, 0x10);
+ if (ret)
+ pr_err("%s: ulpi write 0x10 failed\n", __func__);
+
+ /* Disable IdFloat comparator */
+ ret = otg_io_write(phy->ulpi, 0x00, 0x19);
+ if (ret)
+ pr_err("%s: ulpi write 0x19 failed\n", __func__);
+
+ ret = otg_io_write(phy->ulpi, 0x00, 0x1D);
+ if (ret)
+ pr_err("%s: ulpi write 0x1D failed\n", __func__);
+
+ /* Clear WKCN/WKDS/WKOC wake-on events that can cause the USB
+ * Controller to immediately bring the ULPI PHY out of low power
+ */
+ val = readl(base + USB_PORTSC1);
+ val &= ~(USB_PORTSC1_WKOC | USB_PORTSC1_WKDS | USB_PORTSC1_WKCN);
+ writel(val, base + USB_PORTSC1);
+
+ /* Put the PHY in the low power mode */
+ val = readl(base + USB_PORTSC1);
+ val |= USB_PORTSC1_PHCD;
+ writel(val, base + USB_PORTSC1);
+
+ if (utmi_wait_register(base + USB_SUSP_CTRL, USB_PHY_CLK_VALID, 0) < 0)
+ pr_err("%s: timeout waiting for phy to stop\n", __func__);
+#else
+ val = readl(base + HOSTPC1_DEVLC);
+ val &= ~(HOSTPC1_DEVLC_PHCD);
+ writel(val, base + HOSTPC1_DEVLC);
+#endif
+
+ if(phy->clk)
+ clk_disable(phy->clk);
+
+ return 0;
+}
+
+static inline void null_phy_set_tristate(bool enable)
+{
+ int tristate = (enable) ? TEGRA_TRI_TRISTATE : TEGRA_TRI_NORMAL;
+
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+ tegra_pinmux_set_tristate(TEGRA_PINGROUP_UAA, tristate);
+ tegra_pinmux_set_tristate(TEGRA_PINGROUP_UAB, tristate);
+ tegra_pinmux_set_tristate(TEGRA_PINGROUP_UDA, tristate);
+#else
+ tegra_pinmux_set_tristate(TEGRA_PINGROUP_ULPI_DATA0, tristate);
+ tegra_pinmux_set_tristate(TEGRA_PINGROUP_ULPI_DATA1, tristate);
+ tegra_pinmux_set_tristate(TEGRA_PINGROUP_ULPI_DATA2, tristate);
+ tegra_pinmux_set_tristate(TEGRA_PINGROUP_ULPI_DATA3, tristate);
+ tegra_pinmux_set_tristate(TEGRA_PINGROUP_ULPI_DATA4, tristate);
+ tegra_pinmux_set_tristate(TEGRA_PINGROUP_ULPI_DATA5, tristate);
+ tegra_pinmux_set_tristate(TEGRA_PINGROUP_ULPI_DATA6, tristate);
+ tegra_pinmux_set_tristate(TEGRA_PINGROUP_ULPI_DATA7, tristate);
+ tegra_pinmux_set_tristate(TEGRA_PINGROUP_ULPI_NXT, tristate);
+
+ if (enable)
+ tegra_pinmux_set_tristate(TEGRA_PINGROUP_ULPI_DIR, tristate);
+#endif
+}
+
+static void null_phy_restore_start(struct tegra_usb_phy *phy,
+ enum tegra_usb_phy_port_speed port_speed)
+{
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+ struct tegra_ulpi_config *config = phy->config;
+
+ if (config->phy_restore_start)
+ config->phy_restore_start();
+#endif
+}
+
+static void null_phy_restore_end(struct tegra_usb_phy *phy)
+{
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+ unsigned long val;
+ void __iomem *base = phy->regs;
+ struct tegra_ulpi_config *config = phy->config;
+ int retry = 20000;
+
+ /* disable ULPI pinmux bypass */
+ ulpi_pinmux_bypass(phy, false);
+
+ /* driving linestate using GPIO */
+ gpio_set_value(TEGRA_GPIO_PO1, 0);
+ gpio_set_value(TEGRA_GPIO_PO2, 0);
+
+ /* remove ULPI tristate except DIR */
+ null_phy_set_tristate(false);
+
+ if (config->phy_restore_end)
+ config->phy_restore_end();
+
+ while (retry) {
+#if TRIGGER_BY_MDM2AP_ACK
+ if (gpio_get_value(TEGRA_GPIO_PU5)) /* poll MDM2AP_ACK high */
+ break;
+#else
+ if (gpio_get_value(TEGRA_GPIO_PY3)) /* poll STP high */
+ break;
+#endif
+ retry--;
+ }
+
+ if (retry == 0)
+ pr_info("MDM2AP_ACK timeout\n");
+
+ /* enable ULPI CLK output pad */
+ val = readl(base + ULPI_TIMING_CTRL_0);
+ val |= ULPI_CLK_PADOUT_ENA;
+ writel(val, base + ULPI_TIMING_CTRL_0);
+
+ /* enable ULPI pinmux bypass */
+ ulpi_pinmux_bypass(phy, true);
+ udelay(5);
+
+ /* remove DIR tristate */
+ tegra_pinmux_set_tristate(TEGRA_PINGROUP_ULPI_DIR, TEGRA_TRI_NORMAL);
+#endif
+}
+
+static int null_phy_power_on(struct tegra_usb_phy *phy, bool is_dpd)
+{
+ const struct tegra_ulpi_trimmer default_trimmer = {0, 0, 4, 4};
+ unsigned long val;
+ void __iomem *base = phy->regs;
+ struct tegra_ulpi_config *config = phy->config;
+ static bool cold_boot = true;
+
+ if (!config->trimmer)
+ config->trimmer = &default_trimmer;
+
+ ulpi_phy_reset(base);
+
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+ /* remove ULPI PADS CLKEN reset */
val = readl(base + USB_SUSP_CTRL);
- val |= USB_SUSP_CLR;
+ val &= ~ULPI_PADS_CLKEN_RESET;
writel(val, base + USB_SUSP_CTRL);
- udelay(100);
+ udelay(10);
+#endif
+ val = readl(base + ULPI_TIMING_CTRL_0);
+ val |= ULPI_OUTPUT_PINMUX_BYP | ULPI_CLKOUT_PINMUX_BYP;
+ writel(val, base + ULPI_TIMING_CTRL_0);
+
+ if (config->pre_phy_on && config->pre_phy_on())
+ return -EAGAIN;
val = readl(base + USB_SUSP_CTRL);
- val &= ~USB_SUSP_CLR;
+ val |= ULPI_PHY_ENABLE;
+ writel(val, base + USB_SUSP_CTRL);
+ udelay(10);
+
+ /* set timming parameters */
+ val = readl(base + ULPI_TIMING_CTRL_0);
+ val |= ULPI_SHADOW_CLK_LOOPBACK_EN;
+ val &= ~ULPI_SHADOW_CLK_SEL;
+ val &= ~ULPI_LBK_PAD_EN;
+ val |= ULPI_SHADOW_CLK_DELAY(config->trimmer->shadow_clk_delay);
+ val |= ULPI_CLOCK_OUT_DELAY(config->trimmer->clock_out_delay);
+ val |= ULPI_LBK_PAD_E_INPUT_OR;
+ writel(val, base + ULPI_TIMING_CTRL_0);
+
+ writel(0, base + ULPI_TIMING_CTRL_1);
+ udelay(10);
+
+ /* start internal 60MHz clock */
+ val = readl(base + ULPIS2S_CTRL);
+ val |= ULPIS2S_ENA;
+ val |= ULPIS2S_SUPPORT_DISCONNECT;
+ val |= ULPIS2S_SPARE((phy->mode == TEGRA_USB_PHY_MODE_HOST) ? 3 : 1);
+ val |= ULPIS2S_PLLU_MASTER_BLASTER60;
+ writel(val, base + ULPIS2S_CTRL);
+
+ /* select ULPI_CORE_CLK_SEL to SHADOW_CLK */
+ val = readl(base + ULPI_TIMING_CTRL_0);
+ val |= ULPI_CORE_CLK_SEL;
+ writel(val, base + ULPI_TIMING_CTRL_0);
+ udelay(10);
+
+ /* enable ULPI null phy clock - can't set the trimmers before this */
+ val = readl(base + ULPI_TIMING_CTRL_0);
+ val |= ULPI_CLK_OUT_ENA;
+ writel(val, base + ULPI_TIMING_CTRL_0);
+ udelay(10);
+
+ if (utmi_wait_register(base + USB_SUSP_CTRL, USB_PHY_CLK_VALID,
+ USB_PHY_CLK_VALID)) {
+ pr_err("%s: timeout waiting for phy to stabilize\n", __func__);
+ return -ETIMEDOUT;
+ }
+
+ /* set ULPI trimmers */
+ ulpi_set_trimmer(base, config->trimmer->data_trimmer,
+ config->trimmer->stpdirnxt_trimmer, 1);
+
+ ulpi_set_host(base);
+
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+ /* remove slave0 reset */
+ val = readl(base + USB_SUSP_CTRL);
+ val &= ~ULPIS2S_SLV0_RESET;
writel(val, base + USB_SUSP_CTRL);
+ /* remove slave1 and line reset */
+ val = readl(base + USB_SUSP_CTRL);
+ val &= ~ULPIS2S_SLV1_RESET;
+ val &= ~ULPIS2S_LINE_RESET;
+
+ /* remove ULPI PADS reset */
+ val &= ~ULPI_PADS_RESET;
+ writel(val, base + USB_SUSP_CTRL);
+#endif
+ if (cold_boot) {
+ val = readl(base + ULPI_TIMING_CTRL_0);
+ val |= ULPI_CLK_PADOUT_ENA;
+ writel(val, base + ULPI_TIMING_CTRL_0);
+ cold_boot = false;
+ }
+
+ udelay(10);
+
+ if (config->post_phy_on && config->post_phy_on())
+ return -EAGAIN;
+
return 0;
}
-static void ulpi_phy_power_off(struct tegra_usb_phy *phy)
+static int null_phy_power_off(struct tegra_usb_phy *phy, bool is_dpd)
{
+ struct tegra_ulpi_config *config = phy->config;
+
+ if (config->pre_phy_off && config->pre_phy_off())
+ return -EAGAIN;
+
+ null_phy_set_tristate(true);
+
+ if (config->post_phy_off && config->post_phy_off())
+ return -EAGAIN;
+
+ return 0;
+}
+
+static int null_phy_pre_usbcmd_reset(struct tegra_usb_phy *phy, bool is_dpd)
+{
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
unsigned long val;
void __iomem *base = phy->regs;
- struct tegra_ulpi_config *config = phy->config;
- /* Clear WKCN/WKDS/WKOC wake-on events that can cause the USB
- * Controller to immediately bring the ULPI PHY out of low power
- */
+ val = readl(base + ULPIS2S_CTRL);
+ val |= ULPIS2S_SLV0_CLAMP_XMIT;
+ writel(val, base + ULPIS2S_CTRL);
+
+ val = readl(base + USB_SUSP_CTRL);
+ val |= ULPIS2S_SLV0_RESET;
+ writel(val, base + USB_SUSP_CTRL);
+ udelay(10);
+#endif
+ return 0;
+}
+
+static int null_phy_post_usbcmd_reset(struct tegra_usb_phy *phy, bool is_dpd)
+{
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+ unsigned long val;
+ void __iomem *base = phy->regs;
+
+ ulpi_set_host(base);
+
+ /* remove slave0 reset */
+ val = readl(base + USB_SUSP_CTRL);
+ val &= ~ULPIS2S_SLV0_RESET;
+ writel(val, base + USB_SUSP_CTRL);
+
+ val = readl(base + ULPIS2S_CTRL);
+ val &= ~ULPIS2S_SLV0_CLAMP_XMIT;
+ writel(val, base + ULPIS2S_CTRL);
+ udelay(10);
+#endif
+ return 0;
+}
+
+static int uhsic_phy_power_on(struct tegra_usb_phy *phy, bool is_dpd)
+{
+ unsigned long val;
+ void __iomem *base = phy->regs;
+ struct tegra_uhsic_config *uhsic_config = phy->config;
+
+ if (uhsic_config->enable_gpio != -1) {
+ gpio_set_value_cansleep(uhsic_config->enable_gpio, 1);
+ /* keep hsic reset asserted for 1 ms */
+ udelay(1000);
+ }
+
+ val = readl(base + UHSIC_PADS_CFG1);
+ val &= ~(UHSIC_PD_BG | UHSIC_PD_TX | UHSIC_PD_TRK | UHSIC_PD_RX |
+ UHSIC_PD_ZI | UHSIC_RPD_DATA | UHSIC_RPD_STROBE);
+ val |= UHSIC_RX_SEL;
+ writel(val, base + UHSIC_PADS_CFG1);
+ udelay(2);
+
+ val = readl(base + USB_SUSP_CTRL);
+ val |= UHSIC_RESET;
+ writel(val, base + USB_SUSP_CTRL);
+ udelay(30);
+
+ val = readl(base + USB_SUSP_CTRL);
+ val |= UHSIC_PHY_ENABLE;
+ writel(val, base + USB_SUSP_CTRL);
+
+ val = readl(base + UHSIC_HSRX_CFG0);
+ val |= UHSIC_IDLE_WAIT(uhsic_config->idle_wait_delay);
+ val |= UHSIC_ELASTIC_UNDERRUN_LIMIT(uhsic_config->elastic_underrun_limit);
+ val |= UHSIC_ELASTIC_OVERRUN_LIMIT(uhsic_config->elastic_overrun_limit);
+ writel(val, base + UHSIC_HSRX_CFG0);
+
+ val = readl(base + UHSIC_HSRX_CFG1);
+ val |= UHSIC_HS_SYNC_START_DLY(uhsic_config->sync_start_delay);
+ writel(val, base + UHSIC_HSRX_CFG1);
+
+ val = readl(base + UHSIC_MISC_CFG0);
+ val |= UHSIC_SUSPEND_EXIT_ON_EDGE;
+ writel(val, base + UHSIC_MISC_CFG0);
+
+ val = readl(base + UHSIC_MISC_CFG1);
+ val |= UHSIC_PLLU_STABLE_COUNT(phy->freq->stable_count);
+ writel(val, base + UHSIC_MISC_CFG1);
+
+ val = readl(base + UHSIC_PLL_CFG1);
+ val |= UHSIC_PLLU_ENABLE_DLY_COUNT(phy->freq->enable_delay);
+ val |= UHSIC_XTAL_FREQ_COUNT(phy->freq->xtal_freq_count);
+ writel(val, base + UHSIC_PLL_CFG1);
+
+ val = readl(base + USB_SUSP_CTRL);
+ val &= ~(UHSIC_RESET);
+ writel(val, base + USB_SUSP_CTRL);
+ udelay(2);
+
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+ val = readl(base + USB_PORTSC1);
+ val &= ~USB_PORTSC1_PTS(~0);
+ writel(val, base + USB_PORTSC1);
+
+ val = readl(base + USB_TXFILLTUNING);
+ if ((val & USB_FIFO_TXFILL_MASK) != USB_FIFO_TXFILL_THRES(0x10)) {
+ val = USB_FIFO_TXFILL_THRES(0x10);
+ writel(val, base + USB_TXFILLTUNING);
+ }
+#endif
+
val = readl(base + USB_PORTSC1);
val &= ~(USB_PORTSC1_WKOC | USB_PORTSC1_WKDS | USB_PORTSC1_WKCN);
writel(val, base + USB_PORTSC1);
- gpio_direction_output(config->reset_gpio, 0);
- clk_disable(phy->clk);
+ val = readl(base + UHSIC_PADS_CFG0);
+ val &= ~(UHSIC_TX_RTUNEN);
+ /* set Rtune impedance to 40 ohm */
+ val |= UHSIC_TX_RTUNE(0);
+ writel(val, base + UHSIC_PADS_CFG0);
+
+ if (utmi_wait_register(base + USB_SUSP_CTRL, USB_PHY_CLK_VALID,
+ USB_PHY_CLK_VALID)) {
+ pr_err("%s: timeout waiting for phy to stabilize\n", __func__);
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static int uhsic_phy_power_off(struct tegra_usb_phy *phy, bool is_dpd)
+{
+ unsigned long val;
+ void __iomem *base = phy->regs;
+ struct tegra_uhsic_config *uhsic_config = phy->config;
+
+ val = readl(base + UHSIC_PADS_CFG1);
+ val &= ~UHSIC_RPU_STROBE;
+ val |= UHSIC_RPD_STROBE;
+ writel(val, base + UHSIC_PADS_CFG1);
+
+ val = readl(base + USB_SUSP_CTRL);
+ val |= UHSIC_RESET;
+ writel(val, base + USB_SUSP_CTRL);
+ udelay(30);
+
+ val = readl(base + USB_SUSP_CTRL);
+ val &= ~UHSIC_PHY_ENABLE;
+ writel(val, base + USB_SUSP_CTRL);
+
+ if (uhsic_config->enable_gpio != -1) {
+ gpio_set_value_cansleep(uhsic_config->enable_gpio, 0);
+ /* keep hsic reset de-asserted for 1 ms */
+ udelay(1000);
+ }
+ if (uhsic_config->post_phy_off && uhsic_config->post_phy_off())
+ return -EAGAIN;
+
+ return 0;
+}
+
+#ifdef CONFIG_USB_TEGRA_OTG
+extern void tegra_otg_check_vbus_detection(void);
+#endif
+
+static irqreturn_t usb_phy_vbus_irq_thr(int irq, void *pdata)
+{
+ struct tegra_usb_phy *phy = pdata;
+
+ if (!phy->regulator_on) {
+ regulator_enable(phy->reg_vdd);
+ phy->regulator_on = 1;
+ /*
+ * Optimal time to get the regulator turned on
+ * before detecting vbus interrupt.
+ */
+ mdelay(15);
+ }
+
+#ifdef CONFIG_USB_TEGRA_OTG
+ tegra_otg_check_vbus_detection();
+#endif
+
+ return IRQ_HANDLED;
}
struct tegra_usb_phy *tegra_usb_phy_open(int instance, void __iomem *regs,
- void *config, enum tegra_usb_phy_mode phy_mode)
+ void *config, enum tegra_usb_phy_mode phy_mode,
+ enum tegra_usb_phy_type usb_phy_type)
{
struct tegra_usb_phy *phy;
struct tegra_ulpi_config *ulpi_config;
unsigned long parent_rate;
int i;
int err;
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+ struct tegra_ulpi_config *uhsic_config;
+ int reset_gpio, enable_gpio;
+#endif
- phy = kmalloc(sizeof(struct tegra_usb_phy), GFP_KERNEL);
+ phy = kzalloc(sizeof(struct tegra_usb_phy), GFP_KERNEL);
if (!phy)
return ERR_PTR(-ENOMEM);
@@ -669,9 +2251,17 @@ struct tegra_usb_phy *tegra_usb_phy_open(int instance, void __iomem *regs,
phy->regs = regs;
phy->config = config;
phy->mode = phy_mode;
+ phy->usb_phy_type = usb_phy_type;
+ phy->initialized = 0;
+ phy->regulator_on = 0;
+ phy->power_on = 0;
+ phy->remote_wakeup = false;
+ phy->hotplug = 0;
+ phy->xcvr_setup_value = 0;
if (!phy->config) {
- if (phy_is_ulpi(phy)) {
+ if (phy->usb_phy_type == TEGRA_USB_PHY_TYPE_LINK_ULPI ||
+ phy->usb_phy_type == TEGRA_USB_PHY_TYPE_NULL_ULPI) {
pr_err("%s: ulpi phy configuration missing", __func__);
err = -EINVAL;
goto err0;
@@ -689,10 +2279,19 @@ struct tegra_usb_phy *tegra_usb_phy_open(int instance, void __iomem *regs,
clk_enable(phy->pll_u);
parent_rate = clk_get_rate(clk_get_parent(phy->pll_u));
- for (i = 0; i < ARRAY_SIZE(tegra_freq_table); i++) {
- if (tegra_freq_table[i].freq == parent_rate) {
- phy->freq = &tegra_freq_table[i];
- break;
+ if (phy->usb_phy_type == TEGRA_USB_PHY_TYPE_HSIC) {
+ for (i = 0; i < ARRAY_SIZE(tegra_uhsic_freq_table); i++) {
+ if (tegra_uhsic_freq_table[i].freq == parent_rate) {
+ phy->freq = &tegra_uhsic_freq_table[i];
+ break;
+ }
+ }
+ } else {
+ for (i = 0; i < ARRAY_SIZE(tegra_freq_table); i++) {
+ if (tegra_freq_table[i].freq == parent_rate) {
+ phy->freq = &tegra_freq_table[i];
+ break;
+ }
}
}
if (!phy->freq) {
@@ -701,25 +2300,105 @@ struct tegra_usb_phy *tegra_usb_phy_open(int instance, void __iomem *regs,
goto err1;
}
- if (phy_is_ulpi(phy)) {
- ulpi_config = config;
- phy->clk = clk_get_sys(NULL, ulpi_config->clk);
- if (IS_ERR(phy->clk)) {
- pr_err("%s: can't get ulpi clock\n", __func__);
- err = -ENXIO;
+ if (phy->usb_phy_type == TEGRA_USB_PHY_TYPE_UTMIP) {
+ err = utmip_pad_open(phy);
+ phy->xcvr_setup_value = tegra_phy_xcvr_setup_value(phy->config);
+ if (err < 0)
goto err1;
+ } else if (phy->usb_phy_type == TEGRA_USB_PHY_TYPE_LINK_ULPI) {
+ ulpi_config = config;
+
+ if (ulpi_config->clk) {
+ phy->clk = clk_get_sys(NULL, ulpi_config->clk);
+ if (IS_ERR(phy->clk)) {
+ pr_err("%s: can't get ulpi clock\n", __func__);
+ err = -ENXIO;
+ goto err1;
+ }
+ } else {
+ /* Some USB ULPI chips are not driven by Tegra clocks or PLL */
+ phy->clk = NULL;
}
tegra_gpio_enable(ulpi_config->reset_gpio);
gpio_request(ulpi_config->reset_gpio, "ulpi_phy_reset_b");
gpio_direction_output(ulpi_config->reset_gpio, 0);
phy->ulpi = otg_ulpi_create(&ulpi_viewport_access_ops, 0);
phy->ulpi->io_priv = regs + ULPI_VIEWPORT;
- } else {
- err = utmip_pad_open(phy);
- if (err < 0)
+ }
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+ else if (phy->usb_phy_type == TEGRA_USB_PHY_TYPE_HSIC) {
+ uhsic_config = config;
+ enable_gpio = gpio_request(uhsic_config->enable_gpio,
+ "uhsic_enable");
+ reset_gpio = gpio_request(uhsic_config->reset_gpio,
+ "uhsic_reset");
+ /* hsic enable signal deasserted, hsic reset asserted */
+ if (!enable_gpio)
+ gpio_direction_output(uhsic_config->enable_gpio,
+ 0 /* deasserted */);
+ if (!reset_gpio)
+ gpio_direction_output(uhsic_config->reset_gpio,
+ 0 /* asserted */);
+ if (!enable_gpio)
+ tegra_gpio_enable(uhsic_config->enable_gpio);
+ if (!reset_gpio)
+ tegra_gpio_enable(uhsic_config->reset_gpio);
+ /* keep hsic reset asserted for 1 ms */
+ udelay(1000);
+ /* enable (power on) hsic */
+ if (!enable_gpio)
+ gpio_set_value_cansleep(uhsic_config->enable_gpio, 1);
+ udelay(1000);
+ /* deassert reset */
+ if (!reset_gpio)
+ gpio_set_value_cansleep(uhsic_config->reset_gpio, 1);
+ }
+#endif
+
+ phy->reg_vdd = regulator_get(NULL, "avdd_usb");
+ if (WARN_ON(IS_ERR_OR_NULL(phy->reg_vdd))) {
+ pr_err("couldn't get regulator avdd_usb: %ld \n",
+ PTR_ERR(phy->reg_vdd));
+ err = PTR_ERR(phy->reg_vdd);
+ goto err1;
+ }
+
+ if (instance == 0 && usb_phy_data[0].vbus_irq) {
+ err = request_threaded_irq(usb_phy_data[0].vbus_irq, NULL, usb_phy_vbus_irq_thr, IRQF_SHARED,
+ "usb_phy_vbus", phy);
+ if (err) {
+ pr_err("Failed to register IRQ\n");
goto err1;
+ }
}
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+ /* Power-up the VBUS detector for UTMIP PHY */
+ if (phy->usb_phy_type == TEGRA_USB_PHY_TYPE_UTMIP) {
+ writel(readl((IO_ADDRESS(TEGRA_PMC_BASE) + TEGRA_PMC_USB_AO)) &
+ ~(TEGRA_PMC_USB_AO_VBUS_WAKEUP_PD_P0 | TEGRA_PMC_USB_AO_ID_PD_P0),
+ (IO_ADDRESS(TEGRA_PMC_BASE) + TEGRA_PMC_USB_AO));
+
+ if (usb_phy_data[phy->instance].vbus_reg_supply) {
+ phy->reg_vbus = regulator_get(NULL, usb_phy_data[phy->instance].vbus_reg_supply);
+ if (WARN_ON(IS_ERR_OR_NULL(phy->reg_vbus))) {
+ pr_err("couldn't get regulator vdd_vbus_usb: %ld, instance : %d\n",
+ PTR_ERR(phy->reg_vbus), phy->instance);
+ err = PTR_ERR(phy->reg_vbus);
+ goto err1;
+ }
+ }
+ }
+ if (instance == 2) {
+ writel(readl((IO_ADDRESS(TEGRA_PMC_BASE) + TEGRA_PMC_USB_AO)) &
+ (TEGRA_PMC_USB_AO_PD_P2),
+ (IO_ADDRESS(TEGRA_PMC_BASE) + TEGRA_PMC_USB_AO));
+ }
+#endif
+ if (((instance == 2) || (instance == 0)) &&
+ (phy->mode == TEGRA_USB_PHY_MODE_HOST)) {
+ vbus_enable(phy);
+ }
return phy;
err1:
@@ -730,66 +2409,458 @@ err0:
return ERR_PTR(err);
}
-int tegra_usb_phy_power_on(struct tegra_usb_phy *phy)
+int tegra_usb_phy_power_on(struct tegra_usb_phy *phy, bool is_dpd)
{
- if (phy_is_ulpi(phy))
- return ulpi_phy_power_on(phy);
- else
- return utmi_phy_power_on(phy);
+ int ret = 0;
+
+ const tegra_phy_fp power_on[] = {
+ utmi_phy_power_on,
+ ulpi_phy_power_on,
+ null_phy_power_on,
+ uhsic_phy_power_on,
+ };
+
+ if (phy->power_on)
+ return ret;
+
+ if (phy->reg_vdd && !phy->regulator_on) {
+ regulator_enable(phy->reg_vdd);
+ phy->regulator_on = 1;
+ }
+
+ if (power_on[phy->usb_phy_type])
+ ret = power_on[phy->usb_phy_type](phy, is_dpd);
+
+ phy->power_on = true;
+ return ret;
}
-void tegra_usb_phy_power_off(struct tegra_usb_phy *phy)
+void tegra_usb_phy_power_off(struct tegra_usb_phy *phy, bool is_dpd)
{
- if (phy_is_ulpi(phy))
- ulpi_phy_power_off(phy);
- else
- utmi_phy_power_off(phy);
+ const tegra_phy_fp power_off[] = {
+ utmi_phy_power_off,
+ ulpi_phy_power_off,
+ null_phy_power_off,
+ uhsic_phy_power_off,
+ };
+
+ if (!phy->power_on)
+ return;
+
+ if (power_off[phy->usb_phy_type])
+ power_off[phy->usb_phy_type](phy, is_dpd);
+
+ if (phy->reg_vdd && phy->regulator_on && is_dpd) {
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+ if (tegra_get_revision() >= TEGRA_REVISION_A03)
+#endif
+ regulator_disable(phy->reg_vdd);
+ phy->regulator_on = 0;
+ }
+ phy->power_on = false;
+}
+
+void tegra_usb_phy_preresume(struct tegra_usb_phy *phy, bool is_dpd)
+{
+ const tegra_phy_fp preresume[] = {
+ utmi_phy_preresume,
+ NULL,
+ NULL,
+ uhsic_phy_preresume,
+ };
+
+ if (preresume[phy->usb_phy_type])
+ preresume[phy->usb_phy_type](phy, is_dpd);
+}
+
+void tegra_usb_phy_postsuspend(struct tegra_usb_phy *phy, bool is_dpd)
+
+{
+ const tegra_phy_fp postsuspend[] = {
+ NULL,
+ NULL,
+ NULL,
+ uhsic_phy_postsuspend,
+ };
+
+ if (postsuspend[phy->usb_phy_type])
+ postsuspend[phy->usb_phy_type](phy, is_dpd);
}
-void tegra_usb_phy_preresume(struct tegra_usb_phy *phy)
+void tegra_usb_phy_postresume(struct tegra_usb_phy *phy, bool is_dpd)
{
- if (!phy_is_ulpi(phy))
- utmi_phy_preresume(phy);
+ const tegra_phy_fp postresume[] = {
+ utmi_phy_postresume,
+ NULL,
+ NULL,
+ uhsic_phy_postresume,
+ };
+
+ if (postresume[phy->usb_phy_type])
+ postresume[phy->usb_phy_type](phy, is_dpd);
}
-void tegra_usb_phy_postresume(struct tegra_usb_phy *phy)
+void tegra_ehci_pre_reset(struct tegra_usb_phy *phy, bool is_dpd)
{
- if (!phy_is_ulpi(phy))
- utmi_phy_postresume(phy);
+ const tegra_phy_fp pre_reset[] = {
+ NULL,
+ NULL,
+ null_phy_pre_usbcmd_reset,
+ NULL,
+ };
+
+ if (pre_reset[phy->usb_phy_type])
+ pre_reset[phy->usb_phy_type](phy, is_dpd);
+}
+
+void tegra_ehci_post_reset(struct tegra_usb_phy *phy, bool is_dpd)
+{
+ const tegra_phy_fp post_reset[] = {
+ NULL,
+ NULL,
+ null_phy_post_usbcmd_reset,
+ NULL,
+ };
+
+ if (post_reset[phy->usb_phy_type])
+ post_reset[phy->usb_phy_type](phy, is_dpd);
}
void tegra_ehci_phy_restore_start(struct tegra_usb_phy *phy,
enum tegra_usb_phy_port_speed port_speed)
{
- if (!phy_is_ulpi(phy))
- utmi_phy_restore_start(phy, port_speed);
+ const tegra_phy_restore_start_fp phy_restore_start[] = {
+ utmi_phy_restore_start,
+ ulpi_phy_restore_start,
+ null_phy_restore_start,
+ NULL,
+ };
+
+ if (phy_restore_start[phy->usb_phy_type])
+ phy_restore_start[phy->usb_phy_type](phy, port_speed);
}
void tegra_ehci_phy_restore_end(struct tegra_usb_phy *phy)
{
- if (!phy_is_ulpi(phy))
- utmi_phy_restore_end(phy);
+ const tegra_phy_restore_end_fp phy_restore_end[] = {
+ utmi_phy_restore_end,
+ ulpi_phy_restore_end,
+ null_phy_restore_end,
+ NULL,
+ };
+
+ if (phy_restore_end[phy->usb_phy_type])
+ phy_restore_end[phy->usb_phy_type](phy);
}
void tegra_usb_phy_clk_disable(struct tegra_usb_phy *phy)
{
- if (!phy_is_ulpi(phy))
+ if (phy->usb_phy_type == TEGRA_USB_PHY_TYPE_UTMIP)
utmi_phy_clk_disable(phy);
}
void tegra_usb_phy_clk_enable(struct tegra_usb_phy *phy)
{
- if (!phy_is_ulpi(phy))
+ if (phy->usb_phy_type == TEGRA_USB_PHY_TYPE_UTMIP)
utmi_phy_clk_enable(phy);
}
void tegra_usb_phy_close(struct tegra_usb_phy *phy)
{
- if (phy_is_ulpi(phy))
- clk_put(phy->clk);
- else
+ if (phy->usb_phy_type == TEGRA_USB_PHY_TYPE_UTMIP) {
utmip_pad_close(phy);
+ utmip_phy_disable_pmc_bus_ctrl(phy);
+ }
+ else if (phy->usb_phy_type == TEGRA_USB_PHY_TYPE_LINK_ULPI && phy->clk)
+ clk_put(phy->clk);
+ if (phy->mode == TEGRA_USB_PHY_MODE_HOST) {
+ vbus_disable(phy);
+ }
clk_disable(phy->pll_u);
clk_put(phy->pll_u);
+ if (phy->reg_vbus)
+ regulator_put(phy->reg_vbus);
+ if (phy->reg_vdd)
+ regulator_put(phy->reg_vdd);
+ if (phy->instance == 0 && usb_phy_data[0].vbus_irq)
+ free_irq(usb_phy_data[0].vbus_irq, phy);
kfree(phy);
}
+
+int tegra_usb_phy_bus_connect(struct tegra_usb_phy *phy)
+{
+ unsigned long val;
+ void __iomem *base = phy->regs;
+ struct tegra_uhsic_config *uhsic_config = phy->config;
+
+ if (phy->usb_phy_type == TEGRA_USB_PHY_TYPE_HSIC) {
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+ /* Change the USB controller PHY type to HSIC */
+ val = readl(base + HOSTPC1_DEVLC);
+ val &= ~HOSTPC1_DEVLC_PTS(HOSTPC1_DEVLC_PTS_MASK);
+ val |= HOSTPC1_DEVLC_PTS(HOSTPC1_DEVLC_PTS_HSIC);
+ val &= ~HOSTPC1_DEVLC_PSPD(HOSTPC1_DEVLC_PSPD_MASK);
+ val |= HOSTPC1_DEVLC_PSPD(HOSTPC1_DEVLC_PSPD_HIGH_SPEED);
+ writel(val, base + HOSTPC1_DEVLC);
+#endif
+ val = readl(base + UHSIC_MISC_CFG0);
+ val |= UHSIC_DETECT_SHORT_CONNECT;
+ writel(val, base + UHSIC_MISC_CFG0);
+ udelay(1);
+
+ val = readl(base + UHSIC_MISC_CFG0);
+ val |= UHSIC_FORCE_XCVR_MODE;
+ writel(val, base + UHSIC_MISC_CFG0);
+
+ val = readl(base + UHSIC_PADS_CFG1);
+ val &= ~UHSIC_RPD_STROBE;
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+ val |= UHSIC_RPU_STROBE;
+#endif
+ writel(val, base + UHSIC_PADS_CFG1);
+
+ if (uhsic_config->usb_phy_ready &&
+ uhsic_config->usb_phy_ready())
+ return -EAGAIN;
+
+ if (utmi_wait_register(base + UHSIC_STAT_CFG0, UHSIC_CONNECT_DETECT, UHSIC_CONNECT_DETECT) < 0) {
+ pr_err("%s: timeout waiting for hsic connect detect\n", __func__);
+ return -ETIMEDOUT;
+ }
+
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+ if (utmi_wait_register(base + USB_PORTSC1, USB_PORTSC1_LS(2), USB_PORTSC1_LS(2)) < 0) {
+ pr_err("%s: timeout waiting for dplus state\n", __func__);
+ return -ETIMEDOUT;
+ }
+#endif
+ }
+
+ return 0;
+}
+
+int tegra_usb_phy_bus_reset(struct tegra_usb_phy *phy)
+{
+ unsigned long val;
+ void __iomem *base = phy->regs;
+
+ if (phy->usb_phy_type == TEGRA_USB_PHY_TYPE_HSIC) {
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+ val = readl(base + USB_PORTSC1);
+ val |= USB_PORTSC1_PTC(5);
+ writel(val, base + USB_PORTSC1);
+ udelay(2);
+
+ val = readl(base + USB_PORTSC1);
+ val &= ~USB_PORTSC1_PTC(~0);
+ writel(val, base + USB_PORTSC1);
+ udelay(2);
+#endif
+
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+ if (utmi_wait_register(base + USB_PORTSC1, USB_PORTSC1_LS(0), 0) < 0) {
+ pr_err("%s: timeout waiting for SE0\n", __func__);
+ return -ETIMEDOUT;
+ }
+#endif
+ if (utmi_wait_register(base + USB_PORTSC1, USB_PORTSC1_CCS, USB_PORTSC1_CCS) < 0) {
+ pr_err("%s: timeout waiting for connection status\n", __func__);
+ return -ETIMEDOUT;
+ }
+
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+ if (utmi_wait_register(base + USB_PORTSC1, USB_PORTSC1_PSPD(2), USB_PORTSC1_PSPD(2)) < 0) {
+ pr_err("%s: timeout waiting hsic high speed configuration\n", __func__);
+ return -ETIMEDOUT;
+ }
+#endif
+ val = readl(base + USB_USBCMD);
+ val &= ~USB_USBCMD_RS;
+ writel(val, base + USB_USBCMD);
+
+ if (utmi_wait_register(base + USB_USBSTS, USB_USBSTS_HCH, USB_USBSTS_HCH) < 0) {
+ pr_err("%s: timeout waiting for stopping the controller\n", __func__);
+ return -ETIMEDOUT;
+ }
+
+ val = readl(base + UHSIC_PADS_CFG1);
+ val &= ~UHSIC_RPU_STROBE;
+ val |= UHSIC_RPD_STROBE;
+ writel(val, base + UHSIC_PADS_CFG1);
+
+ mdelay(50);
+
+ val = readl(base + UHSIC_PADS_CFG1);
+ val &= ~UHSIC_RPD_STROBE;
+ val |= UHSIC_RPU_STROBE;
+ writel(val, base + UHSIC_PADS_CFG1);
+
+ val = readl(base + USB_USBCMD);
+ val |= USB_USBCMD_RS;
+ writel(val, base + USB_USBCMD);
+
+ val = readl(base + UHSIC_PADS_CFG1);
+ val &= ~UHSIC_RPU_STROBE;
+ writel(val, base + UHSIC_PADS_CFG1);
+
+ if (utmi_wait_register(base + USB_USBCMD, USB_USBCMD_RS, USB_USBCMD_RS) < 0) {
+ pr_err("%s: timeout waiting for starting the controller\n", __func__);
+ return -ETIMEDOUT;
+ }
+ }
+
+ return 0;
+}
+
+int tegra_usb_phy_bus_idle(struct tegra_usb_phy *phy)
+{
+ unsigned long val;
+ void __iomem *base = phy->regs;
+ struct tegra_uhsic_config *uhsic_config = phy->config;
+
+ if (phy->usb_phy_type == TEGRA_USB_PHY_TYPE_HSIC) {
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+ /* Change the USB controller PHY type to HSIC */
+ val = readl(base + HOSTPC1_DEVLC);
+ val &= ~HOSTPC1_DEVLC_PTS(HOSTPC1_DEVLC_PTS_MASK);
+ val |= HOSTPC1_DEVLC_PTS(HOSTPC1_DEVLC_PTS_HSIC);
+ val &= ~HOSTPC1_DEVLC_PSPD(HOSTPC1_DEVLC_PSPD_MASK);
+ val |= HOSTPC1_DEVLC_PSPD(HOSTPC1_DEVLC_PSPD_HIGH_SPEED);
+ writel(val, base + HOSTPC1_DEVLC);
+#endif
+ val = readl(base + UHSIC_MISC_CFG0);
+ val |= UHSIC_DETECT_SHORT_CONNECT;
+ writel(val, base + UHSIC_MISC_CFG0);
+ udelay(1);
+
+ val = readl(base + UHSIC_MISC_CFG0);
+ val |= UHSIC_FORCE_XCVR_MODE;
+ writel(val, base + UHSIC_MISC_CFG0);
+
+ val = readl(base + UHSIC_PADS_CFG1);
+ val &= ~UHSIC_RPD_STROBE;
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+ val |= UHSIC_RPU_STROBE;
+#endif
+ writel(val, base + UHSIC_PADS_CFG1);
+
+ if (uhsic_config->usb_phy_ready &&
+ uhsic_config->usb_phy_ready())
+ return -EAGAIN;
+ }
+ return 0;
+}
+
+bool tegra_usb_phy_is_device_connected(struct tegra_usb_phy *phy)
+{
+ void __iomem *base = phy->regs;
+
+ if (phy->usb_phy_type == TEGRA_USB_PHY_TYPE_HSIC) {
+ if (!((readl(base + UHSIC_STAT_CFG0) & UHSIC_CONNECT_DETECT) == UHSIC_CONNECT_DETECT)) {
+ pr_err("%s: hsic no device connection\n", __func__);
+ return false;
+ }
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+ if (utmi_wait_register(base + USB_PORTSC1, USB_PORTSC1_LS(2), USB_PORTSC1_LS(2)) < 0) {
+ pr_err("%s: timeout waiting for dplus state\n", __func__);
+ return false;
+ }
+#endif
+ }
+ return true;
+}
+
+bool tegra_usb_phy_charger_detect(struct tegra_usb_phy *phy)
+{
+ unsigned long val;
+ void __iomem *base = phy->regs;
+ bool status;
+
+ if (phy->usb_phy_type != TEGRA_USB_PHY_TYPE_UTMIP)
+ {
+ /* Charger detection is not there for ULPI
+ * return Charger not available */
+ return false;
+ }
+
+ /* Enable charger detection logic */
+ val = readl(base + UTMIP_BAT_CHRG_CFG0);
+ val |= UTMIP_OP_SRC_EN | UTMIP_ON_SINK_EN;
+ writel(val, base + UTMIP_BAT_CHRG_CFG0);
+
+ /* Source should be on for 100 ms as per USB charging spec */
+ msleep(TDP_SRC_ON_MS);
+
+ val = readl(base + USB_PHY_VBUS_WAKEUP_ID);
+ /* If charger is not connected disable the interrupt */
+ val &= ~VDAT_DET_INT_EN;
+ val |= VDAT_DET_CHG_DET;
+ writel(val,base + USB_PHY_VBUS_WAKEUP_ID);
+
+ val = readl(base + USB_PHY_VBUS_WAKEUP_ID);
+ if (val & VDAT_DET_STS)
+ status = true;
+ else
+ status = false;
+
+ /* Disable charger detection logic */
+ val = readl(base + UTMIP_BAT_CHRG_CFG0);
+ val &= ~(UTMIP_OP_SRC_EN | UTMIP_ON_SINK_EN);
+ writel(val, base + UTMIP_BAT_CHRG_CFG0);
+
+ /* Delay of 40 ms before we pull the D+ as per battery charger spec */
+ msleep(TDPSRC_CON_MS);
+
+ return status;
+}
+
+int __init tegra_usb_phy_init(struct usb_phy_plat_data *pdata, int size)
+{
+ if (pdata) {
+ int i;
+
+ for (i = 0; i < size; i++, pdata++) {
+ usb_phy_data[pdata->instance].instance = pdata->instance;
+ usb_phy_data[pdata->instance].vbus_irq = pdata->vbus_irq;
+ usb_phy_data[pdata->instance].vbus_gpio = pdata->vbus_gpio;
+ usb_phy_data[pdata->instance].vbus_reg_supply = pdata->vbus_reg_supply;
+ }
+ }
+
+ return 0;
+}
+
+/* disable walk and wake events after resume from LP0 */
+bool tegra_usb_phy_is_remotewake_detected(struct tegra_usb_phy *phy)
+{
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+ void __iomem *pmc_base = IO_ADDRESS(TEGRA_PMC_BASE);
+ void __iomem *base = phy->regs;
+ unsigned int inst = phy->instance;
+ u32 val;
+
+ val = readl(base + UTMIP_PMC_WAKEUP0);
+ if (val & EVENT_INT_ENB) {
+ val = readl(pmc_base + UTMIP_UHSIC_STATUS);
+ if (UTMIP_WAKE_ALARM(inst) & val) {
+ val = readl(pmc_base + PMC_SLEEP_CFG);
+ val &= ~UTMIP_WAKE_VAL(inst, 0x0);
+ val |= UTMIP_WAKE_VAL(inst, WAKE_VAL_NONE);
+ writel(val, pmc_base + PMC_SLEEP_CFG);
+
+ val = readl(pmc_base + PMC_TRIGGERS);
+ val |= UTMIP_CLR_WAKE_ALARM(inst) |
+ UTMIP_CLR_WALK_PTR(inst);
+ writel(val, pmc_base + PMC_TRIGGERS);
+
+ val = readl(base + UTMIP_PMC_WAKEUP0);
+ val &= ~EVENT_INT_ENB;
+ writel(val, base + UTMIP_PMC_WAKEUP0);
+ phy->remote_wakeup = true;
+ return true;
+ }
+ }
+#endif
+ return false;
+}
+
diff --git a/arch/arm/mach-tegra/wakeups-t2.c b/arch/arm/mach-tegra/wakeups-t2.c
new file mode 100644
index 000000000000..7c5d12ac60d4
--- /dev/null
+++ b/arch/arm/mach-tegra/wakeups-t2.c
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2011, Google, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/io.h>
+
+#include <mach/iomap.h>
+#include <mach/irqs.h>
+#include <mach/gpio.h>
+
+#include "gpio-names.h"
+
+static int tegra_wake_event_irq[] = {
+ [0] = TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PO5),
+ [1] = TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PV3),
+ [2] = TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PL1),
+ [3] = TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PB6),
+ [4] = TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PN7),
+ [5] = TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PA0),
+ [6] = TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PU5),
+ [7] = TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PU6),
+ [8] = TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PC7),
+ [9] = TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PS2),
+ [10] = TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PAA1),
+ [11] = TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PW3),
+ [12] = TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PW2),
+ [13] = TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PY6),
+ [14] = TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PV6),
+ [15] = TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PJ7),
+ [16] = INT_RTC,
+ [17] = INT_KBC,
+ [18] = INT_EXTERNAL_PMU,
+ [19] = -EINVAL, /* TEGRA_USB1_VBUS, */
+ [20] = -EINVAL, /* TEGRA_USB3_VBUS, */
+ [21] = -EINVAL, /* TEGRA_USB1_ID, */
+ [22] = -EINVAL, /* TEGRA_USB3_ID, */
+ [23] = TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PI5),
+ [24] = TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PV2),
+ [25] = TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PS4),
+ [26] = TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PS5),
+ [27] = TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PS0),
+ [28] = TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PQ6),
+ [29] = TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PQ7),
+ [30] = TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PN2),
+};
+
+int tegra_irq_to_wake(int irq)
+{
+ int i;
+ int wake_irq;
+ int search_gpio;
+ static int last_wake = -1;
+
+ /* Two level wake irq search for gpio based wakeups -
+ * 1. check for GPIO irq(based on tegra_wake_event_irq table)
+ * e.g. for a board, wake7 based on GPIO PU6 and irq==358 done first
+ * 2. check for gpio bank irq assuming search for GPIO irq
+ * preceded this search.
+ * e.g. in this step check for gpio bank irq GPIO6 irq==119
+ */
+ for (i = 0; i < ARRAY_SIZE(tegra_wake_event_irq); i++) {
+ /* return if step 1 matches */
+ if (tegra_wake_event_irq[i] == irq) {
+ pr_info("Wake%d for irq=%d\n", i, irq);
+ last_wake = i;
+ return i;
+ }
+
+ /* step 2 below uses saved last_wake from step 1
+ * in previous call */
+ search_gpio = irq_to_gpio(
+ tegra_wake_event_irq[i]);
+ if (search_gpio < 0)
+ continue;
+ wake_irq = tegra_gpio_get_bank_int_nr(search_gpio);
+ if (wake_irq < 0)
+ continue;
+ if ((last_wake == i) &&
+ (wake_irq == irq)) {
+ pr_info("gpio bank wake found: wake%d for irq=%d\n",
+ i, irq);
+ return i;
+ }
+ }
+
+ return -EINVAL;
+}
+
+int tegra_wake_to_irq(int wake)
+{
+ if (wake < 0)
+ return -EINVAL;
+
+ if (wake >= ARRAY_SIZE(tegra_wake_event_irq))
+ return -EINVAL;
+
+ return tegra_wake_event_irq[wake];
+}
diff --git a/arch/arm/mach-tegra/wakeups-t2.h b/arch/arm/mach-tegra/wakeups-t2.h
new file mode 100644
index 000000000000..eb193c0aaf9e
--- /dev/null
+++ b/arch/arm/mach-tegra/wakeups-t2.h
@@ -0,0 +1,65 @@
+/*
+ * arch/arm/mach-tegra/wakeups-t2.h
+ *
+ * Declarations of Tegra 2 LP0 wakeup sources
+ *
+ * Copyright (c) 2010, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __MACH_TEGRA_WAKEUPS_T2_H
+#define __MACH_TEGRA_WAKEUPS_T2_H
+
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+#error "Tegra 2 wakeup sources valid only for CONFIG_ARCH_TEGRA_2x_SOC"
+#endif
+
+int tegra_irq_to_wake(int irq);
+int tegra_wake_to_irq(int wake);
+
+#define TEGRA_WAKE_GPIO_PO5 (1 << 0)
+#define TEGRA_WAKE_GPIO_PV3 (1 << 1)
+#define TEGRA_WAKE_GPIO_PL1 (1 << 2)
+#define TEGRA_WAKE_GPIO_PB6 (1 << 3)
+#define TEGRA_WAKE_GPIO_PN7 (1 << 4)
+#define TEGRA_WAKE_GPIO_PA0 (1 << 5)
+#define TEGRA_WAKE_GPIO_PU5 (1 << 6)
+#define TEGRA_WAKE_GPIO_PU6 (1 << 7)
+#define TEGRA_WAKE_GPIO_PC7 (1 << 8)
+#define TEGRA_WAKE_GPIO_PS2 (1 << 9)
+#define TEGRA_WAKE_GPIO_PAA1 (1 << 10)
+#define TEGRA_WAKE_GPIO_PW3 (1 << 11)
+#define TEGRA_WAKE_GPIO_PW2 (1 << 12)
+#define TEGRA_WAKE_GPIO_PY6 (1 << 13)
+#define TEGRA_WAKE_GPIO_PV6 (1 << 14)
+#define TEGRA_WAKE_GPIO_PJ7 (1 << 15)
+#define TEGRA_WAKE_RTC_ALARM (1 << 16)
+#define TEGRA_WAKE_KBC_EVENT (1 << 17)
+#define TEGRA_WAKE_PWR_INT (1 << 18)
+#define TEGRA_WAKE_USB1_VBUS (1 << 19)
+#define TEGRA_WAKE_USB3_VBUS (1 << 20)
+#define TEGRA_WAKE_USB1_ID (1 << 21)
+#define TEGRA_WAKE_USB3_ID (1 << 22)
+#define TEGRA_WAKE_GPIO_PI5 (1 << 23)
+#define TEGRA_WAKE_GPIO_PV2 (1 << 24)
+#define TEGRA_WAKE_GPIO_PS4 (1 << 25)
+#define TEGRA_WAKE_GPIO_PS5 (1 << 26)
+#define TEGRA_WAKE_GPIO_PS0 (1 << 27)
+#define TEGRA_WAKE_GPIO_PQ6 (1 << 28)
+#define TEGRA_WAKE_GPIO_PQ7 (1 << 29)
+#define TEGRA_WAKE_GPIO_PN2 (1 << 30)
+
+#endif
diff --git a/arch/arm/mach-tegra/wakeups-t3.c b/arch/arm/mach-tegra/wakeups-t3.c
new file mode 100644
index 000000000000..823736204362
--- /dev/null
+++ b/arch/arm/mach-tegra/wakeups-t3.c
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2011, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/io.h>
+
+#include <mach/iomap.h>
+#include <mach/irqs.h>
+#include <mach/gpio.h>
+
+#include "gpio-names.h"
+
+static int tegra_wake_event_irq[] = {
+ TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PO5), /* wake0 */
+ TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PV1), /* wake1 */
+ TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PL1), /* wake2 */
+ TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PB6), /* wake3 */
+ TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PN7), /* wake4 */
+ TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PBB6), /* wake5 */
+ TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PU5), /* wake6 */
+ TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PU6), /* wake7 */
+ TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PC7), /* wake8 */
+ TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PS2), /* wake9 */
+ TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PAA1), /* wake10 */
+ TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PW3), /* wake11 */
+ TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PW2), /* wake12 */
+ TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PY6), /* wake13 */
+ TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PDD3), /* wake14 */
+ TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PJ2), /* wake15 */
+ INT_RTC, /* wake16 */
+ INT_KBC, /* wake17 */
+ INT_EXTERNAL_PMU, /* wake18 */
+ -EINVAL, /* TEGRA_USB1_VBUS, */ /* wake19 */
+ -EINVAL, /* TEGRA_USB2_VBUS, */ /* wake20 */
+ -EINVAL, /* TEGRA_USB1_ID, */ /* wake21 */
+ -EINVAL, /* TEGRA_USB2_ID, */ /* wake22 */
+ TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PI5), /* wake23 */
+ TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PV0), /* wake24 */
+ TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PS4), /* wake25 */
+ TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PS5), /* wake26 */
+ TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PS0), /* wake27 */
+ TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PS6), /* wake28 */
+ TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PS7), /* wake29 */
+ TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PN2), /* wake30 */
+ -EINVAL, /* not used */ /* wake31 */
+ TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PO4), /* wake32 */
+ TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PJ0), /* wake33 */
+ TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PK2), /* wake34 */
+ TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PI6), /* wake35 */
+ TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PBB1), /* wake36 */
+ -EINVAL, /* TEGRA_USB3_VBUS, */ /* wake37 */
+ -EINVAL, /* TEGRA_USB3_ID, */ /* wake38 */
+ INT_USB, /* TEGRA_USB1_UTMIP, */ /* wake39 */
+ INT_USB2, /* TEGRA_USB2_UTMIP, */ /* wake40 */
+ INT_USB3, /* TEGRA_USB3_UTMIP, */ /* wake41 */
+};
+
+int tegra_irq_to_wake(int irq)
+{
+ int i;
+ int wake_irq;
+ int search_gpio;
+ static int last_wake = -1;
+
+ /* Two level wake irq search for gpio based wakeups -
+ * 1. check for GPIO irq(based on tegra_wake_event_irq table)
+ * e.g. for a board, wake7 based on GPIO PU6 and irq==390 done first
+ * 2. check for gpio bank irq assuming search for GPIO irq
+ * preceded this search.
+ * e.g. in this step check for gpio bank irq GPIO6 irq==119
+ */
+ for (i = 0; i < ARRAY_SIZE(tegra_wake_event_irq); i++) {
+ /* return if step 1 matches */
+ if (tegra_wake_event_irq[i] == irq) {
+ pr_info("Wake%d for irq=%d\n", i, irq);
+ last_wake = i;
+ return i;
+ }
+
+ /* step 2 below uses saved last_wake from step 1
+ * in previous call */
+ search_gpio = irq_to_gpio(
+ tegra_wake_event_irq[i]);
+ if (search_gpio < 0)
+ continue;
+ wake_irq = tegra_gpio_get_bank_int_nr(search_gpio);
+ if (wake_irq < 0)
+ continue;
+ if ((last_wake == i) &&
+ (wake_irq == irq)) {
+ pr_info("gpio bank wake found: wake%d for irq=%d\n",
+ i, irq);
+ return i;
+ }
+ }
+
+ return -EINVAL;
+}
+
+int tegra_wake_to_irq(int wake)
+{
+ if (wake < 0)
+ return -EINVAL;
+
+ if (wake >= ARRAY_SIZE(tegra_wake_event_irq))
+ return -EINVAL;
+
+ return tegra_wake_event_irq[wake];
+}
diff --git a/arch/arm/mach-tegra/wakeups-t3.h b/arch/arm/mach-tegra/wakeups-t3.h
new file mode 100644
index 000000000000..f811d8939387
--- /dev/null
+++ b/arch/arm/mach-tegra/wakeups-t3.h
@@ -0,0 +1,71 @@
+/*
+ * arch/arm/mach-tegra/wakeups-t3.h
+ *
+ * Declarations of Tegra 3 LP0 wakeup sources
+ *
+ * Copyright (c) 2010, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __MACH_TEGRA_WAKEUPS_T3_H
+#define __MACH_TEGRA_WAKEUPS_T3_H
+
+#ifndef CONFIG_ARCH_TEGRA_3x_SOC
+#error "Tegra 3 wakeup sources valid only for CONFIG_ARCH_TEGRA_3x_SOC"
+#endif
+
+#define TEGRA_WAKE_GPIO_PO5 (1ull << 0)
+#define TEGRA_WAKE_GPIO_PV1 (1ull << 1)
+#define TEGRA_WAKE_GPIO_PL1 (1ull << 2)
+#define TEGRA_WAKE_GPIO_PB6 (1ull << 3)
+#define TEGRA_WAKE_GPIO_PN7 (1ull << 4)
+#define TEGRA_WAKE_GPIO_PBB6 (1ull << 5)
+#define TEGRA_WAKE_GPIO_PU5 (1ull << 6)
+#define TEGRA_WAKE_GPIO_PU6 (1ull << 7)
+#define TEGRA_WAKE_GPIO_PC7 (1ull << 8)
+#define TEGRA_WAKE_GPIO_PS2 (1ull << 9)
+#define TEGRA_WAKE_GPIO_PAA1 (1ull << 10)
+#define TEGRA_WAKE_GPIO_PW3 (1ull << 11)
+#define TEGRA_WAKE_GPIO_PW2 (1ull << 12)
+#define TEGRA_WAKE_GPIO_PY6 (1ull << 13)
+#define TEGRA_WAKE_GPIO_PDD3 (1ull << 14)
+#define TEGRA_WAKE_GPIO_PJ2 (1ull << 15)
+#define TEGRA_WAKE_RTC_ALARM (1ull << 16)
+#define TEGRA_WAKE_KBC_EVENT (1ull << 17)
+#define TEGRA_WAKE_PWR_INT (1ull << 18)
+#define TEGRA_WAKE_USB1_VBUS (1ull << 19)
+#define TEGRA_WAKE_USB2_VBUS (1ull << 20)
+#define TEGRA_WAKE_USB1_ID (1ull << 21)
+#define TEGRA_WAKE_USB2_ID (1ull << 22)
+#define TEGRA_WAKE_GPIO_PI5 (1ull << 23)
+#define TEGRA_WAKE_GPIO_PV0 (1ull << 24)
+#define TEGRA_WAKE_GPIO_PS4 (1ull << 25)
+#define TEGRA_WAKE_GPIO_PS5 (1ull << 26)
+#define TEGRA_WAKE_GPIO_PS0 (1ull << 27)
+#define TEGRA_WAKE_GPIO_PS6 (1ull << 28)
+#define TEGRA_WAKE_GPIO_PS7 (1ull << 29)
+#define TEGRA_WAKE_GPIO_PN2 (1ull << 30)
+/* bit 31 is unused */
+
+#define TEGRA_WAKE_GPIO_PO4 (1ull << 32)
+#define TEGRA_WAKE_GPIO_PJ0 (1ull << 33)
+#define TEGRA_WAKE_GPIO_PK2 (1ull << 34)
+#define TEGRA_WAKE_GPIO_PI6 (1ull << 35)
+#define TEGRA_WAKE_GPIO_PBB1 (1ull << 36)
+#define TEGRA_WAKE_USB3_ID (1ull << 37)
+#define TEGRA_WAKE_USB3_VBUS (1ull << 38)
+
+#endif
diff --git a/arch/arm/mm/Makefile b/arch/arm/mm/Makefile
index bca7e61928c7..47e2e3ba1902 100644
--- a/arch/arm/mm/Makefile
+++ b/arch/arm/mm/Makefile
@@ -6,7 +6,7 @@ obj-y := dma-mapping.o extable.o fault.o init.o \
iomap.o
obj-$(CONFIG_MMU) += fault-armv.o flush.o idmap.o ioremap.o \
- mmap.o pgd.o mmu.o vmregion.o
+ mmap.o pgd.o mmu.o vmregion.o pageattr.o
ifneq ($(CONFIG_MMU),y)
obj-y += nommu.o
diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c
index 9ecfdb511951..0dddb54ea986 100644
--- a/arch/arm/mm/cache-l2x0.c
+++ b/arch/arm/mm/cache-l2x0.c
@@ -29,6 +29,16 @@ static void __iomem *l2x0_base;
static DEFINE_SPINLOCK(l2x0_lock);
static uint32_t l2x0_way_mask; /* Bitmask of active ways */
static uint32_t l2x0_size;
+static u32 l2x0_cache_id;
+static unsigned int l2x0_sets;
+static unsigned int l2x0_ways;
+
+static inline bool is_pl310_rev(int rev)
+{
+ return (l2x0_cache_id &
+ (L2X0_CACHE_ID_PART_MASK | L2X0_CACHE_ID_REV_MASK)) ==
+ (L2X0_CACHE_ID_PART_L310 | rev);
+}
static inline void cache_wait_way(void __iomem *reg, unsigned long mask)
{
@@ -120,6 +130,23 @@ static void l2x0_cache_sync(void)
spin_unlock_irqrestore(&l2x0_lock, flags);
}
+#ifdef CONFIG_PL310_ERRATA_727915
+static void l2x0_for_each_set_way(void __iomem *reg)
+{
+ int set;
+ int way;
+ unsigned long flags;
+
+ for (way = 0; way < l2x0_ways; way++) {
+ spin_lock_irqsave(&l2x0_lock, flags);
+ for (set = 0; set < l2x0_sets; set++)
+ writel_relaxed((way << 28) | (set << 5), reg);
+ cache_sync();
+ spin_unlock_irqrestore(&l2x0_lock, flags);
+ }
+}
+#endif
+
static void __l2x0_flush_all(void)
{
debug_writel(0x03);
@@ -133,6 +160,13 @@ static void l2x0_flush_all(void)
{
unsigned long flags;
+#ifdef CONFIG_PL310_ERRATA_727915
+ if (is_pl310_rev(REV_PL310_R2P0)) {
+ l2x0_for_each_set_way(l2x0_base + L2X0_CLEAN_INV_LINE_IDX);
+ return;
+ }
+#endif
+
/* clean all ways */
spin_lock_irqsave(&l2x0_lock, flags);
__l2x0_flush_all();
@@ -143,11 +177,20 @@ static void l2x0_clean_all(void)
{
unsigned long flags;
+#ifdef CONFIG_PL310_ERRATA_727915
+ if (is_pl310_rev(REV_PL310_R2P0)) {
+ l2x0_for_each_set_way(l2x0_base + L2X0_CLEAN_LINE_IDX);
+ return;
+ }
+#endif
+
/* clean all ways */
spin_lock_irqsave(&l2x0_lock, flags);
+ debug_writel(0x03);
writel_relaxed(l2x0_way_mask, l2x0_base + L2X0_CLEAN_WAY);
cache_wait_way(l2x0_base + L2X0_CLEAN_WAY, l2x0_way_mask);
cache_sync();
+ debug_writel(0x00);
spin_unlock_irqrestore(&l2x0_lock, flags);
}
@@ -266,6 +309,16 @@ static void l2x0_flush_range(unsigned long start, unsigned long end)
spin_unlock_irqrestore(&l2x0_lock, flags);
}
+/* enables l2x0 after l2x0_disable, does not invalidate */
+void l2x0_enable(void)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&l2x0_lock, flags);
+ writel_relaxed(1, l2x0_base + L2X0_CTRL);
+ spin_unlock_irqrestore(&l2x0_lock, flags);
+}
+
static void l2x0_disable(void)
{
unsigned long flags;
@@ -296,50 +349,49 @@ static void __init l2x0_unlock(__u32 cache_id)
}
}
-void __init l2x0_init(void __iomem *base, __u32 aux_val, __u32 aux_mask)
+void l2x0_init(void __iomem *base, __u32 aux_val, __u32 aux_mask)
{
__u32 aux;
- __u32 cache_id;
__u32 way_size = 0;
- int ways;
const char *type;
l2x0_base = base;
- cache_id = readl_relaxed(l2x0_base + L2X0_CACHE_ID);
+ l2x0_cache_id = readl_relaxed(l2x0_base + L2X0_CACHE_ID);
aux = readl_relaxed(l2x0_base + L2X0_AUX_CTRL);
aux &= aux_mask;
aux |= aux_val;
/* Determine the number of ways */
- switch (cache_id & L2X0_CACHE_ID_PART_MASK) {
+ switch (l2x0_cache_id & L2X0_CACHE_ID_PART_MASK) {
case L2X0_CACHE_ID_PART_L310:
if (aux & (1 << 16))
- ways = 16;
+ l2x0_ways = 16;
else
- ways = 8;
+ l2x0_ways = 8;
type = "L310";
break;
case L2X0_CACHE_ID_PART_L210:
- ways = (aux >> 13) & 0xf;
+ l2x0_ways = (aux >> 13) & 0xf;
type = "L210";
break;
default:
/* Assume unknown chips have 8 ways */
- ways = 8;
+ l2x0_ways = 8;
type = "L2x0 series";
break;
}
- l2x0_way_mask = (1 << ways) - 1;
+ l2x0_way_mask = (1 << l2x0_ways) - 1;
/*
* L2 cache Size = Way size * Number of ways
*/
way_size = (aux & L2X0_AUX_CTRL_WAY_SIZE_MASK) >> 17;
- way_size = 1 << (way_size + 3);
- l2x0_size = ways * way_size * SZ_1K;
+ way_size = SZ_1K << (way_size + 3);
+ l2x0_size = l2x0_ways * way_size;
+ l2x0_sets = way_size / CACHE_LINE_SIZE;
/*
* Check if l2x0 controller is already enabled.
@@ -348,7 +400,7 @@ void __init l2x0_init(void __iomem *base, __u32 aux_val, __u32 aux_mask)
*/
if (!(readl_relaxed(l2x0_base + L2X0_CTRL) & 1)) {
/* Make sure that I&D is not locked down when starting */
- l2x0_unlock(cache_id);
+ l2x0_unlock(l2x0_cache_id);
/* l2x0 controller is disabled */
writel_relaxed(aux, l2x0_base + L2X0_AUX_CTRL);
@@ -368,7 +420,7 @@ void __init l2x0_init(void __iomem *base, __u32 aux_val, __u32 aux_mask)
outer_cache.disable = l2x0_disable;
outer_cache.set_debug = l2x0_set_debug;
- printk(KERN_INFO "%s cache controller enabled\n", type);
- printk(KERN_INFO "l2x0: %d ways, CACHE_ID 0x%08x, AUX_CTRL 0x%08x, Cache size: %d B\n",
- ways, cache_id, aux, l2x0_size);
+ pr_info_once("%s cache controller enabled\n", type);
+ pr_info_once("l2x0: %d ways, CACHE_ID 0x%08x, AUX_CTRL 0x%08x, Cache size: %d B\n",
+ l2x0_ways, l2x0_cache_id, aux, l2x0_size);
}
diff --git a/arch/arm/mm/cache-v6.S b/arch/arm/mm/cache-v6.S
index 74c2e5a33a4d..2edb6f67f69d 100644
--- a/arch/arm/mm/cache-v6.S
+++ b/arch/arm/mm/cache-v6.S
@@ -272,6 +272,11 @@ v6_dma_clean_range:
* - end - virtual end address of region
*/
ENTRY(v6_dma_flush_range)
+#ifdef CONFIG_CACHE_FLUSH_RANGE_LIMIT
+ sub r2, r1, r0
+ cmp r2, #CONFIG_CACHE_FLUSH_RANGE_LIMIT
+ bhi v6_dma_flush_dcache_all
+#endif
#ifdef CONFIG_DMA_CACHE_RWFO
ldrb r2, [r0] @ read for ownership
strb r2, [r0] @ write for ownership
@@ -294,6 +299,18 @@ ENTRY(v6_dma_flush_range)
mcr p15, 0, r0, c7, c10, 4 @ drain write buffer
mov pc, lr
+#ifdef CONFIG_CACHE_FLUSH_RANGE_LIMIT
+v6_dma_flush_dcache_all:
+ mov r0, #0
+#ifdef HARVARD_CACHE
+ mcr p15, 0, r0, c7, c14, 0 @ D cache clean+invalidate
+#else
+ mcr p15, 0, r0, c7, c15, 0 @ Cache clean+invalidate
+#endif
+ mcr p15, 0, r0, c7, c10, 4 @ drain write buffer
+ mov pc, lr
+#endif
+
/*
* dma_map_area(start, size, dir)
* - start - kernel virtual start address
diff --git a/arch/arm/mm/cache-v7.S b/arch/arm/mm/cache-v7.S
index 07c4bc8ea0a4..963325eb083e 100644
--- a/arch/arm/mm/cache-v7.S
+++ b/arch/arm/mm/cache-v7.S
@@ -33,27 +33,28 @@ ENTRY(v7_flush_icache_all)
ENDPROC(v7_flush_icache_all)
/*
- * v7_flush_dcache_all()
+ * v7_op_dcache_all op
*
- * Flush the whole D-cache.
+ * op=c14, Flush the whole D-cache.
+ * op=c10, Clean the whole D-cache.
*
* Corrupted registers: r0-r7, r9-r11 (r6 only in Thumb mode)
*
* - mm - mm_struct describing address space
*/
-ENTRY(v7_flush_dcache_all)
+.macro v7_op_dcache_all op @ op=c10 clean, op=c14 flush
dmb @ ensure ordering with previous memory accesses
mrc p15, 1, r0, c0, c0, 1 @ read clidr
ands r3, r0, #0x7000000 @ extract loc from clidr
mov r3, r3, lsr #23 @ left align loc bit field
- beq finished @ if loc is 0, then no need to clean
+ beq 1005f @ if loc is 0, then no need to clean
mov r10, #0 @ start clean at cache level 0
-loop1:
+1001:
add r2, r10, r10, lsr #1 @ work out 3x current cache level
mov r1, r0, lsr r2 @ extract cache type bits from clidr
and r1, r1, #7 @ mask of the bits for current cache only
cmp r1, #2 @ see what cache we have at this level
- blt skip @ skip if no cache, or just i-cache
+ blt 1004f @ skip if no cache, or just i-cache
mcr p15, 2, r10, c0, c0, 0 @ select current cache level in cssr
isb @ isb to sych the new cssr&csidr
mrc p15, 1, r1, c0, c0, 0 @ read the new csidr
@@ -64,32 +65,40 @@ loop1:
clz r5, r4 @ find bit position of way size increment
ldr r7, =0x7fff
ands r7, r7, r1, lsr #13 @ extract max number of the index size
-loop2:
+1002:
mov r9, r4 @ create working copy of max way size
-loop3:
+1003:
ARM( orr r11, r10, r9, lsl r5 ) @ factor way and cache number into r11
THUMB( lsl r6, r9, r5 )
THUMB( orr r11, r10, r6 ) @ factor way and cache number into r11
ARM( orr r11, r11, r7, lsl r2 ) @ factor index number into r11
THUMB( lsl r6, r7, r2 )
THUMB( orr r11, r11, r6 ) @ factor index number into r11
- mcr p15, 0, r11, c7, c14, 2 @ clean & invalidate by set/way
+ mcr p15, 0, r11, c7, \op, 2 @ op=c10/c14, clean/flush by set/way
subs r9, r9, #1 @ decrement the way
- bge loop3
+ bge 1003b
subs r7, r7, #1 @ decrement the index
- bge loop2
-skip:
+ bge 1002b
+1004:
add r10, r10, #2 @ increment cache number
cmp r3, r10
- bgt loop1
-finished:
+ bgt 1001b
+1005:
mov r10, #0 @ swith back to cache level 0
mcr p15, 2, r10, c0, c0, 0 @ select current cache level in cssr
dsb
isb
mov pc, lr
+.endm
+
+ENTRY(v7_flush_dcache_all)
+ v7_op_dcache_all c14
ENDPROC(v7_flush_dcache_all)
+ENTRY(v7_clean_dcache_all)
+ v7_op_dcache_all c10
+ENDPROC(v7_clean_dcache_all)
+
/*
* v7_flush_cache_all()
*
@@ -114,6 +123,24 @@ ENTRY(v7_flush_kern_cache_all)
ENDPROC(v7_flush_kern_cache_all)
/*
+ * v7_clean_kern_cache_all()
+ */
+ENTRY(v7_clean_kern_cache_all)
+ ARM( stmfd sp!, {r4-r5, r7, r9-r11, lr} )
+ THUMB( stmfd sp!, {r4-r7, r9-r11, lr} )
+ bl v7_clean_dcache_all
+ mov r0, #0
+#ifdef CONFIG_SMP
+ mcr p15, 0, r0, c7, c1, 0 @ invalidate I-cache inner shareable
+#else
+ mcr p15, 0, r0, c7, c5, 0 @ I+BTB cache invalidate
+#endif
+ ARM( ldmfd sp!, {r4-r5, r7, r9-r11, lr} )
+ THUMB( ldmfd sp!, {r4-r7, r9-r11, lr} )
+ mov pc, lr
+ENDPROC(v7_clean_kern_cache_all)
+
+/*
* v7_flush_cache_all()
*
* Flush all TLB entries in a particular address space
diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c
index c3ff82f92d9c..9cd5334019e4 100644
--- a/arch/arm/mm/dma-mapping.c
+++ b/arch/arm/mm/dma-mapping.c
@@ -310,6 +310,13 @@ __dma_alloc(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp,
struct page *page;
void *addr;
+ /* Following is a work-around (a.k.a. hack) to prevent pages
+ * with __GFP_COMP being passed to split_page() which cannot
+ * handle them. The real problem is that this flag probably
+ * should be 0 on ARM as it is not supported on this
+ * platform--see CONFIG_HUGETLB_PAGE. */
+ gfp &= ~(__GFP_COMP);
+
*handle = ~0;
size = PAGE_ALIGN(size);
diff --git a/arch/arm/mm/mmu.c b/arch/arm/mm/mmu.c
index 594d677b92c8..fd38662098cd 100644
--- a/arch/arm/mm/mmu.c
+++ b/arch/arm/mm/mmu.c
@@ -554,6 +554,7 @@ static void __init alloc_init_section(pud_t *pud, unsigned long addr,
const struct mem_type *type)
{
pmd_t *pmd = pmd_offset(pud, addr);
+ unsigned long pages_2m = 0, pages_4k = 0;
/*
* Try a section mapping - end, addr and phys must all be aligned
@@ -572,6 +573,8 @@ static void __init alloc_init_section(pud_t *pud, unsigned long addr,
phys += SECTION_SIZE;
} while (pmd++, addr += SECTION_SIZE, addr != end);
+ pages_2m += (end-addr) >> SECTION_SHIFT;
+
flush_pmd_entry(p);
} else {
/*
@@ -579,6 +582,12 @@ static void __init alloc_init_section(pud_t *pud, unsigned long addr,
* individual L1 entries.
*/
alloc_init_pte(pmd, addr, end, __phys_to_pfn(phys), type);
+ pages_4k += (end-addr) >> PAGE_SHIFT;
+ }
+
+ if ((addr < lowmem_limit) && (end < lowmem_limit)) {
+ update_page_count(PG_LEVEL_2M, pages_2m);
+ update_page_count(PG_LEVEL_4K, pages_4k);
}
}
@@ -757,7 +766,7 @@ static int __init early_vmalloc(char *arg)
}
early_param("vmalloc", early_vmalloc);
-static phys_addr_t lowmem_limit __initdata = 0;
+phys_addr_t lowmem_limit;
void __init sanity_check_meminfo(void)
{
diff --git a/arch/arm/mm/pageattr.c b/arch/arm/mm/pageattr.c
new file mode 100644
index 000000000000..414a8460ca2c
--- /dev/null
+++ b/arch/arm/mm/pageattr.c
@@ -0,0 +1,997 @@
+/*
+ * Copyright 2002 Andi Kleen, SuSE Labs.
+ * Thanks to Ben LaHaise for precious feedback.
+ */
+#include <linux/highmem.h>
+#include <linux/bootmem.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/seq_file.h>
+#include <linux/debugfs.h>
+#include <linux/pfn.h>
+#include <linux/percpu.h>
+#include <linux/gfp.h>
+#include <linux/vmalloc.h>
+
+#include <asm/processor.h>
+#include <asm/tlbflush.h>
+#include <asm/sections.h>
+#include <asm/setup.h>
+#include <asm/uaccess.h>
+#include <asm/pgalloc.h>
+
+#ifdef CPA_DEBUG
+#define cpa_debug(x, ...) printk(x, __VA_ARGS__)
+#else
+#define cpa_debug(x, ...)
+#endif
+
+/*
+ * The current flushing context - we pass it instead of 5 arguments:
+ */
+struct cpa_data {
+ unsigned long *vaddr;
+ pgprot_t mask_set;
+ pgprot_t mask_clr;
+ int numpages;
+ int flags;
+ unsigned long pfn;
+ unsigned force_split:1;
+ int curpage;
+ struct page **pages;
+};
+
+/*
+ * Serialize cpa() (for !DEBUG_PAGEALLOC which uses large identity mappings)
+ * using cpa_lock. So that we don't allow any other cpu, with stale large tlb
+ * entries change the page attribute in parallel to some other cpu
+ * splitting a large page entry along with changing the attribute.
+ */
+static DEFINE_SPINLOCK(cpa_lock);
+
+#define CPA_FLUSHTLB 1
+#define CPA_ARRAY 2
+#define CPA_PAGES_ARRAY 4
+
+#ifdef CONFIG_PROC_FS
+static unsigned long direct_pages_count[PG_LEVEL_NUM];
+
+void update_page_count(int level, unsigned long pages)
+{
+ unsigned long flags;
+
+ /* Protect against CPA */
+ spin_lock_irqsave(&pgd_lock, flags);
+ direct_pages_count[level] += pages;
+ spin_unlock_irqrestore(&pgd_lock, flags);
+}
+
+static void split_page_count(int level)
+{
+ direct_pages_count[level]--;
+ direct_pages_count[level - 1] += PTRS_PER_PTE;
+}
+
+void arch_report_meminfo(struct seq_file *m)
+{
+ seq_printf(m, "DirectMap4k: %8lu kB\n",
+ direct_pages_count[PG_LEVEL_4K] << 2);
+ seq_printf(m, "DirectMap2M: %8lu kB\n",
+ direct_pages_count[PG_LEVEL_2M] << 11);
+}
+#else
+static inline void split_page_count(int level) { }
+#endif
+
+#ifdef CONFIG_DEBUG_PAGEALLOC
+# define debug_pagealloc 1
+#else
+# define debug_pagealloc 0
+#endif
+
+static inline int
+within(unsigned long addr, unsigned long start, unsigned long end)
+{
+ return addr >= start && addr < end;
+}
+
+static void cpa_flush_range(unsigned long start, int numpages, int cache)
+{
+ unsigned int i, level;
+ unsigned long addr;
+
+ BUG_ON(irqs_disabled());
+ WARN_ON(PAGE_ALIGN(start) != start);
+
+ flush_tlb_kernel_range(start, start + (numpages << PAGE_SHIFT));
+
+ if (!cache)
+ return;
+
+ for (i = 0, addr = start; i < numpages; i++, addr += PAGE_SIZE) {
+ pte_t *pte = lookup_address(addr, &level);
+
+ /*
+ * Only flush present addresses:
+ */
+ if (pte && pte_present(*pte)) {
+ __cpuc_flush_dcache_area((void *) addr, PAGE_SIZE);
+ outer_flush_range(__pa((void *)addr),
+ __pa((void *)addr) + PAGE_SIZE);
+ }
+ }
+}
+
+static void cpa_flush_array(unsigned long *start, int numpages, int cache,
+ int in_flags, struct page **pages)
+{
+ unsigned int i, level;
+
+ BUG_ON(irqs_disabled());
+
+ for (i = 0; i < numpages; i++) {
+ unsigned long addr;
+ pte_t *pte;
+
+ if (in_flags & CPA_PAGES_ARRAY)
+ addr = (unsigned long)page_address(pages[i]);
+ else
+ addr = start[i];
+
+ flush_tlb_kernel_range(addr, addr + PAGE_SIZE);
+
+ if (cache) {
+
+ pte = lookup_address(addr, &level);
+
+ /*
+ * Only flush present addresses:
+ */
+ if (pte && pte_present(*pte)) {
+ __cpuc_flush_dcache_area((void *)addr,
+ PAGE_SIZE);
+ outer_flush_range(__pa((void *)addr),
+ __pa((void *)addr) + PAGE_SIZE);
+ }
+ }
+ }
+}
+
+/*
+ * Certain areas of memory require very specific protection flags,
+ * for example the kernel text. Callers don't always get this
+ * right so this function checks and fixes these known static
+ * required protection bits.
+ */
+static inline pgprot_t static_protections(pgprot_t prot, unsigned long address,
+ unsigned long pfn)
+{
+ pgprot_t forbidden = __pgprot(0);
+
+ /*
+ * The kernel text needs to be executable for obvious reasons
+ * Does not cover __inittext since that is gone later on.
+ */
+ if (within(address, (unsigned long)_text, (unsigned long)_etext))
+ pgprot_val(forbidden) |= L_PTE_XN;
+
+ /*
+ * The .rodata section needs to be read-only. Using the pfn
+ * catches all aliases.
+ */
+ if (within(pfn, __pa((unsigned long)__start_rodata) >> PAGE_SHIFT,
+ __pa((unsigned long)__end_rodata) >> PAGE_SHIFT))
+ prot |= L_PTE_RDONLY;
+
+ /*
+ * Mask off the forbidden bits and set the bits that are needed
+ */
+ prot = __pgprot(pgprot_val(prot) & ~pgprot_val(forbidden));
+
+
+ return prot;
+}
+
+static inline pgprot_t pte_to_pmd_pgprot(unsigned long pte,
+ unsigned long ext_prot)
+{
+ pgprot_t ref_prot;
+
+ ref_prot = PMD_TYPE_SECT | PMD_SECT_AP_WRITE;
+
+ if (pte & L_PTE_MT_BUFFERABLE)
+ ref_prot |= PMD_SECT_BUFFERABLE;
+
+ if (pte & L_PTE_MT_WRITETHROUGH)
+ ref_prot |= PMD_SECT_CACHEABLE;
+
+ if (pte & L_PTE_SHARED)
+ ref_prot |= PMD_SECT_S;
+
+ if (pte & L_PTE_XN)
+ ref_prot |= PMD_SECT_XN;
+
+ if (pte & L_PTE_RDONLY)
+ ref_prot &= ~PMD_SECT_AP_WRITE;
+
+ ref_prot |= (ext_prot & (PTE_EXT_AP0 | PTE_EXT_AP1 | PTE_EXT_APX |
+ PTE_EXT_NG | (7 << 6))) << 6;
+
+ return ref_prot;
+}
+
+static inline pgprot_t pmd_to_pte_pgprot(unsigned long pmd,
+ unsigned long *ext_prot)
+{
+ pgprot_t ref_prot = 0;
+
+ ref_prot |= L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY | L_PTE_RDONLY;
+
+ if (pmd & PMD_SECT_BUFFERABLE)
+ ref_prot |= L_PTE_MT_BUFFERABLE;
+
+ if (pmd & PMD_SECT_CACHEABLE)
+ ref_prot |= L_PTE_MT_WRITETHROUGH;
+
+ if (pmd & PMD_SECT_S)
+ ref_prot |= L_PTE_SHARED;
+
+ if (pmd & PMD_SECT_XN)
+ ref_prot |= L_PTE_XN;
+
+ if (pmd & PMD_SECT_AP_WRITE)
+ ref_prot &= ~L_PTE_RDONLY;
+
+ /* AP/APX/TEX bits */
+ *ext_prot = (pmd & (PMD_SECT_AP_WRITE | PMD_SECT_AP_READ |
+ PMD_SECT_APX | PMD_SECT_nG | (7 << 12))) >> 6;
+
+ return ref_prot;
+}
+
+/*
+ * Lookup the page table entry for a virtual address. Return a pointer
+ * to the entry and the level of the mapping.
+ *
+ * Note: We return pud and pmd either when the entry is marked large
+ * or when the present bit is not set. Otherwise we would return a
+ * pointer to a nonexisting mapping.
+ */
+pte_t *lookup_address(unsigned long address, unsigned int *level)
+{
+ pgd_t *pgd = pgd_offset_k(address);
+ pte_t *pte;
+ pmd_t *pmd;
+
+ /* pmds are folded into pgds on ARM */
+ *level = PG_LEVEL_NONE;
+
+ if (pgd == NULL || pgd_none(*pgd))
+ return NULL;
+
+ pmd = pmd_offset(pgd, address);
+
+ if (pmd == NULL || pmd_none(*pmd) || !pmd_present(*pmd))
+ return NULL;
+
+ if (((pmd_val(*pmd) & (PMD_TYPE_SECT | PMD_SECT_SUPER))
+ == (PMD_TYPE_SECT | PMD_SECT_SUPER)) || !pmd_present(*pmd)) {
+
+ return NULL;
+ } else if (pmd_val(*pmd) & PMD_TYPE_SECT) {
+
+ *level = PG_LEVEL_2M;
+ return (pte_t *)pmd;
+ }
+
+ pte = pte_offset_kernel(pmd, address);
+
+ if ((pte == NULL) || pte_none(*pte))
+ return NULL;
+
+ *level = PG_LEVEL_4K;
+
+ return pte;
+}
+EXPORT_SYMBOL_GPL(lookup_address);
+
+/*
+ * Set the new pmd in all the pgds we know about:
+ */
+static void __set_pmd_pte(pmd_t *pmd, unsigned long address, pte_t *pte)
+{
+ struct page *page;
+
+ cpa_debug("__set_pmd_pte %x %x %x\n", pmd, pte, *pte);
+
+ /* change init_mm */
+ pmd_populate_kernel(&init_mm, pmd, pte);
+
+ /* change entry in all the pgd's */
+ list_for_each_entry(page, &pgd_list, lru) {
+ cpa_debug("list %x %x %x\n", (unsigned long)page,
+ (unsigned long)pgd_index(address), address);
+ pmd = pmd_offset(((pgd_t *)page_address(page)) +
+ pgd_index(address), address);
+ pmd_populate_kernel(NULL, pmd, pte);
+ }
+
+}
+
+static int
+try_preserve_large_page(pte_t *kpte, unsigned long address,
+ struct cpa_data *cpa)
+{
+ unsigned long nextpage_addr, numpages, pmask, psize, flags, addr, pfn;
+ pte_t old_pte, *tmp;
+ pgprot_t old_prot, new_prot, ext_prot, req_prot;
+ int i, do_split = 1;
+ unsigned int level;
+
+ if (cpa->force_split)
+ return 1;
+
+ spin_lock_irqsave(&pgd_lock, flags);
+ /*
+ * Check for races, another CPU might have split this page
+ * up already:
+ */
+ tmp = lookup_address(address, &level);
+ if (tmp != kpte)
+ goto out_unlock;
+
+ switch (level) {
+
+ case PG_LEVEL_2M:
+ psize = PMD_SIZE;
+ pmask = PMD_MASK;
+ break;
+
+ default:
+ do_split = -EINVAL;
+ goto out_unlock;
+ }
+
+ /*
+ * Calculate the number of pages, which fit into this large
+ * page starting at address:
+ */
+ nextpage_addr = (address + psize) & pmask;
+ numpages = (nextpage_addr - address) >> PAGE_SHIFT;
+ if (numpages < cpa->numpages)
+ cpa->numpages = numpages;
+
+ old_prot = new_prot = req_prot = pmd_to_pte_pgprot(pmd_val(*kpte),
+ &ext_prot);
+
+ pgprot_val(req_prot) &= ~pgprot_val(cpa->mask_clr);
+ pgprot_val(req_prot) |= pgprot_val(cpa->mask_set);
+
+ /*
+ * old_pte points to the large page base address. So we need
+ * to add the offset of the virtual address:
+ */
+ pfn = pmd_pfn(*kpte) + ((address & (psize - 1)) >> PAGE_SHIFT);
+ cpa->pfn = pfn;
+
+ new_prot = static_protections(req_prot, address, pfn);
+
+ /*
+ * We need to check the full range, whether
+ * static_protection() requires a different pgprot for one of
+ * the pages in the range we try to preserve:
+ */
+ addr = address & pmask;
+ pfn = pmd_pfn(old_pte);
+ for (i = 0; i < (psize >> PAGE_SHIFT); i++, addr += PAGE_SIZE, pfn++) {
+ pgprot_t chk_prot = static_protections(req_prot, addr, pfn);
+
+ if (pgprot_val(chk_prot) != pgprot_val(new_prot))
+ goto out_unlock;
+ }
+
+ /*
+ * If there are no changes, return. maxpages has been updated
+ * above:
+ */
+ if (pgprot_val(new_prot) == pgprot_val(old_prot)) {
+ do_split = 0;
+ goto out_unlock;
+ }
+
+ /*
+ * convert prot to pmd format
+ */
+ new_prot = pte_to_pmd_pgprot(new_prot, ext_prot);
+
+ /*
+ * We need to change the attributes. Check, whether we can
+ * change the large page in one go. We request a split, when
+ * the address is not aligned and the number of pages is
+ * smaller than the number of pages in the large page. Note
+ * that we limited the number of possible pages already to
+ * the number of pages in the large page.
+ */
+ if (address == (nextpage_addr - psize) && cpa->numpages == numpages) {
+ /*
+ * The address is aligned and the number of pages
+ * covers the full page.
+ */
+ phys_addr_t phys = __pfn_to_phys(pmd_pfn(*kpte));
+ pmd_t *p = (pmd_t *)kpte;
+
+ *kpte++ = __pmd(phys | new_prot);
+ *kpte = __pmd((phys + SECTION_SIZE) | new_prot);
+ flush_pmd_entry(p);
+ cpa->flags |= CPA_FLUSHTLB;
+ do_split = 0;
+ cpa_debug("preserving page at phys %x pmd %x\n", phys, p);
+ }
+
+out_unlock:
+ spin_unlock_irqrestore(&pgd_lock, flags);
+
+ return do_split;
+}
+
+static int split_large_page(pte_t *kpte, unsigned long address)
+{
+ unsigned long flags, pfn, pfninc = 1;
+ unsigned int i, level;
+ pte_t *pbase, *tmp;
+ pgprot_t ref_prot = 0, ext_prot = 0;
+ int ret = 0;
+
+ pbase = pte_alloc_one_kernel(&init_mm, address);
+ if (!pbase)
+ return -ENOMEM;
+
+ cpa_debug("split_large_page %x PMD %x new pte @ %x\n", address,
+ *kpte, pbase);
+
+ spin_lock_irqsave(&pgd_lock, flags);
+ /*
+ * Check for races, another CPU might have split this page
+ * up for us already:
+ */
+ tmp = lookup_address(address, &level);
+ if (tmp != kpte)
+ goto out_unlock;
+
+ /*
+ * we only split 2MB entries for now
+ */
+ if (level != PG_LEVEL_2M) {
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+
+ ref_prot = pmd_to_pte_pgprot(pmd_val(*kpte), &ext_prot);
+
+ /*
+ * Get the target pfn from the original entry:
+ */
+ pfn = pmd_pfn(*kpte);
+ for (i = 0; i < PTRS_PER_PTE; i++, pfn += pfninc)
+ set_pte_ext(&pbase[i], pfn_pte(pfn, ref_prot), ext_prot);
+
+ if (address >= (unsigned long)__va(0) &&
+ address < (unsigned long)__va(lowmem_limit))
+ split_page_count(level);
+
+ /*
+ * Install the new, split up pagetable.
+ */
+ __set_pmd_pte((pmd_t *)kpte, address, pbase);
+
+ pbase = NULL;
+
+out_unlock:
+ /*
+ * If we dropped out via the lookup_address check under
+ * pgd_lock then stick the page back into the pool:
+ */
+ if (pbase)
+ pte_free_kernel(&init_mm, pbase);
+
+ spin_unlock_irqrestore(&pgd_lock, flags);
+
+ return ret;
+}
+
+static int __cpa_process_fault(struct cpa_data *cpa, unsigned long vaddr,
+ int primary)
+{
+ /*
+ * Ignore all non primary paths.
+ */
+ if (!primary)
+ return 0;
+
+ /*
+ * Ignore the NULL PTE for kernel identity mapping, as it is expected
+ * to have holes.
+ * Also set numpages to '1' indicating that we processed cpa req for
+ * one virtual address page and its pfn. TBD: numpages can be set based
+ * on the initial value and the level returned by lookup_address().
+ */
+ if (within(vaddr, PAGE_OFFSET,
+ PAGE_OFFSET + lowmem_limit)) {
+ cpa->numpages = 1;
+ cpa->pfn = __pa(vaddr) >> PAGE_SHIFT;
+ return 0;
+ } else {
+ WARN(1, KERN_WARNING "CPA: called for zero pte. "
+ "vaddr = %lx cpa->vaddr = %lx\n", vaddr,
+ *cpa->vaddr);
+
+ return -EFAULT;
+ }
+}
+
+static int __change_page_attr(struct cpa_data *cpa, int primary)
+{
+ unsigned long address;
+ int do_split, err;
+ unsigned int level;
+ pte_t *kpte, old_pte;
+
+ if (cpa->flags & CPA_PAGES_ARRAY) {
+ struct page *page = cpa->pages[cpa->curpage];
+
+ if (unlikely(PageHighMem(page)))
+ return 0;
+
+ address = (unsigned long)page_address(page);
+
+ } else if (cpa->flags & CPA_ARRAY)
+ address = cpa->vaddr[cpa->curpage];
+ else
+ address = *cpa->vaddr;
+
+repeat:
+ kpte = lookup_address(address, &level);
+ if (!kpte)
+ return __cpa_process_fault(cpa, address, primary);
+
+ old_pte = *kpte;
+ if (!pte_val(old_pte))
+ return __cpa_process_fault(cpa, address, primary);
+
+ if (level == PG_LEVEL_4K) {
+ pte_t new_pte;
+ pgprot_t new_prot = pte_pgprot(old_pte);
+ unsigned long pfn = pte_pfn(old_pte);
+
+ pgprot_val(new_prot) &= ~pgprot_val(cpa->mask_clr);
+ pgprot_val(new_prot) |= pgprot_val(cpa->mask_set);
+
+ new_prot = static_protections(new_prot, address, pfn);
+
+ /*
+ * We need to keep the pfn from the existing PTE,
+ * after all we're only going to change it's attributes
+ * not the memory it points to
+ */
+ new_pte = pfn_pte(pfn, new_prot);
+ cpa->pfn = pfn;
+
+ /*
+ * Do we really change anything ?
+ */
+ if (pte_val(old_pte) != pte_val(new_pte)) {
+ set_pte_ext(kpte, new_pte, 0);
+ /*
+ * FIXME : is this needed on arm?
+ * set_pte_ext already does a flush
+ */
+ cpa->flags |= CPA_FLUSHTLB;
+ }
+ cpa->numpages = 1;
+ return 0;
+ }
+
+ /*
+ * Check, whether we can keep the large page intact
+ * and just change the pte:
+ */
+ do_split = try_preserve_large_page(kpte, address, cpa);
+
+ /*
+ * When the range fits into the existing large page,
+ * return. cp->numpages and cpa->tlbflush have been updated in
+ * try_large_page:
+ */
+ if (do_split <= 0)
+ return do_split;
+
+ /*
+ * We have to split the large page:
+ */
+ err = split_large_page(kpte, address);
+
+ if (!err) {
+ /*
+ * Do a global flush tlb after splitting the large page
+ * and before we do the actual change page attribute in the PTE.
+ *
+ * With out this, we violate the TLB application note, that says
+ * "The TLBs may contain both ordinary and large-page
+ * translations for a 4-KByte range of linear addresses. This
+ * may occur if software modifies the paging structures so that
+ * the page size used for the address range changes. If the two
+ * translations differ with respect to page frame or attributes
+ * (e.g., permissions), processor behavior is undefined and may
+ * be implementation-specific."
+ *
+ * We do this global tlb flush inside the cpa_lock, so that we
+ * don't allow any other cpu, with stale tlb entries change the
+ * page attribute in parallel, that also falls into the
+ * just split large page entry.
+ */
+ flush_tlb_all();
+ goto repeat;
+ }
+
+ return err;
+}
+
+static int __change_page_attr_set_clr(struct cpa_data *cpa, int checkalias);
+
+static int cpa_process_alias(struct cpa_data *cpa)
+{
+ struct cpa_data alias_cpa;
+ unsigned long laddr = (unsigned long)__va(cpa->pfn << PAGE_SHIFT);
+ unsigned long vaddr;
+ int ret;
+
+ if (cpa->pfn >= (lowmem_limit >> PAGE_SHIFT))
+ return 0;
+
+ /*
+ * No need to redo, when the primary call touched the direct
+ * mapping already:
+ */
+ if (cpa->flags & CPA_PAGES_ARRAY) {
+ struct page *page = cpa->pages[cpa->curpage];
+ if (unlikely(PageHighMem(page)))
+ return 0;
+ vaddr = (unsigned long)page_address(page);
+ } else if (cpa->flags & CPA_ARRAY)
+ vaddr = cpa->vaddr[cpa->curpage];
+ else
+ vaddr = *cpa->vaddr;
+
+ if (!(within(vaddr, PAGE_OFFSET,
+ PAGE_OFFSET + lowmem_limit))) {
+
+ alias_cpa = *cpa;
+ alias_cpa.vaddr = &laddr;
+ alias_cpa.flags &= ~(CPA_PAGES_ARRAY | CPA_ARRAY);
+
+ ret = __change_page_attr_set_clr(&alias_cpa, 0);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int __change_page_attr_set_clr(struct cpa_data *cpa, int checkalias)
+{
+ int ret, numpages = cpa->numpages;
+
+ while (numpages) {
+ /*
+ * Store the remaining nr of pages for the large page
+ * preservation check.
+ */
+ cpa->numpages = numpages;
+ /* for array changes, we can't use large page */
+ if (cpa->flags & (CPA_ARRAY | CPA_PAGES_ARRAY))
+ cpa->numpages = 1;
+
+ if (!debug_pagealloc)
+ spin_lock(&cpa_lock);
+ ret = __change_page_attr(cpa, checkalias);
+ if (!debug_pagealloc)
+ spin_unlock(&cpa_lock);
+ if (ret)
+ return ret;
+
+ if (checkalias) {
+ ret = cpa_process_alias(cpa);
+ if (ret)
+ return ret;
+ }
+
+ /*
+ * Adjust the number of pages with the result of the
+ * CPA operation. Either a large page has been
+ * preserved or a single page update happened.
+ */
+ BUG_ON(cpa->numpages > numpages);
+ numpages -= cpa->numpages;
+ if (cpa->flags & (CPA_PAGES_ARRAY | CPA_ARRAY))
+ cpa->curpage++;
+ else
+ *cpa->vaddr += cpa->numpages * PAGE_SIZE;
+ }
+ return 0;
+}
+
+static inline int cache_attr(pgprot_t attr)
+{
+ /*
+ * We need to flush the cache for all memory type changes
+ * except when a page is being marked write back cacheable
+ */
+ return !((pgprot_val(attr) & L_PTE_MT_MASK) == L_PTE_MT_WRITEBACK);
+}
+
+static int change_page_attr_set_clr(unsigned long *addr, int numpages,
+ pgprot_t mask_set, pgprot_t mask_clr,
+ int force_split, int in_flag,
+ struct page **pages)
+{
+ struct cpa_data cpa;
+ int ret, cache, checkalias;
+ unsigned long baddr = 0;
+
+ if (!pgprot_val(mask_set) && !pgprot_val(mask_clr) && !force_split)
+ return 0;
+
+ /* Ensure we are PAGE_SIZE aligned */
+ if (in_flag & CPA_ARRAY) {
+ int i;
+ for (i = 0; i < numpages; i++) {
+ if (addr[i] & ~PAGE_MASK) {
+ addr[i] &= PAGE_MASK;
+ WARN_ON_ONCE(1);
+ }
+ }
+ } else if (!(in_flag & CPA_PAGES_ARRAY)) {
+ /*
+ * in_flag of CPA_PAGES_ARRAY implies it is aligned.
+ * No need to cehck in that case
+ */
+ if (*addr & ~PAGE_MASK) {
+ *addr &= PAGE_MASK;
+ /*
+ * People should not be passing in unaligned addresses:
+ */
+ WARN_ON_ONCE(1);
+ }
+ /*
+ * Save address for cache flush. *addr is modified in the call
+ * to __change_page_attr_set_clr() below.
+ */
+ baddr = *addr;
+ }
+
+ /* Must avoid aliasing mappings in the highmem code */
+ kmap_flush_unused();
+
+ vm_unmap_aliases();
+
+ cpa.vaddr = addr;
+ cpa.pages = pages;
+ cpa.numpages = numpages;
+ cpa.mask_set = mask_set;
+ cpa.mask_clr = mask_clr;
+ cpa.flags = 0;
+ cpa.curpage = 0;
+ cpa.force_split = force_split;
+
+ if (in_flag & (CPA_ARRAY | CPA_PAGES_ARRAY))
+ cpa.flags |= in_flag;
+
+ /* No alias checking for XN bit modifications */
+ checkalias = (pgprot_val(mask_set) |
+ pgprot_val(mask_clr)) != L_PTE_XN;
+
+ ret = __change_page_attr_set_clr(&cpa, checkalias);
+
+ /*
+ * Check whether we really changed something:
+ */
+ if (!(cpa.flags & CPA_FLUSHTLB))
+ goto out;
+
+ cache = cache_attr(mask_set);
+
+ if (cpa.flags & (CPA_PAGES_ARRAY | CPA_ARRAY)) {
+ cpa_flush_array(addr, numpages, cache,
+ cpa.flags, pages);
+ } else
+ cpa_flush_range(baddr, numpages, cache);
+
+out:
+ return ret;
+}
+
+static inline int change_page_attr_set(unsigned long *addr, int numpages,
+ pgprot_t mask, int array)
+{
+ return change_page_attr_set_clr(addr, numpages, mask, __pgprot(0), 0,
+ (array ? CPA_ARRAY : 0), NULL);
+}
+
+static inline int change_page_attr_clear(unsigned long *addr, int numpages,
+ pgprot_t mask, int array)
+{
+ return change_page_attr_set_clr(addr, numpages, __pgprot(0), mask, 0,
+ (array ? CPA_ARRAY : 0), NULL);
+}
+
+static inline int cpa_set_pages_array(struct page **pages, int numpages,
+ pgprot_t mask)
+{
+ return change_page_attr_set_clr(NULL, numpages, mask, __pgprot(0), 0,
+ CPA_PAGES_ARRAY, pages);
+}
+
+static inline int cpa_clear_pages_array(struct page **pages, int numpages,
+ pgprot_t mask)
+{
+ return change_page_attr_set_clr(NULL, numpages, __pgprot(0), mask, 0,
+ CPA_PAGES_ARRAY, pages);
+}
+
+int set_memory_uc(unsigned long addr, int numpages)
+{
+ return change_page_attr_set_clr(&addr, numpages,
+ __pgprot(L_PTE_MT_UNCACHED),
+ __pgprot(L_PTE_MT_MASK), 0, 0, NULL);
+}
+EXPORT_SYMBOL(set_memory_uc);
+
+int _set_memory_array(unsigned long *addr, int addrinarray,
+ unsigned long set, unsigned long clr)
+{
+ return change_page_attr_set_clr(addr, addrinarray, __pgprot(set),
+ __pgprot(clr), 0, CPA_ARRAY, NULL);
+}
+
+int set_memory_array_uc(unsigned long *addr, int addrinarray)
+{
+ return _set_memory_array(addr, addrinarray,
+ L_PTE_MT_UNCACHED, L_PTE_MT_MASK);
+}
+EXPORT_SYMBOL(set_memory_array_uc);
+
+int set_memory_array_wc(unsigned long *addr, int addrinarray)
+{
+ return _set_memory_array(addr, addrinarray,
+ L_PTE_MT_BUFFERABLE, L_PTE_MT_MASK);
+}
+EXPORT_SYMBOL(set_memory_array_wc);
+
+int set_memory_wc(unsigned long addr, int numpages)
+{
+ int ret;
+
+ ret = change_page_attr_set_clr(&addr, numpages,
+ __pgprot(L_PTE_MT_BUFFERABLE),
+ __pgprot(L_PTE_MT_MASK),
+ 0, 0, NULL);
+ return ret;
+}
+EXPORT_SYMBOL(set_memory_wc);
+
+int set_memory_wb(unsigned long addr, int numpages)
+{
+ return change_page_attr_set_clr(&addr, numpages,
+ __pgprot(L_PTE_MT_WRITEBACK),
+ __pgprot(L_PTE_MT_MASK),
+ 0, 0, NULL);
+}
+EXPORT_SYMBOL(set_memory_wb);
+
+int set_memory_iwb(unsigned long addr, int numpages)
+{
+ return change_page_attr_set_clr(&addr, numpages,
+ __pgprot(L_PTE_MT_INNER_WB),
+ __pgprot(L_PTE_MT_MASK),
+ 0, 0, NULL);
+}
+EXPORT_SYMBOL(set_memory_iwb);
+
+int set_memory_array_wb(unsigned long *addr, int addrinarray)
+{
+ return change_page_attr_set_clr(addr, addrinarray,
+ __pgprot(L_PTE_MT_WRITEBACK),
+ __pgprot(L_PTE_MT_MASK),
+ 0, CPA_ARRAY, NULL);
+
+}
+EXPORT_SYMBOL(set_memory_array_wb);
+
+int set_memory_array_iwb(unsigned long *addr, int addrinarray)
+{
+ return change_page_attr_set_clr(addr, addrinarray,
+ __pgprot(L_PTE_MT_INNER_WB),
+ __pgprot(L_PTE_MT_MASK),
+ 0, CPA_ARRAY, NULL);
+
+}
+EXPORT_SYMBOL(set_memory_array_iwb);
+
+int set_memory_x(unsigned long addr, int numpages)
+{
+ return change_page_attr_clear(&addr, numpages,
+ __pgprot(L_PTE_XN), 0);
+}
+EXPORT_SYMBOL(set_memory_x);
+
+int set_memory_nx(unsigned long addr, int numpages)
+{
+ return change_page_attr_set(&addr, numpages,
+ __pgprot(L_PTE_XN), 0);
+}
+EXPORT_SYMBOL(set_memory_nx);
+
+int set_memory_ro(unsigned long addr, int numpages)
+{
+ return change_page_attr_set(&addr, numpages,
+ __pgprot(L_PTE_RDONLY), 0);
+}
+EXPORT_SYMBOL_GPL(set_memory_ro);
+
+int set_memory_rw(unsigned long addr, int numpages)
+{
+ return change_page_attr_clear(&addr, numpages,
+ __pgprot(L_PTE_RDONLY), 0);
+}
+EXPORT_SYMBOL_GPL(set_memory_rw);
+
+int set_memory_np(unsigned long addr, int numpages)
+{
+ return change_page_attr_clear(&addr, numpages,
+ __pgprot(L_PTE_PRESENT), 0);
+}
+
+int set_memory_4k(unsigned long addr, int numpages)
+{
+ return change_page_attr_set_clr(&addr, numpages, __pgprot(0),
+ __pgprot(0), 1, 0, NULL);
+}
+
+static int _set_pages_array(struct page **pages, int addrinarray,
+ unsigned long set, unsigned long clr)
+{
+ return change_page_attr_set_clr(NULL, addrinarray,
+ __pgprot(set),
+ __pgprot(clr),
+ 0, CPA_PAGES_ARRAY, pages);
+}
+
+int set_pages_array_uc(struct page **pages, int addrinarray)
+{
+ return _set_pages_array(pages, addrinarray,
+ L_PTE_MT_UNCACHED, L_PTE_MT_MASK);
+}
+EXPORT_SYMBOL(set_pages_array_uc);
+
+int set_pages_array_wc(struct page **pages, int addrinarray)
+{
+ return _set_pages_array(pages, addrinarray, L_PTE_MT_BUFFERABLE,
+ L_PTE_MT_MASK);
+}
+EXPORT_SYMBOL(set_pages_array_wc);
+
+int set_pages_array_wb(struct page **pages, int addrinarray)
+{
+ return _set_pages_array(pages, addrinarray,
+ L_PTE_MT_WRITEBACK, L_PTE_MT_MASK);
+}
+EXPORT_SYMBOL(set_pages_array_wb);
+
+int set_pages_array_iwb(struct page **pages, int addrinarray)
+{
+ return _set_pages_array(pages, addrinarray,
+ L_PTE_MT_INNER_WB, L_PTE_MT_MASK);
+}
+EXPORT_SYMBOL(set_pages_array_iwb);
diff --git a/arch/arm/mm/pgd.c b/arch/arm/mm/pgd.c
index b2027c154b2a..3e9503bb7bf5 100644
--- a/arch/arm/mm/pgd.c
+++ b/arch/arm/mm/pgd.c
@@ -17,6 +17,23 @@
#include "mm.h"
+DEFINE_SPINLOCK(pgd_lock);
+LIST_HEAD(pgd_list);
+
+static inline void pgd_list_add(pgd_t *pgd)
+{
+ struct page *page = virt_to_page(pgd);
+
+ list_add(&page->lru, &pgd_list);
+}
+
+static inline void pgd_list_del(pgd_t *pgd)
+{
+ struct page *page = virt_to_page(pgd);
+
+ list_del(&page->lru);
+}
+
/*
* need to get a 16k page for level 1
*/
@@ -26,6 +43,7 @@ pgd_t *pgd_alloc(struct mm_struct *mm)
pud_t *new_pud, *init_pud;
pmd_t *new_pmd, *init_pmd;
pte_t *new_pte, *init_pte;
+ unsigned long flags;
new_pgd = (pgd_t *)__get_free_pages(GFP_KERNEL, 2);
if (!new_pgd)
@@ -33,6 +51,7 @@ pgd_t *pgd_alloc(struct mm_struct *mm)
memset(new_pgd, 0, USER_PTRS_PER_PGD * sizeof(pgd_t));
+ spin_lock_irqsave(&pgd_lock, flags);
/*
* Copy over the kernel and IO PGD entries
*/
@@ -40,7 +59,12 @@ pgd_t *pgd_alloc(struct mm_struct *mm)
memcpy(new_pgd + USER_PTRS_PER_PGD, init_pgd + USER_PTRS_PER_PGD,
(PTRS_PER_PGD - USER_PTRS_PER_PGD) * sizeof(pgd_t));
+#if !defined(CONFIG_CPU_CACHE_V7) || !defined(CONFIG_SMP)
clean_dcache_area(new_pgd, PTRS_PER_PGD * sizeof(pgd_t));
+#endif
+
+ pgd_list_add(new_pgd);
+ spin_unlock_irqrestore(&pgd_lock, flags);
if (!vectors_high()) {
/*
@@ -74,6 +98,9 @@ no_pte:
no_pmd:
pud_free(mm, new_pud);
no_pud:
+ spin_lock_irqsave(&pgd_lock, flags);
+ pgd_list_del(new_pgd);
+ spin_unlock_irqrestore(&pgd_lock, flags);
free_pages((unsigned long)new_pgd, 2);
no_pgd:
return NULL;
@@ -85,10 +112,15 @@ void pgd_free(struct mm_struct *mm, pgd_t *pgd_base)
pud_t *pud;
pmd_t *pmd;
pgtable_t pte;
+ unsigned long flags;
if (!pgd_base)
return;
+ spin_lock_irqsave(&pgd_lock, flags);
+ pgd_list_del(pgd_base);
+ spin_unlock_irqrestore(&pgd_lock, flags);
+
pgd = pgd_base + pgd_index(0);
if (pgd_none_or_clear_bad(pgd))
goto no_pgd;
diff --git a/arch/arm/mm/proc-macros.S b/arch/arm/mm/proc-macros.S
index 307a4def8d3a..87f8ee2ebf78 100644
--- a/arch/arm/mm/proc-macros.S
+++ b/arch/arm/mm/proc-macros.S
@@ -121,7 +121,7 @@
.long PTE_CACHEABLE @ L_PTE_MT_WRITETHROUGH
.long PTE_CACHEABLE | PTE_BUFFERABLE @ L_PTE_MT_WRITEBACK
.long PTE_BUFFERABLE @ L_PTE_MT_DEV_SHARED
- .long 0x00 @ unused
+ .long PTE_EXT_TEX(4) | PTE_BUFFERABLE @ L_PTE_MT_INNER_WB
.long 0x00 @ L_PTE_MT_MINICACHE (not present)
.long PTE_EXT_TEX(1) | PTE_CACHEABLE | PTE_BUFFERABLE @ L_PTE_MT_WRITEALLOC
.long 0x00 @ unused
diff --git a/arch/arm/mm/proc-v7.S b/arch/arm/mm/proc-v7.S
index 9049c0764db2..e666e4fe029c 100644
--- a/arch/arm/mm/proc-v7.S
+++ b/arch/arm/mm/proc-v7.S
@@ -176,7 +176,9 @@ ENTRY(cpu_v7_set_pte_ext)
ARM( str r3, [r0, #2048]! )
THUMB( add r0, r0, #2048 )
THUMB( str r3, [r0] )
- mcr p15, 0, r0, c7, c10, 1 @ flush_pte
+ mrc p15, 0, r3, c0, c1, 7 @ read ID_MMFR3
+ tst r3, #0xf << 20 @ check the coherent walk bits
+ mcreq p15, 0, r0, c7, c10, 1 @ flush_pte
#endif
mov pc, lr
ENDPROC(cpu_v7_set_pte_ext)
@@ -212,38 +214,136 @@ ENDPROC(cpu_v7_set_pte_ext)
* NS1 = PRRR[19] = 1 - normal shareable property
* NOS = PRRR[24+n] = 1 - not outer shareable
*/
-.equ PRRR, 0xff0a81a8
-.equ NMRR, 0x40e040e0
+.equ PRRR, 0xff0a89a8
+.equ NMRR, 0xc0e044e0
/* Suspend/resume support: derived from arch/arm/mach-s5pv210/sleep.S */
.globl cpu_v7_suspend_size
-.equ cpu_v7_suspend_size, 4 * 9
+.equ cpu_v7_suspend_size, (4 * (10 + 4 + (16 * 2) + (16 * 2)))
+/* 10 CP15 registers
+ * 4 CP14 registers
+ * 16x2 CP14 breakpoint registers (maximum)
+ * 16x2 CP14 watchpoint registers (maximum)
+ */
+
+.macro save_brkpt cm
+ mrc p14, 0, r4, c0, \cm, 4
+ mrc p14, 0, r5, c0, \cm, 5
+ stmia r0!, {r4 - r5}
+.endm
+
+.macro restore_brkpt cm
+ ldmia r0!, {r4 - r5}
+ mcr p14, 0, r4, c0, \cm, 4
+ mcr p14, 0, r5, c0, \cm, 5
+.endm
+
+.macro save_wpt cm
+ mrc p14, 0, r4, c0, \cm, 6
+ mrc p14, 0, r5, c0, \cm, 7
+ stmia r0!, {r4 - r5}
+.endm
+
+.macro restore_wpt cm
+ ldmia r0!, {r4 - r5}
+ mcr p14, 0, r4, c0, \cm, 6
+ mcr p14, 0, r5, c0, \cm, 7
+.endm
+
#ifdef CONFIG_PM_SLEEP
ENTRY(cpu_v7_do_suspend)
- stmfd sp!, {r4 - r11, lr}
+ stmfd sp!, {r0, r3 - r11, lr}
+ mrc p15, 0, r3, c15, c0, 1 @ diag
mrc p15, 0, r4, c13, c0, 0 @ FCSE/PID
mrc p15, 0, r5, c13, c0, 1 @ Context ID
mrc p15, 0, r6, c13, c0, 3 @ User r/o thread ID
- stmia r0!, {r4 - r6}
+ stmia r0!, {r3 - r6}
mrc p15, 0, r6, c3, c0, 0 @ Domain ID
mrc p15, 0, r7, c2, c0, 0 @ TTB 0
mrc p15, 0, r8, c2, c0, 1 @ TTB 1
mrc p15, 0, r9, c1, c0, 0 @ Control register
mrc p15, 0, r10, c1, c0, 1 @ Auxiliary control register
mrc p15, 0, r11, c1, c0, 2 @ Co-processor access control
- stmia r0, {r6 - r11}
- ldmfd sp!, {r4 - r11, pc}
+ stmia r0!, {r6 - r11}
+
+ /* Save CP14 debug controller context */
+ mrc p14, 0, r4, c0, c1, 0 @ DSCR
+ mrc p14, 0, r5, c0, c6, 0 @ WFAR
+ mrc p14, 0, r6, c0, c7, 0 @ VCR
+ mrc p14, 0, r7, c7, c9, 6 @ CLAIM
+ stmia r0!, {r4-r7}
+
+ mrc p14, 0, r8, c0, c0, 0 @ read IDR
+ mov r3, r8, lsr #24
+ and r3, r3, #0xf @ r3 has the number of brkpt
+ rsb r3, r3, #0xf
+
+ /* r3 = (15 - #of brkpt) ;
+ switch offset = r3*12 - 4 = (r3*3 - 1)<<2
+ */
+ add r3, r3, r3, lsl #1
+ sub r3, r3, #1
+ add pc, pc, r3, lsl #2
+
+ save_brkpt c15
+ save_brkpt c14
+ save_brkpt c13
+ save_brkpt c12
+ save_brkpt c11
+ save_brkpt c10
+ save_brkpt c9
+ save_brkpt c8
+ save_brkpt c7
+ save_brkpt c6
+ save_brkpt c5
+ save_brkpt c4
+ save_brkpt c3
+ save_brkpt c2
+ save_brkpt c1
+ save_brkpt c0
+
+ mov r3, r8, lsr #28 @ r3 has the number of wpt
+ rsb r3, r3, #0xf
+
+ /* r3 = (15 - #of wpt) ;
+ switch offset = r3*12 - 4 = (r3*3 - 1)<<2
+ */
+ add r3, r3, r3, lsl #1
+ sub r3, r3, #1
+ add pc, pc, r3, lsl #2
+
+ save_wpt c15
+ save_wpt c14
+ save_wpt c13
+ save_wpt c12
+ save_wpt c11
+ save_wpt c10
+ save_wpt c9
+ save_wpt c8
+ save_wpt c7
+ save_wpt c6
+ save_wpt c5
+ save_wpt c4
+ save_wpt c3
+ save_wpt c2
+ save_wpt c1
+ save_wpt c0
+
+ ldmfd sp!, {r0, r3 - r11, pc}
ENDPROC(cpu_v7_do_suspend)
ENTRY(cpu_v7_do_resume)
mov ip, #0
mcr p15, 0, ip, c8, c7, 0 @ invalidate TLBs
mcr p15, 0, ip, c7, c5, 0 @ invalidate I cache
- ldmia r0!, {r4 - r6}
+ ldmia r0!, {r3 - r6}
+#ifndef CONFIG_TRUSTED_FOUNDATIONS
+ mcr p15, 0, r3, c15, c0, 1 @ diag
+#endif
mcr p15, 0, r4, c13, c0, 0 @ FCSE/PID
mcr p15, 0, r5, c13, c0, 1 @ Context ID
mcr p15, 0, r6, c13, c0, 3 @ User r/o thread ID
- ldmia r0, {r6 - r11}
+ ldmia r0!, {r6 - r11}
mcr p15, 0, r6, c3, c0, 0 @ Domain ID
mcr p15, 0, r7, c2, c0, 0 @ TTB 0
mcr p15, 0, r8, c2, c0, 1 @ TTB 1
@@ -257,6 +357,74 @@ ENTRY(cpu_v7_do_resume)
mcr p15, 0, r4, c10, c2, 0 @ write PRRR
mcr p15, 0, r5, c10, c2, 1 @ write NMRR
isb
+
+ /* Restore CP14 debug controller context */
+
+ ldmia r0!, {r2 - r5}
+ mcr p14, 0, r3, c0, c6, 0 @ WFAR
+ mcr p14, 0, r4, c0, c7, 0 @ VCR
+ mcr p14, 0, r5, c7, c8, 6 @ CLAIM
+
+ mrc p14, 0, r8, c0, c0, 0 @ read IDR
+ mov r3, r8, lsr #24
+ and r3, r3, #0xf @ r3 has the number of brkpt
+ rsb r3, r3, #0xf
+
+ /* r3 = (15 - #of wpt) ;
+ switch offset = r3*12 - 4 = (r3*3 - 1)<<2
+ */
+ add r3, r3, r3, lsl #1
+ sub r3, r3, #1
+ add pc, pc, r3, lsl #2
+
+ restore_brkpt c15
+ restore_brkpt c14
+ restore_brkpt c13
+ restore_brkpt c12
+ restore_brkpt c11
+ restore_brkpt c10
+ restore_brkpt c9
+ restore_brkpt c8
+ restore_brkpt c7
+ restore_brkpt c6
+ restore_brkpt c5
+ restore_brkpt c4
+ restore_brkpt c3
+ restore_brkpt c2
+ restore_brkpt c1
+ restore_brkpt c0
+
+ mov r3, r8, lsr #28 @ r3 has the number of wpt
+ rsb r3, r3, #0xf
+
+ /* r3 = (15 - #of wpt) ;
+ switch offset = r3*12 - 4 = (r3*3 - 1)<<2
+ */
+ add r3, r3, r3, lsl #1
+ sub r3, r3, #1
+ add pc, pc, r3, lsl #2
+
+start_restore_wpt:
+ restore_wpt c15
+ restore_wpt c14
+ restore_wpt c13
+ restore_wpt c12
+ restore_wpt c11
+ restore_wpt c10
+ restore_wpt c9
+ restore_wpt c8
+ restore_wpt c7
+ restore_wpt c6
+ restore_wpt c5
+ restore_wpt c4
+ restore_wpt c3
+ restore_wpt c2
+ restore_wpt c1
+ restore_wpt c0
+ isb
+
+ mcr p14, 0, r2, c0, c2, 2 @ DSCR
+ isb
dsb
mov r0, r9 @ control register
mov r2, r7, lsr #14 @ get TTB0 base
@@ -346,6 +514,17 @@ __v7_setup:
2: ldr r10, =0x00000c09 @ Cortex-A9 primary part number
teq r0, r10
bne 3f
+#ifndef CONFIG_TRUSTED_FOUNDATIONS
+ cmp r6, #0x10 @ power ctrl reg added r1p0
+ mrcge p15, 0, r10, c15, c0, 0 @ read power control register
+ orrge r10, r10, #1 @ enable dynamic clock gating
+ mcrge p15, 0, r10, c15, c0, 0 @ write power control register
+#ifdef CONFIG_ARM_ERRATA_720791
+ teq r5, #0x00100000 @ only present in r1p*
+ mrceq p15, 0, r10, c15, c0, 2 @ read "chicken power ctrl" reg
+ orreq r10, r10, #0x30 @ disable core clk gate on
+ mcreq p15, 0, r10, c15, c0, 2 @ instr-side waits
+#endif
#ifdef CONFIG_ARM_ERRATA_742230
cmp r6, #0x22 @ only present up to r2p2
mrcle p15, 0, r10, c15, c0, 1 @ read diagnostic register
@@ -365,6 +544,8 @@ __v7_setup:
teq r6, #0x20 @ present in r2p0
teqne r6, #0x21 @ present in r2p1
teqne r6, #0x22 @ present in r2p2
+ teqne r6, #0x27 @ present in r2p7
+ teqne r6, #0x29 @ present in r2p9
mrceq p15, 0, r10, c15, c0, 1 @ read diagnostic register
orreq r10, r10, #1 << 6 @ set bit #6
mcreq p15, 0, r10, c15, c0, 1 @ write diagnostic register
@@ -375,6 +556,13 @@ __v7_setup:
orrlt r10, r10, #1 << 11 @ set bit #11
mcrlt p15, 0, r10, c15, c0, 1 @ write diagnostic register
#endif
+#ifdef CONFIG_ARM_ERRATA_752520
+ cmp r6, #0x29 @ present prior to r2p9
+ mrclt p15, 0, r10, c15, c0, 1 @ read diagnostic register
+ orrlt r10, r10, #1 << 20 @ set bit #20
+ mcrlt p15, 0, r10, c15, c0, 1 @ write diagnostic register
+#endif
+#endif
3: mov r10, #0
#ifdef HARVARD_CACHE
diff --git a/arch/arm/mm/proc-xsc3.S b/arch/arm/mm/proc-xsc3.S
index 755e1bf22681..1a2021cedc76 100644
--- a/arch/arm/mm/proc-xsc3.S
+++ b/arch/arm/mm/proc-xsc3.S
@@ -375,7 +375,7 @@ cpu_xsc3_mt_table:
.long PTE_EXT_TEX(5) | PTE_CACHEABLE @ L_PTE_MT_WRITETHROUGH
.long PTE_CACHEABLE | PTE_BUFFERABLE @ L_PTE_MT_WRITEBACK
.long PTE_EXT_TEX(1) | PTE_BUFFERABLE @ L_PTE_MT_DEV_SHARED
- .long 0x00 @ unused
+ .long PTE_EXT_TEX(4) | PTE_BUFFERABLE @ L_PTE_MT_INNER_WB (not present?)
.long 0x00 @ L_PTE_MT_MINICACHE (not present)
.long PTE_EXT_TEX(5) | PTE_CACHEABLE | PTE_BUFFERABLE @ L_PTE_MT_WRITEALLOC (not present?)
.long 0x00 @ unused
diff --git a/arch/arm/mm/proc-xscale.S b/arch/arm/mm/proc-xscale.S
index fbc06e55b87a..b0fe4b1e233d 100644
--- a/arch/arm/mm/proc-xscale.S
+++ b/arch/arm/mm/proc-xscale.S
@@ -484,7 +484,7 @@ cpu_xscale_mt_table:
.long PTE_CACHEABLE @ L_PTE_MT_WRITETHROUGH
.long PTE_CACHEABLE | PTE_BUFFERABLE @ L_PTE_MT_WRITEBACK
.long PTE_EXT_TEX(1) | PTE_BUFFERABLE @ L_PTE_MT_DEV_SHARED
- .long 0x00 @ unused
+ .long PTE_EXT_TEX(1) | PTE_CACHEABLE | PTE_BUFFERABLE @ L_PTE_MT_INNER_WB
.long PTE_EXT_TEX(1) | PTE_CACHEABLE @ L_PTE_MT_MINICACHE
.long PTE_EXT_TEX(1) | PTE_CACHEABLE | PTE_BUFFERABLE @ L_PTE_MT_WRITEALLOC
.long 0x00 @ unused
diff --git a/arch/arm/plat-samsung/pm.c b/arch/arm/plat-samsung/pm.c
index ae6f99834cdd..138e24774f70 100644
--- a/arch/arm/plat-samsung/pm.c
+++ b/arch/arm/plat-samsung/pm.c
@@ -302,6 +302,10 @@ static int s3c_pm_enter(suspend_state_t state)
cpu_suspend(0, pm_cpu_sleep);
+ /* restore the cpu state using the kernel's cpu init code. */
+
+ cpu_init();
+
/* restore the system state */
s3c_pm_restore_core();
diff --git a/arch/arm/tools/mach-types b/arch/arm/tools/mach-types
index 62cc8f981171..fe0b5e140472 100644
--- a/arch/arm/tools/mach-types
+++ b/arch/arm/tools/mach-types
@@ -1113,3 +1113,5 @@ blissc MACH_BLISSC BLISSC 3491
thales_adc MACH_THALES_ADC THALES_ADC 3492
ubisys_p9d_evp MACH_UBISYS_P9D_EVP UBISYS_P9D_EVP 3493
atdgp318 MACH_ATDGP318 ATDGP318 3494
+tegra_enterprise MACH_TEGRA_ENTERPRISE TEGRA_ENTERPRISE 3512
+p852 MACH_P852 P852 3667
diff --git a/arch/arm/vfp/entry.S b/arch/arm/vfp/entry.S
index 4fa9903b83cf..c1a978402583 100644
--- a/arch/arm/vfp/entry.S
+++ b/arch/arm/vfp/entry.S
@@ -10,7 +10,7 @@
*
* Basic entry code, called from the kernel's undefined instruction trap.
* r0 = faulted instruction
- * r5 = faulted PC+4
+ * r2 = faulted PC+4
* r9 = successful return
* r10 = thread_info structure
* lr = failure return
@@ -26,6 +26,7 @@ ENTRY(do_vfp)
str r11, [r10, #TI_PREEMPT]
#endif
enable_irq
+ str r2, [sp, #S_PC] @ update regs->ARM_pc for Thumb 2 case
ldr r4, .LCvfp
ldr r11, [r10, #TI_CPU] @ CPU number
add r10, r10, #TI_VFPSTATE @ r10 = workspace
diff --git a/arch/arm/vfp/vfphw.S b/arch/arm/vfp/vfphw.S
index 2d30c7f6edd3..404538ae591d 100644
--- a/arch/arm/vfp/vfphw.S
+++ b/arch/arm/vfp/vfphw.S
@@ -82,22 +82,19 @@ ENTRY(vfp_support_entry)
ldr r4, [r3, r11, lsl #2] @ vfp_current_hw_state pointer
bic r5, r1, #FPEXC_EX @ make sure exceptions are disabled
cmp r4, r10 @ this thread owns the hw context?
-#ifndef CONFIG_SMP
- @ For UP, checking that this thread owns the hw context is
- @ sufficient to determine that the hardware state is valid.
beq vfp_hw_state_valid
- @ On UP, we lazily save the VFP context. As a different
- @ thread wants ownership of the VFP hardware, save the old
- @ state if there was a previous (valid) owner.
-
VFPFMXR FPEXC, r5 @ enable VFP, disable any pending
@ exceptions, so we can get at the
@ rest of it
+#ifndef CONFIG_SMP
+ @ Save out the current registers to the old thread state
+ @ No need for SMP since this is not done lazily
+
DBGSTR1 "save old state %p", r4
- cmp r4, #0 @ if the vfp_current_hw_state is NULL
- beq vfp_reload_hw @ then the hw state needs reloading
+ cmp r4, #0
+ beq no_old_VFP_process
VFPFSTMIA r4, r5 @ save the working registers
VFPFMRX r5, FPSCR @ current status
#ifndef CONFIG_CPU_FEROCEON
@@ -110,33 +107,11 @@ ENTRY(vfp_support_entry)
1:
#endif
stmia r4, {r1, r5, r6, r8} @ save FPEXC, FPSCR, FPINST, FPINST2
-vfp_reload_hw:
-
-#else
- @ For SMP, if this thread does not own the hw context, then we
- @ need to reload it. No need to save the old state as on SMP,
- @ we always save the state when we switch away from a thread.
- bne vfp_reload_hw
-
- @ This thread has ownership of the current hardware context.
- @ However, it may have been migrated to another CPU, in which
- @ case the saved state is newer than the hardware context.
- @ Check this by looking at the CPU number which the state was
- @ last loaded onto.
- ldr ip, [r10, #VFP_CPU]
- teq ip, r11
- beq vfp_hw_state_valid
-
-vfp_reload_hw:
- @ We're loading this threads state into the VFP hardware. Update
- @ the CPU number which contains the most up to date VFP context.
- str r11, [r10, #VFP_CPU]
-
- VFPFMXR FPEXC, r5 @ enable VFP, disable any pending
- @ exceptions, so we can get at the
- @ rest of it
+ @ and point r4 at the word at the
+ @ start of the register dump
#endif
+no_old_VFP_process:
DBGSTR1 "load state %p", r10
str r10, [r3, r11, lsl #2] @ update the vfp_current_hw_state pointer
@ Load the saved state back into the VFP
diff --git a/arch/arm/vfp/vfpmodule.c b/arch/arm/vfp/vfpmodule.c
index 79bcb4316930..e381dc68505d 100644
--- a/arch/arm/vfp/vfpmodule.c
+++ b/arch/arm/vfp/vfpmodule.c
@@ -21,6 +21,7 @@
#include <asm/cputype.h>
#include <asm/thread_notify.h>
#include <asm/vfp.h>
+#include <asm/cpu_pm.h>
#include "vfpinstr.h"
#include "vfp.h"
@@ -35,51 +36,18 @@ void vfp_null_entry(void);
void (*vfp_vector)(void) = vfp_null_entry;
/*
- * Dual-use variable.
- * Used in startup: set to non-zero if VFP checks fail
- * After startup, holds VFP architecture
- */
-unsigned int VFP_arch;
-
-/*
* The pointer to the vfpstate structure of the thread which currently
* owns the context held in the VFP hardware, or NULL if the hardware
* context is invalid.
- *
- * For UP, this is sufficient to tell which thread owns the VFP context.
- * However, for SMP, we also need to check the CPU number stored in the
- * saved state too to catch migrations.
*/
union vfp_state *vfp_current_hw_state[NR_CPUS];
/*
- * Is 'thread's most up to date state stored in this CPUs hardware?
- * Must be called from non-preemptible context.
- */
-static bool vfp_state_in_hw(unsigned int cpu, struct thread_info *thread)
-{
-#ifdef CONFIG_SMP
- if (thread->vfpstate.hard.cpu != cpu)
- return false;
-#endif
- return vfp_current_hw_state[cpu] == &thread->vfpstate;
-}
-
-/*
- * Force a reload of the VFP context from the thread structure. We do
- * this by ensuring that access to the VFP hardware is disabled, and
- * clear last_VFP_context. Must be called from non-preemptible context.
+ * Dual-use variable.
+ * Used in startup: set to non-zero if VFP checks fail
+ * After startup, holds VFP architecture
*/
-static void vfp_force_reload(unsigned int cpu, struct thread_info *thread)
-{
- if (vfp_state_in_hw(cpu, thread)) {
- fmxr(FPEXC, fmrx(FPEXC) & ~FPEXC_EN);
- vfp_current_hw_state[cpu] = NULL;
- }
-#ifdef CONFIG_SMP
- thread->vfpstate.hard.cpu = NR_CPUS;
-#endif
-}
+unsigned int VFP_arch;
/*
* Per-thread VFP initialization.
@@ -89,27 +57,21 @@ static void vfp_thread_flush(struct thread_info *thread)
union vfp_state *vfp = &thread->vfpstate;
unsigned int cpu;
+ memset(vfp, 0, sizeof(union vfp_state));
+
+ vfp->hard.fpexc = FPEXC_EN;
+ vfp->hard.fpscr = FPSCR_ROUND_NEAREST;
+
/*
* Disable VFP to ensure we initialize it first. We must ensure
- * that the modification of vfp_current_hw_state[] and hardware
- * disable are done for the same CPU and without preemption.
- *
- * Do this first to ensure that preemption won't overwrite our
- * state saving should access to the VFP be enabled at this point.
+ * that the modification of vfp_current_hw_state[] and hardware disable
+ * are done for the same CPU and without preemption.
*/
cpu = get_cpu();
if (vfp_current_hw_state[cpu] == vfp)
vfp_current_hw_state[cpu] = NULL;
fmxr(FPEXC, fmrx(FPEXC) & ~FPEXC_EN);
put_cpu();
-
- memset(vfp, 0, sizeof(union vfp_state));
-
- vfp->hard.fpexc = FPEXC_EN;
- vfp->hard.fpscr = FPSCR_ROUND_NEAREST;
-#ifdef CONFIG_SMP
- vfp->hard.cpu = NR_CPUS;
-#endif
}
static void vfp_thread_exit(struct thread_info *thread)
@@ -129,9 +91,6 @@ static void vfp_thread_copy(struct thread_info *thread)
vfp_sync_hwstate(parent);
thread->vfpstate = parent->vfpstate;
-#ifdef CONFIG_SMP
- thread->vfpstate.hard.cpu = NR_CPUS;
-#endif
}
/*
@@ -177,8 +136,17 @@ static int vfp_notifier(struct notifier_block *self, unsigned long cmd, void *v)
* case the thread migrates to a different CPU. The
* restoring is done lazily.
*/
- if ((fpexc & FPEXC_EN) && vfp_current_hw_state[cpu])
+ if ((fpexc & FPEXC_EN) && vfp_current_hw_state[cpu]) {
vfp_save_state(vfp_current_hw_state[cpu], fpexc);
+ vfp_current_hw_state[cpu]->hard.cpu = cpu;
+ }
+ /*
+ * Thread migration, just force the reloading of the
+ * state on the new CPU in case the VFP registers
+ * contain stale data.
+ */
+ if (thread->vfpstate.hard.cpu != cpu)
+ vfp_current_hw_state[cpu] = NULL;
#endif
/*
@@ -208,6 +176,35 @@ static struct notifier_block vfp_notifier_block = {
.notifier_call = vfp_notifier,
};
+static int vfp_cpu_pm_notifier(struct notifier_block *self, unsigned long cmd,
+ void *v)
+{
+ u32 fpexc = fmrx(FPEXC);
+ unsigned int cpu = smp_processor_id();
+
+ switch (cmd) {
+ case CPU_PM_ENTER:
+ if (vfp_current_hw_state[cpu]) {
+ fmxr(FPEXC, fpexc | FPEXC_EN);
+ vfp_save_state(vfp_current_hw_state[cpu], fpexc);
+ /* force a reload when coming back from idle */
+ vfp_current_hw_state[cpu] = NULL;
+ fmxr(FPEXC, fpexc & ~FPEXC_EN);
+ }
+ break;
+ case CPU_PM_ENTER_FAILED:
+ case CPU_PM_EXIT:
+ /* make sure VFP is disabled when leaving idle */
+ fmxr(FPEXC, fpexc & ~FPEXC_EN);
+ break;
+ }
+ return NOTIFY_OK;
+}
+
+static struct notifier_block vfp_cpu_pm_notifier_block = {
+ .notifier_call = vfp_cpu_pm_notifier,
+};
+
/*
* Raise a SIGFPE for the current process.
* sicode describes the signal being raised.
@@ -444,6 +441,12 @@ static int vfp_pm_suspend(void)
struct thread_info *ti = current_thread_info();
u32 fpexc = fmrx(FPEXC);
+ /* If lazy disable, re-enable the VFP ready for it to be saved */
+ if (vfp_current_hw_state[ti->cpu] != &ti->vfpstate) {
+ fpexc |= FPEXC_EN;
+ fmxr(FPEXC, fpexc);
+ }
+
/* if vfp is on, then save state for resumption */
if (fpexc & FPEXC_EN) {
printk(KERN_DEBUG "%s: saving vfp state\n", __func__);
@@ -451,6 +454,10 @@ static int vfp_pm_suspend(void)
/* disable, just in case */
fmxr(FPEXC, fmrx(FPEXC) & ~FPEXC_EN);
+ } else if (vfp_current_hw_state[ti->cpu]) {
+ fmxr(FPEXC, fpexc | FPEXC_EN);
+ vfp_save_state(vfp_current_hw_state[ti->cpu], fpexc);
+ fmxr(FPEXC, fpexc);
}
/* clear any information we had about last context state */
@@ -482,15 +489,15 @@ static void vfp_pm_init(void)
static inline void vfp_pm_init(void) { }
#endif /* CONFIG_PM */
-/*
- * Ensure that the VFP state stored in 'thread->vfpstate' is up to date
- * with the hardware state.
- */
void vfp_sync_hwstate(struct thread_info *thread)
{
unsigned int cpu = get_cpu();
- if (vfp_state_in_hw(cpu, thread)) {
+ /*
+ * If the thread we're interested in is the current owner of the
+ * hardware VFP state, then we need to save its state.
+ */
+ if (vfp_current_hw_state[cpu] == &thread->vfpstate) {
u32 fpexc = fmrx(FPEXC);
/*
@@ -504,13 +511,36 @@ void vfp_sync_hwstate(struct thread_info *thread)
put_cpu();
}
-/* Ensure that the thread reloads the hardware VFP state on the next use. */
void vfp_flush_hwstate(struct thread_info *thread)
{
unsigned int cpu = get_cpu();
- vfp_force_reload(cpu, thread);
+ /*
+ * If the thread we're interested in is the current owner of the
+ * hardware VFP state, then we need to save its state.
+ */
+ if (vfp_current_hw_state[cpu] == &thread->vfpstate) {
+ u32 fpexc = fmrx(FPEXC);
+ fmxr(FPEXC, fpexc & ~FPEXC_EN);
+
+ /*
+ * Set the context to NULL to force a reload the next time
+ * the thread uses the VFP.
+ */
+ vfp_current_hw_state[cpu] = NULL;
+ }
+
+#ifdef CONFIG_SMP
+ /*
+ * For SMP we still have to take care of the case where the thread
+ * migrates to another CPU and then back to the original CPU on which
+ * the last VFP user is still the same thread. Mark the thread VFP
+ * state as belonging to a non-existent CPU so that the saved one will
+ * be reloaded in the above case.
+ */
+ thread->vfpstate.hard.cpu = NR_CPUS;
+#endif
put_cpu();
}
@@ -529,7 +559,8 @@ static int vfp_hotplug(struct notifier_block *b, unsigned long action,
void *hcpu)
{
if (action == CPU_DYING || action == CPU_DYING_FROZEN) {
- vfp_force_reload((long)hcpu, current_thread_info());
+ unsigned int cpu = (long)hcpu;
+ vfp_current_hw_state[cpu] = NULL;
} else if (action == CPU_STARTING || action == CPU_STARTING_FROZEN)
vfp_enable(NULL);
return NOTIFY_OK;
@@ -578,6 +609,7 @@ static int __init vfp_init(void)
vfp_vector = vfp_support_entry;
thread_register_notifier(&vfp_notifier_block);
+ cpu_pm_register_notifier(&vfp_cpu_pm_notifier_block);
vfp_pm_init();
/*