aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/nouveau/nvkm/subdev
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/nouveau/nvkm/subdev')
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/Kbuild19
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/bar/Kbuild4
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/bar/base.c144
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/bar/gf100.c219
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/bar/gk20a.c50
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/bar/nv50.c271
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/bar/priv.h30
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/bios/Kbuild37
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/bios/M0203.c128
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/bios/M0205.c135
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/bios/M0209.c135
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/bios/P0260.c107
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/bios/base.c206
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/bios/bit.c49
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/bios/boost.c126
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/bios/conn.c97
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/bios/cstep.c122
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/bios/dcb.c234
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/bios/disp.c172
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/bios/dp.c215
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/bios/extdev.c97
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/bios/fan.c93
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/bios/gpio.c150
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/bios/i2c.c159
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/bios/image.c77
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/bios/init.c2247
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/bios/mxm.c134
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/bios/npde.c58
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/bios/pcir.c68
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/bios/perf.c201
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/bios/pll.c417
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/bios/pmu.c134
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/bios/priv.h23
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/bios/ramcfg.c78
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/bios/rammap.c211
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadow.c272
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowacpi.c112
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowof.c72
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowpci.c109
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowramin.c115
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowrom.c70
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/bios/therm.c214
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/bios/timing.c166
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/bios/vmap.c111
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/bios/volt.c136
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/bios/xpio.c74
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/bus/Kbuild6
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/bus/g94.c58
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/bus/gf100.c80
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/bus/hwsq.c143
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/bus/hwsq.h111
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/bus/nv04.c94
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/bus/nv04.h21
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/bus/nv31.c91
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/bus/nv50.c104
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/clk/Kbuild12
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/clk/base.c591
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/clk/g84.c47
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/clk/gf100.c462
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk104.c500
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk20a.c680
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/clk/gt215.c533
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/clk/gt215.h18
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/clk/mcp77.c429
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/clk/nv04.c103
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/clk/nv40.c241
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/clk/nv50.c561
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/clk/nv50.h28
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/clk/pll.h11
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/clk/pllgt215.c87
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/clk/pllnv04.c245
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/clk/seq.h14
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/devinit/Kbuild14
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/devinit/base.c96
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/devinit/fbmem.h84
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/devinit/g84.c66
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/devinit/g98.c65
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gf100.c124
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gm107.c59
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gm204.c172
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gt215.c150
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/devinit/mcp89.c66
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv04.c470
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv04.h22
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv05.c140
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv10.c111
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv1a.c40
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv20.c77
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv50.c174
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv50.h21
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/devinit/priv.h34
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/Kbuild45
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/base.c155
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/g84.c38
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/gddr3.c115
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/gddr5.c120
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/gf100.c122
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/gf100.h28
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/gk104.c37
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/gk20a.c69
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/gm107.c37
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/gt215.c38
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/mcp77.c38
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/mcp89.c38
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv04.c87
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv04.h53
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv10.c70
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv1a.c43
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv20.c94
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv25.c60
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv30.c140
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv35.c61
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv36.c61
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv40.c75
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv40.h14
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv41.c68
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv44.c77
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv46.c57
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv47.c44
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv49.c44
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv4e.c43
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv50.c320
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv50.h31
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/priv.h74
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramfuc.h180
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgf100.c731
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgk104.c1639
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgk20a.c149
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgm107.c55
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgt215.c1012
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/rammcp77.c101
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv04.c79
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv10.c59
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv1a.c72
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv20.c62
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv40.c212
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv41.c66
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv44.c64
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv49.c66
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv4e.c54
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv50.c465
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramseq.h15
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/regsnv04.h22
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/sddr2.c93
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/sddr3.c119
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fuse/Kbuild4
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fuse/base.c51
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fuse/gf100.c78
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fuse/gm107.c64
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fuse/nv50.c76
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fuse/priv.h7
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gpio/Kbuild6
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gpio/base.c251
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gpio/g94.c73
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gpio/gf110.c84
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gpio/gk104.c73
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gpio/nv10.c115
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gpio/nv50.c128
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gpio/priv.h64
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/i2c/Kbuild16
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/i2c/anx9805.c292
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/i2c/aux.c113
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/i2c/base.c622
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/i2c/bit.c233
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/i2c/g94.c279
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gf110.c106
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gf117.c38
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gk104.c71
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gm204.c219
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/i2c/nv04.c128
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/i2c/nv4e.c120
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/i2c/nv50.c133
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/i2c/nv50.h32
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/i2c/pad.c83
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/i2c/pad.h56
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padg94.c85
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padgm204.c85
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padnv04.c34
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/i2c/port.h13
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/i2c/priv.h87
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/ibus/Kbuild3
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/ibus/gf100.c122
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/ibus/gk104.c139
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/ibus/gk20a.c102
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/instmem/Kbuild4
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/instmem/base.c146
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv04.c185
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv04.h36
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv40.c136
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv50.c169
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/instmem/priv.h54
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/ltc/Kbuild4
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/ltc/base.c124
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/ltc/gf100.c236
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/ltc/gk104.c59
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/ltc/gm107.c153
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/ltc/priv.h69
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/mc/Kbuild11
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/mc/base.c169
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/mc/g94.c37
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/mc/g98.c58
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/mc/gf100.c76
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/mc/gf106.c38
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/mc/gk20a.c37
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv04.c78
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv04.h20
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv40.c44
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv44.c53
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv4c.c36
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv50.c72
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/mc/priv.h36
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/mmu/Kbuild6
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/mmu/base.c480
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/mmu/gf100.c237
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/mmu/nv04.c151
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/mmu/nv04.h19
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/mmu/nv41.c157
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/mmu/nv44.c247
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/mmu/nv50.c241
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/mxm/Kbuild3
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/mxm/base.c271
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/mxm/mxms.c191
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/mxm/mxms.h22
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/mxm/nv50.c231
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/pmu/Kbuild8
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/pmu/base.c268
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/arith.fuc94
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gf100.fuc370
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gf100.fuc3.h1865
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gf110.fuc470
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gf110.fuc4.h1795
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gk208.fuc570
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gk208.fuc5.h1731
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gt215.fuc370
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gt215.fuc3.h1868
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/host.fuc151
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/i2c_.fuc393
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/idle.fuc84
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/kernel.fuc556
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/macros.fuc272
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/memx.fuc447
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/os.h52
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/perf.fuc57
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/test.fuc64
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gf100.c40
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gf110.c40
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gk104.c67
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gk208.c40
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gk20a.c229
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gt215.c49
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/pmu/memx.c200
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/pmu/priv.h43
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/therm/Kbuild13
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/therm/base.c367
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/therm/fan.c282
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/therm/fannil.c53
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/therm/fanpwm.c113
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/therm/fantog.c118
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/therm/g84.c266
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/therm/gf110.c174
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/therm/gm107.c93
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/therm/gt215.c100
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/therm/ic.c119
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/therm/nv40.c225
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/therm/nv50.c198
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/therm/priv.h153
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/therm/temp.c259
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/timer/Kbuild3
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/timer/base.c93
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/timer/gk20a.c56
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/timer/nv04.c262
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/timer/nv04.h25
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/timer/priv.h4
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/volt/Kbuild4
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/volt/base.c204
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/volt/gk20a.c197
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/volt/gpio.c96
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/volt/nv40.c55
278 files changed, 45970 insertions, 0 deletions
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/Kbuild b/drivers/gpu/drm/nouveau/nvkm/subdev/Kbuild
new file mode 100644
index 000000000000..a1bb3e48739c
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/Kbuild
@@ -0,0 +1,19 @@
+include $(src)/nvkm/subdev/bar/Kbuild
+include $(src)/nvkm/subdev/bios/Kbuild
+include $(src)/nvkm/subdev/bus/Kbuild
+include $(src)/nvkm/subdev/clk/Kbuild
+include $(src)/nvkm/subdev/devinit/Kbuild
+include $(src)/nvkm/subdev/fb/Kbuild
+include $(src)/nvkm/subdev/fuse/Kbuild
+include $(src)/nvkm/subdev/gpio/Kbuild
+include $(src)/nvkm/subdev/i2c/Kbuild
+include $(src)/nvkm/subdev/ibus/Kbuild
+include $(src)/nvkm/subdev/instmem/Kbuild
+include $(src)/nvkm/subdev/ltc/Kbuild
+include $(src)/nvkm/subdev/mc/Kbuild
+include $(src)/nvkm/subdev/mmu/Kbuild
+include $(src)/nvkm/subdev/mxm/Kbuild
+include $(src)/nvkm/subdev/pmu/Kbuild
+include $(src)/nvkm/subdev/therm/Kbuild
+include $(src)/nvkm/subdev/timer/Kbuild
+include $(src)/nvkm/subdev/volt/Kbuild
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bar/Kbuild b/drivers/gpu/drm/nouveau/nvkm/subdev/bar/Kbuild
new file mode 100644
index 000000000000..1ab554a0b5e0
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bar/Kbuild
@@ -0,0 +1,4 @@
+nvkm-y += nvkm/subdev/bar/base.o
+nvkm-y += nvkm/subdev/bar/nv50.o
+nvkm-y += nvkm/subdev/bar/gf100.o
+nvkm-y += nvkm/subdev/bar/gk20a.o
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bar/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bar/base.c
new file mode 100644
index 000000000000..3502d00122ef
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bar/base.c
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "priv.h"
+
+#include <core/device.h>
+#include <subdev/fb.h>
+#include <subdev/mmu.h>
+
+struct nvkm_barobj {
+ struct nvkm_object base;
+ struct nvkm_vma vma;
+ void __iomem *iomem;
+};
+
+static int
+nvkm_barobj_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 size,
+ struct nvkm_object **pobject)
+{
+ struct nvkm_device *device = nv_device(parent);
+ struct nvkm_bar *bar = nvkm_bar(device);
+ struct nvkm_mem *mem = data;
+ struct nvkm_barobj *barobj;
+ int ret;
+
+ ret = nvkm_object_create(parent, engine, oclass, 0, &barobj);
+ *pobject = nv_object(barobj);
+ if (ret)
+ return ret;
+
+ ret = bar->kmap(bar, mem, NV_MEM_ACCESS_RW, &barobj->vma);
+ if (ret)
+ return ret;
+
+ barobj->iomem = ioremap(nv_device_resource_start(device, 3) +
+ (u32)barobj->vma.offset, mem->size << 12);
+ if (!barobj->iomem) {
+ nv_warn(bar, "PRAMIN ioremap failed\n");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static void
+nvkm_barobj_dtor(struct nvkm_object *object)
+{
+ struct nvkm_bar *bar = nvkm_bar(object);
+ struct nvkm_barobj *barobj = (void *)object;
+ if (barobj->vma.node) {
+ if (barobj->iomem)
+ iounmap(barobj->iomem);
+ bar->unmap(bar, &barobj->vma);
+ }
+ nvkm_object_destroy(&barobj->base);
+}
+
+static u32
+nvkm_barobj_rd32(struct nvkm_object *object, u64 addr)
+{
+ struct nvkm_barobj *barobj = (void *)object;
+ return ioread32_native(barobj->iomem + addr);
+}
+
+static void
+nvkm_barobj_wr32(struct nvkm_object *object, u64 addr, u32 data)
+{
+ struct nvkm_barobj *barobj = (void *)object;
+ iowrite32_native(data, barobj->iomem + addr);
+}
+
+static struct nvkm_oclass
+nvkm_barobj_oclass = {
+ .ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nvkm_barobj_ctor,
+ .dtor = nvkm_barobj_dtor,
+ .init = nvkm_object_init,
+ .fini = nvkm_object_fini,
+ .rd32 = nvkm_barobj_rd32,
+ .wr32 = nvkm_barobj_wr32,
+ },
+};
+
+int
+nvkm_bar_alloc(struct nvkm_bar *bar, struct nvkm_object *parent,
+ struct nvkm_mem *mem, struct nvkm_object **pobject)
+{
+ struct nvkm_object *gpuobj;
+ int ret = nvkm_object_ctor(parent, &parent->engine->subdev.object,
+ &nvkm_barobj_oclass, mem, 0, &gpuobj);
+ if (ret == 0)
+ *pobject = gpuobj;
+ return ret;
+}
+
+int
+nvkm_bar_create_(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, int length, void **pobject)
+{
+ struct nvkm_bar *bar;
+ int ret;
+
+ ret = nvkm_subdev_create_(parent, engine, oclass, 0, "BARCTL",
+ "bar", length, pobject);
+ bar = *pobject;
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+void
+nvkm_bar_destroy(struct nvkm_bar *bar)
+{
+ nvkm_subdev_destroy(&bar->base);
+}
+
+void
+_nvkm_bar_dtor(struct nvkm_object *object)
+{
+ struct nvkm_bar *bar = (void *)object;
+ nvkm_bar_destroy(bar);
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bar/gf100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bar/gf100.c
new file mode 100644
index 000000000000..12a1aebd9a96
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bar/gf100.c
@@ -0,0 +1,219 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "priv.h"
+
+#include <core/device.h>
+#include <core/gpuobj.h>
+#include <subdev/fb.h>
+#include <subdev/mmu.h>
+
+struct gf100_bar_priv_vm {
+ struct nvkm_gpuobj *mem;
+ struct nvkm_gpuobj *pgd;
+ struct nvkm_vm *vm;
+};
+
+struct gf100_bar_priv {
+ struct nvkm_bar base;
+ spinlock_t lock;
+ struct gf100_bar_priv_vm bar[2];
+};
+
+static int
+gf100_bar_kmap(struct nvkm_bar *bar, struct nvkm_mem *mem, u32 flags,
+ struct nvkm_vma *vma)
+{
+ struct gf100_bar_priv *priv = (void *)bar;
+ int ret;
+
+ ret = nvkm_vm_get(priv->bar[0].vm, mem->size << 12, 12, flags, vma);
+ if (ret)
+ return ret;
+
+ nvkm_vm_map(vma, mem);
+ return 0;
+}
+
+static int
+gf100_bar_umap(struct nvkm_bar *bar, struct nvkm_mem *mem, u32 flags,
+ struct nvkm_vma *vma)
+{
+ struct gf100_bar_priv *priv = (void *)bar;
+ int ret;
+
+ ret = nvkm_vm_get(priv->bar[1].vm, mem->size << 12,
+ mem->page_shift, flags, vma);
+ if (ret)
+ return ret;
+
+ nvkm_vm_map(vma, mem);
+ return 0;
+}
+
+static void
+gf100_bar_unmap(struct nvkm_bar *bar, struct nvkm_vma *vma)
+{
+ nvkm_vm_unmap(vma);
+ nvkm_vm_put(vma);
+}
+
+static int
+gf100_bar_ctor_vm(struct gf100_bar_priv *priv, struct gf100_bar_priv_vm *bar_vm,
+ int bar_nr)
+{
+ struct nvkm_device *device = nv_device(&priv->base);
+ struct nvkm_vm *vm;
+ resource_size_t bar_len;
+ int ret;
+
+ ret = nvkm_gpuobj_new(nv_object(priv), NULL, 0x1000, 0, 0,
+ &bar_vm->mem);
+ if (ret)
+ return ret;
+
+ ret = nvkm_gpuobj_new(nv_object(priv), NULL, 0x8000, 0, 0,
+ &bar_vm->pgd);
+ if (ret)
+ return ret;
+
+ bar_len = nv_device_resource_len(device, bar_nr);
+
+ ret = nvkm_vm_new(device, 0, bar_len, 0, &vm);
+ if (ret)
+ return ret;
+
+ atomic_inc(&vm->engref[NVDEV_SUBDEV_BAR]);
+
+ /*
+ * Bootstrap page table lookup.
+ */
+ if (bar_nr == 3) {
+ ret = nvkm_gpuobj_new(nv_object(priv), NULL,
+ (bar_len >> 12) * 8, 0x1000,
+ NVOBJ_FLAG_ZERO_ALLOC,
+ &vm->pgt[0].obj[0]);
+ vm->pgt[0].refcount[0] = 1;
+ if (ret)
+ return ret;
+ }
+
+ ret = nvkm_vm_ref(vm, &bar_vm->vm, bar_vm->pgd);
+ nvkm_vm_ref(NULL, &vm, NULL);
+ if (ret)
+ return ret;
+
+ nv_wo32(bar_vm->mem, 0x0200, lower_32_bits(bar_vm->pgd->addr));
+ nv_wo32(bar_vm->mem, 0x0204, upper_32_bits(bar_vm->pgd->addr));
+ nv_wo32(bar_vm->mem, 0x0208, lower_32_bits(bar_len - 1));
+ nv_wo32(bar_vm->mem, 0x020c, upper_32_bits(bar_len - 1));
+ return 0;
+}
+
+int
+gf100_bar_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 size,
+ struct nvkm_object **pobject)
+{
+ struct nvkm_device *device = nv_device(parent);
+ struct gf100_bar_priv *priv;
+ bool has_bar3 = nv_device_resource_len(device, 3) != 0;
+ int ret;
+
+ ret = nvkm_bar_create(parent, engine, oclass, &priv);
+ *pobject = nv_object(priv);
+ if (ret)
+ return ret;
+
+ /* BAR3 */
+ if (has_bar3) {
+ ret = gf100_bar_ctor_vm(priv, &priv->bar[0], 3);
+ if (ret)
+ return ret;
+ }
+
+ /* BAR1 */
+ ret = gf100_bar_ctor_vm(priv, &priv->bar[1], 1);
+ if (ret)
+ return ret;
+
+ if (has_bar3) {
+ priv->base.alloc = nvkm_bar_alloc;
+ priv->base.kmap = gf100_bar_kmap;
+ }
+ priv->base.umap = gf100_bar_umap;
+ priv->base.unmap = gf100_bar_unmap;
+ priv->base.flush = g84_bar_flush;
+ spin_lock_init(&priv->lock);
+ return 0;
+}
+
+void
+gf100_bar_dtor(struct nvkm_object *object)
+{
+ struct gf100_bar_priv *priv = (void *)object;
+
+ nvkm_vm_ref(NULL, &priv->bar[1].vm, priv->bar[1].pgd);
+ nvkm_gpuobj_ref(NULL, &priv->bar[1].pgd);
+ nvkm_gpuobj_ref(NULL, &priv->bar[1].mem);
+
+ if (priv->bar[0].vm) {
+ nvkm_gpuobj_ref(NULL, &priv->bar[0].vm->pgt[0].obj[0]);
+ nvkm_vm_ref(NULL, &priv->bar[0].vm, priv->bar[0].pgd);
+ }
+ nvkm_gpuobj_ref(NULL, &priv->bar[0].pgd);
+ nvkm_gpuobj_ref(NULL, &priv->bar[0].mem);
+
+ nvkm_bar_destroy(&priv->base);
+}
+
+int
+gf100_bar_init(struct nvkm_object *object)
+{
+ struct gf100_bar_priv *priv = (void *)object;
+ int ret;
+
+ ret = nvkm_bar_init(&priv->base);
+ if (ret)
+ return ret;
+
+ nv_mask(priv, 0x000200, 0x00000100, 0x00000000);
+ nv_mask(priv, 0x000200, 0x00000100, 0x00000100);
+
+ nv_wr32(priv, 0x001704, 0x80000000 | priv->bar[1].mem->addr >> 12);
+ if (priv->bar[0].mem)
+ nv_wr32(priv, 0x001714,
+ 0xc0000000 | priv->bar[0].mem->addr >> 12);
+ return 0;
+}
+
+struct nvkm_oclass
+gf100_bar_oclass = {
+ .handle = NV_SUBDEV(BAR, 0xc0),
+ .ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = gf100_bar_ctor,
+ .dtor = gf100_bar_dtor,
+ .init = gf100_bar_init,
+ .fini = _nvkm_bar_fini,
+ },
+};
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bar/gk20a.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bar/gk20a.c
new file mode 100644
index 000000000000..148f739a276e
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bar/gk20a.c
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+#include "priv.h"
+
+int
+gk20a_bar_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 size,
+ struct nvkm_object **pobject)
+{
+ struct nvkm_bar *bar;
+ int ret;
+
+ ret = gf100_bar_ctor(parent, engine, oclass, data, size, pobject);
+ if (ret)
+ return ret;
+
+ bar = (struct nvkm_bar *)*pobject;
+ bar->iomap_uncached = true;
+ return 0;
+}
+
+struct nvkm_oclass
+gk20a_bar_oclass = {
+ .handle = NV_SUBDEV(BAR, 0xea),
+ .ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = gk20a_bar_ctor,
+ .dtor = gf100_bar_dtor,
+ .init = gf100_bar_init,
+ .fini = _nvkm_bar_fini,
+ },
+};
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bar/nv50.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bar/nv50.c
new file mode 100644
index 000000000000..8548adb91dcc
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bar/nv50.c
@@ -0,0 +1,271 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "priv.h"
+
+#include <core/device.h>
+#include <core/gpuobj.h>
+#include <subdev/fb.h>
+#include <subdev/mmu.h>
+#include <subdev/timer.h>
+
+struct nv50_bar_priv {
+ struct nvkm_bar base;
+ spinlock_t lock;
+ struct nvkm_gpuobj *mem;
+ struct nvkm_gpuobj *pad;
+ struct nvkm_gpuobj *pgd;
+ struct nvkm_vm *bar1_vm;
+ struct nvkm_gpuobj *bar1;
+ struct nvkm_vm *bar3_vm;
+ struct nvkm_gpuobj *bar3;
+};
+
+static int
+nv50_bar_kmap(struct nvkm_bar *bar, struct nvkm_mem *mem, u32 flags,
+ struct nvkm_vma *vma)
+{
+ struct nv50_bar_priv *priv = (void *)bar;
+ int ret;
+
+ ret = nvkm_vm_get(priv->bar3_vm, mem->size << 12, 12, flags, vma);
+ if (ret)
+ return ret;
+
+ nvkm_vm_map(vma, mem);
+ return 0;
+}
+
+static int
+nv50_bar_umap(struct nvkm_bar *bar, struct nvkm_mem *mem, u32 flags,
+ struct nvkm_vma *vma)
+{
+ struct nv50_bar_priv *priv = (void *)bar;
+ int ret;
+
+ ret = nvkm_vm_get(priv->bar1_vm, mem->size << 12, 12, flags, vma);
+ if (ret)
+ return ret;
+
+ nvkm_vm_map(vma, mem);
+ return 0;
+}
+
+static void
+nv50_bar_unmap(struct nvkm_bar *bar, struct nvkm_vma *vma)
+{
+ nvkm_vm_unmap(vma);
+ nvkm_vm_put(vma);
+}
+
+static void
+nv50_bar_flush(struct nvkm_bar *bar)
+{
+ struct nv50_bar_priv *priv = (void *)bar;
+ unsigned long flags;
+ spin_lock_irqsave(&priv->lock, flags);
+ nv_wr32(priv, 0x00330c, 0x00000001);
+ if (!nv_wait(priv, 0x00330c, 0x00000002, 0x00000000))
+ nv_warn(priv, "flush timeout\n");
+ spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+void
+g84_bar_flush(struct nvkm_bar *bar)
+{
+ struct nv50_bar_priv *priv = (void *)bar;
+ unsigned long flags;
+ spin_lock_irqsave(&priv->lock, flags);
+ nv_wr32(bar, 0x070000, 0x00000001);
+ if (!nv_wait(priv, 0x070000, 0x00000002, 0x00000000))
+ nv_warn(priv, "flush timeout\n");
+ spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+static int
+nv50_bar_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 size,
+ struct nvkm_object **pobject)
+{
+ struct nvkm_device *device = nv_device(parent);
+ struct nvkm_object *heap;
+ struct nvkm_vm *vm;
+ struct nv50_bar_priv *priv;
+ u64 start, limit;
+ int ret;
+
+ ret = nvkm_bar_create(parent, engine, oclass, &priv);
+ *pobject = nv_object(priv);
+ if (ret)
+ return ret;
+
+ ret = nvkm_gpuobj_new(nv_object(priv), NULL, 0x20000, 0,
+ NVOBJ_FLAG_HEAP, &priv->mem);
+ heap = nv_object(priv->mem);
+ if (ret)
+ return ret;
+
+ ret = nvkm_gpuobj_new(nv_object(priv), heap,
+ (device->chipset == 0x50) ? 0x1400 : 0x0200,
+ 0, 0, &priv->pad);
+ if (ret)
+ return ret;
+
+ ret = nvkm_gpuobj_new(nv_object(priv), heap, 0x4000, 0, 0, &priv->pgd);
+ if (ret)
+ return ret;
+
+ /* BAR3 */
+ start = 0x0100000000ULL;
+ limit = start + nv_device_resource_len(device, 3);
+
+ ret = nvkm_vm_new(device, start, limit, start, &vm);
+ if (ret)
+ return ret;
+
+ atomic_inc(&vm->engref[NVDEV_SUBDEV_BAR]);
+
+ ret = nvkm_gpuobj_new(nv_object(priv), heap,
+ ((limit-- - start) >> 12) * 8, 0x1000,
+ NVOBJ_FLAG_ZERO_ALLOC, &vm->pgt[0].obj[0]);
+ vm->pgt[0].refcount[0] = 1;
+ if (ret)
+ return ret;
+
+ ret = nvkm_vm_ref(vm, &priv->bar3_vm, priv->pgd);
+ nvkm_vm_ref(NULL, &vm, NULL);
+ if (ret)
+ return ret;
+
+ ret = nvkm_gpuobj_new(nv_object(priv), heap, 24, 16, 0, &priv->bar3);
+ if (ret)
+ return ret;
+
+ nv_wo32(priv->bar3, 0x00, 0x7fc00000);
+ nv_wo32(priv->bar3, 0x04, lower_32_bits(limit));
+ nv_wo32(priv->bar3, 0x08, lower_32_bits(start));
+ nv_wo32(priv->bar3, 0x0c, upper_32_bits(limit) << 24 |
+ upper_32_bits(start));
+ nv_wo32(priv->bar3, 0x10, 0x00000000);
+ nv_wo32(priv->bar3, 0x14, 0x00000000);
+
+ /* BAR1 */
+ start = 0x0000000000ULL;
+ limit = start + nv_device_resource_len(device, 1);
+
+ ret = nvkm_vm_new(device, start, limit--, start, &vm);
+ if (ret)
+ return ret;
+
+ atomic_inc(&vm->engref[NVDEV_SUBDEV_BAR]);
+
+ ret = nvkm_vm_ref(vm, &priv->bar1_vm, priv->pgd);
+ nvkm_vm_ref(NULL, &vm, NULL);
+ if (ret)
+ return ret;
+
+ ret = nvkm_gpuobj_new(nv_object(priv), heap, 24, 16, 0, &priv->bar1);
+ if (ret)
+ return ret;
+
+ nv_wo32(priv->bar1, 0x00, 0x7fc00000);
+ nv_wo32(priv->bar1, 0x04, lower_32_bits(limit));
+ nv_wo32(priv->bar1, 0x08, lower_32_bits(start));
+ nv_wo32(priv->bar1, 0x0c, upper_32_bits(limit) << 24 |
+ upper_32_bits(start));
+ nv_wo32(priv->bar1, 0x10, 0x00000000);
+ nv_wo32(priv->bar1, 0x14, 0x00000000);
+
+ priv->base.alloc = nvkm_bar_alloc;
+ priv->base.kmap = nv50_bar_kmap;
+ priv->base.umap = nv50_bar_umap;
+ priv->base.unmap = nv50_bar_unmap;
+ if (device->chipset == 0x50)
+ priv->base.flush = nv50_bar_flush;
+ else
+ priv->base.flush = g84_bar_flush;
+ spin_lock_init(&priv->lock);
+ return 0;
+}
+
+static void
+nv50_bar_dtor(struct nvkm_object *object)
+{
+ struct nv50_bar_priv *priv = (void *)object;
+ nvkm_gpuobj_ref(NULL, &priv->bar1);
+ nvkm_vm_ref(NULL, &priv->bar1_vm, priv->pgd);
+ nvkm_gpuobj_ref(NULL, &priv->bar3);
+ if (priv->bar3_vm) {
+ nvkm_gpuobj_ref(NULL, &priv->bar3_vm->pgt[0].obj[0]);
+ nvkm_vm_ref(NULL, &priv->bar3_vm, priv->pgd);
+ }
+ nvkm_gpuobj_ref(NULL, &priv->pgd);
+ nvkm_gpuobj_ref(NULL, &priv->pad);
+ nvkm_gpuobj_ref(NULL, &priv->mem);
+ nvkm_bar_destroy(&priv->base);
+}
+
+static int
+nv50_bar_init(struct nvkm_object *object)
+{
+ struct nv50_bar_priv *priv = (void *)object;
+ int ret, i;
+
+ ret = nvkm_bar_init(&priv->base);
+ if (ret)
+ return ret;
+
+ nv_mask(priv, 0x000200, 0x00000100, 0x00000000);
+ nv_mask(priv, 0x000200, 0x00000100, 0x00000100);
+ nv_wr32(priv, 0x100c80, 0x00060001);
+ if (!nv_wait(priv, 0x100c80, 0x00000001, 0x00000000)) {
+ nv_error(priv, "vm flush timeout\n");
+ return -EBUSY;
+ }
+
+ nv_wr32(priv, 0x001704, 0x00000000 | priv->mem->addr >> 12);
+ nv_wr32(priv, 0x001704, 0x40000000 | priv->mem->addr >> 12);
+ nv_wr32(priv, 0x001708, 0x80000000 | priv->bar1->node->offset >> 4);
+ nv_wr32(priv, 0x00170c, 0x80000000 | priv->bar3->node->offset >> 4);
+ for (i = 0; i < 8; i++)
+ nv_wr32(priv, 0x001900 + (i * 4), 0x00000000);
+ return 0;
+}
+
+static int
+nv50_bar_fini(struct nvkm_object *object, bool suspend)
+{
+ struct nv50_bar_priv *priv = (void *)object;
+ return nvkm_bar_fini(&priv->base, suspend);
+}
+
+struct nvkm_oclass
+nv50_bar_oclass = {
+ .handle = NV_SUBDEV(BAR, 0x50),
+ .ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv50_bar_ctor,
+ .dtor = nv50_bar_dtor,
+ .init = nv50_bar_init,
+ .fini = nv50_bar_fini,
+ },
+};
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bar/priv.h b/drivers/gpu/drm/nouveau/nvkm/subdev/bar/priv.h
new file mode 100644
index 000000000000..aa85f61b48c2
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bar/priv.h
@@ -0,0 +1,30 @@
+#ifndef __NVKM_BAR_PRIV_H__
+#define __NVKM_BAR_PRIV_H__
+#include <subdev/bar.h>
+
+#define nvkm_bar_create(p,e,o,d) \
+ nvkm_bar_create_((p), (e), (o), sizeof(**d), (void **)d)
+#define nvkm_bar_init(p) \
+ nvkm_subdev_init(&(p)->base)
+#define nvkm_bar_fini(p,s) \
+ nvkm_subdev_fini(&(p)->base, (s))
+
+int nvkm_bar_create_(struct nvkm_object *, struct nvkm_object *,
+ struct nvkm_oclass *, int, void **);
+void nvkm_bar_destroy(struct nvkm_bar *);
+
+void _nvkm_bar_dtor(struct nvkm_object *);
+#define _nvkm_bar_init _nvkm_subdev_init
+#define _nvkm_bar_fini _nvkm_subdev_fini
+
+int nvkm_bar_alloc(struct nvkm_bar *, struct nvkm_object *,
+ struct nvkm_mem *, struct nvkm_object **);
+
+void g84_bar_flush(struct nvkm_bar *);
+
+int gf100_bar_ctor(struct nvkm_object *, struct nvkm_object *,
+ struct nvkm_oclass *, void *, u32,
+ struct nvkm_object **);
+void gf100_bar_dtor(struct nvkm_object *);
+int gf100_bar_init(struct nvkm_object *);
+#endif
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/Kbuild b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/Kbuild
new file mode 100644
index 000000000000..64730d5e9351
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/Kbuild
@@ -0,0 +1,37 @@
+nvkm-y += nvkm/subdev/bios/base.o
+nvkm-y += nvkm/subdev/bios/bit.o
+nvkm-y += nvkm/subdev/bios/boost.o
+nvkm-y += nvkm/subdev/bios/conn.o
+nvkm-y += nvkm/subdev/bios/cstep.o
+nvkm-y += nvkm/subdev/bios/dcb.o
+nvkm-y += nvkm/subdev/bios/disp.o
+nvkm-y += nvkm/subdev/bios/dp.o
+nvkm-y += nvkm/subdev/bios/extdev.o
+nvkm-y += nvkm/subdev/bios/fan.o
+nvkm-y += nvkm/subdev/bios/gpio.o
+nvkm-y += nvkm/subdev/bios/i2c.o
+nvkm-y += nvkm/subdev/bios/image.o
+nvkm-y += nvkm/subdev/bios/init.o
+nvkm-y += nvkm/subdev/bios/mxm.o
+nvkm-y += nvkm/subdev/bios/npde.o
+nvkm-y += nvkm/subdev/bios/pcir.o
+nvkm-y += nvkm/subdev/bios/perf.o
+nvkm-y += nvkm/subdev/bios/pll.o
+nvkm-y += nvkm/subdev/bios/pmu.o
+nvkm-y += nvkm/subdev/bios/ramcfg.o
+nvkm-y += nvkm/subdev/bios/rammap.o
+nvkm-y += nvkm/subdev/bios/shadow.o
+nvkm-y += nvkm/subdev/bios/shadowacpi.o
+nvkm-y += nvkm/subdev/bios/shadowof.o
+nvkm-y += nvkm/subdev/bios/shadowpci.o
+nvkm-y += nvkm/subdev/bios/shadowramin.o
+nvkm-y += nvkm/subdev/bios/shadowrom.o
+nvkm-y += nvkm/subdev/bios/timing.o
+nvkm-y += nvkm/subdev/bios/therm.o
+nvkm-y += nvkm/subdev/bios/vmap.o
+nvkm-y += nvkm/subdev/bios/volt.o
+nvkm-y += nvkm/subdev/bios/xpio.o
+nvkm-y += nvkm/subdev/bios/M0203.o
+nvkm-y += nvkm/subdev/bios/M0205.o
+nvkm-y += nvkm/subdev/bios/M0209.o
+nvkm-y += nvkm/subdev/bios/P0260.o
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/M0203.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/M0203.c
new file mode 100644
index 000000000000..08eb03fbc203
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/M0203.c
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2014 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include <subdev/bios.h>
+#include <subdev/bios/bit.h>
+#include <subdev/bios/M0203.h>
+
+u32
+nvbios_M0203Te(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
+{
+ struct bit_entry bit_M;
+ u32 data = 0x00000000;
+
+ if (!bit_entry(bios, 'M', &bit_M)) {
+ if (bit_M.version == 2 && bit_M.length > 0x04)
+ data = nv_ro16(bios, bit_M.offset + 0x03);
+ if (data) {
+ *ver = nv_ro08(bios, data + 0x00);
+ switch (*ver) {
+ case 0x10:
+ *hdr = nv_ro08(bios, data + 0x01);
+ *len = nv_ro08(bios, data + 0x02);
+ *cnt = nv_ro08(bios, data + 0x03);
+ return data;
+ default:
+ break;
+ }
+ }
+ }
+
+ return 0x00000000;
+}
+
+u32
+nvbios_M0203Tp(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
+ struct nvbios_M0203T *info)
+{
+ u32 data = nvbios_M0203Te(bios, ver, hdr, cnt, len);
+ memset(info, 0x00, sizeof(*info));
+ switch (!!data * *ver) {
+ case 0x10:
+ info->type = nv_ro08(bios, data + 0x04);
+ info->pointer = nv_ro16(bios, data + 0x05);
+ break;
+ default:
+ break;
+ }
+ return data;
+}
+
+u32
+nvbios_M0203Ee(struct nvkm_bios *bios, int idx, u8 *ver, u8 *hdr)
+{
+ u8 cnt, len;
+ u32 data = nvbios_M0203Te(bios, ver, hdr, &cnt, &len);
+ if (data && idx < cnt) {
+ data = data + *hdr + idx * len;
+ *hdr = len;
+ return data;
+ }
+ return 0x00000000;
+}
+
+u32
+nvbios_M0203Ep(struct nvkm_bios *bios, int idx, u8 *ver, u8 *hdr,
+ struct nvbios_M0203E *info)
+{
+ u32 data = nvbios_M0203Ee(bios, idx, ver, hdr);
+ memset(info, 0x00, sizeof(*info));
+ switch (!!data * *ver) {
+ case 0x10:
+ info->type = (nv_ro08(bios, data + 0x00) & 0x0f) >> 0;
+ info->strap = (nv_ro08(bios, data + 0x00) & 0xf0) >> 4;
+ info->group = (nv_ro08(bios, data + 0x01) & 0x0f) >> 0;
+ return data;
+ default:
+ break;
+ }
+ return 0x00000000;
+}
+
+u32
+nvbios_M0203Em(struct nvkm_bios *bios, u8 ramcfg, u8 *ver, u8 *hdr,
+ struct nvbios_M0203E *info)
+{
+ struct nvbios_M0203T M0203T;
+ u8 cnt, len, idx = 0xff;
+ u32 data;
+
+ if (!nvbios_M0203Tp(bios, ver, hdr, &cnt, &len, &M0203T)) {
+ nv_warn(bios, "M0203T not found\n");
+ return 0x00000000;
+ }
+
+ while ((data = nvbios_M0203Ep(bios, ++idx, ver, hdr, info))) {
+ switch (M0203T.type) {
+ case M0203T_TYPE_RAMCFG:
+ if (info->strap != ramcfg)
+ continue;
+ return data;
+ default:
+ nv_warn(bios, "M0203T type %02x\n", M0203T.type);
+ return 0x00000000;
+ }
+ }
+
+ return data;
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/M0205.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/M0205.c
new file mode 100644
index 000000000000..e1a8ad5f3066
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/M0205.c
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include <subdev/bios.h>
+#include <subdev/bios/bit.h>
+#include <subdev/bios/M0205.h>
+
+u32
+nvbios_M0205Te(struct nvkm_bios *bios,
+ u8 *ver, u8 *hdr, u8 *cnt, u8 *len, u8 *snr, u8 *ssz)
+{
+ struct bit_entry bit_M;
+ u32 data = 0x00000000;
+
+ if (!bit_entry(bios, 'M', &bit_M)) {
+ if (bit_M.version == 2 && bit_M.length > 0x08)
+ data = nv_ro32(bios, bit_M.offset + 0x05);
+ if (data) {
+ *ver = nv_ro08(bios, data + 0x00);
+ switch (*ver) {
+ case 0x10:
+ *hdr = nv_ro08(bios, data + 0x01);
+ *len = nv_ro08(bios, data + 0x02);
+ *ssz = nv_ro08(bios, data + 0x03);
+ *snr = nv_ro08(bios, data + 0x04);
+ *cnt = nv_ro08(bios, data + 0x05);
+ return data;
+ default:
+ break;
+ }
+ }
+ }
+
+ return 0x00000000;
+}
+
+u32
+nvbios_M0205Tp(struct nvkm_bios *bios,
+ u8 *ver, u8 *hdr, u8 *cnt, u8 *len, u8 *snr, u8 *ssz,
+ struct nvbios_M0205T *info)
+{
+ u32 data = nvbios_M0205Te(bios, ver, hdr, cnt, len, snr, ssz);
+ memset(info, 0x00, sizeof(*info));
+ switch (!!data * *ver) {
+ case 0x10:
+ info->freq = nv_ro16(bios, data + 0x06);
+ break;
+ default:
+ break;
+ }
+ return data;
+}
+
+u32
+nvbios_M0205Ee(struct nvkm_bios *bios, int idx,
+ u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
+{
+ u8 snr, ssz;
+ u32 data = nvbios_M0205Te(bios, ver, hdr, cnt, len, &snr, &ssz);
+ if (data && idx < *cnt) {
+ data = data + *hdr + idx * (*len + (snr * ssz));
+ *hdr = *len;
+ *cnt = snr;
+ *len = ssz;
+ return data;
+ }
+ return 0x00000000;
+}
+
+u32
+nvbios_M0205Ep(struct nvkm_bios *bios, int idx,
+ u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
+ struct nvbios_M0205E *info)
+{
+ u32 data = nvbios_M0205Ee(bios, idx, ver, hdr, cnt, len);
+ memset(info, 0x00, sizeof(*info));
+ switch (!!data * *ver) {
+ case 0x10:
+ info->type = nv_ro08(bios, data + 0x00) & 0x0f;
+ return data;
+ default:
+ break;
+ }
+ return 0x00000000;
+}
+
+u32
+nvbios_M0205Se(struct nvkm_bios *bios, int ent, int idx, u8 *ver, u8 *hdr)
+{
+
+ u8 cnt, len;
+ u32 data = nvbios_M0205Ee(bios, ent, ver, hdr, &cnt, &len);
+ if (data && idx < cnt) {
+ data = data + *hdr + idx * len;
+ *hdr = len;
+ return data;
+ }
+ return 0x00000000;
+}
+
+u32
+nvbios_M0205Sp(struct nvkm_bios *bios, int ent, int idx, u8 *ver, u8 *hdr,
+ struct nvbios_M0205S *info)
+{
+ u32 data = nvbios_M0205Se(bios, ent, idx, ver, hdr);
+ memset(info, 0x00, sizeof(*info));
+ switch (!!data * *ver) {
+ case 0x10:
+ info->data = nv_ro08(bios, data + 0x00);
+ return data;
+ default:
+ break;
+ }
+ return 0x00000000;
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/M0209.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/M0209.c
new file mode 100644
index 000000000000..3026920c3358
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/M0209.c
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include <subdev/bios.h>
+#include <subdev/bios/bit.h>
+#include <subdev/bios/M0209.h>
+
+u32
+nvbios_M0209Te(struct nvkm_bios *bios,
+ u8 *ver, u8 *hdr, u8 *cnt, u8 *len, u8 *snr, u8 *ssz)
+{
+ struct bit_entry bit_M;
+ u32 data = 0x00000000;
+
+ if (!bit_entry(bios, 'M', &bit_M)) {
+ if (bit_M.version == 2 && bit_M.length > 0x0c)
+ data = nv_ro32(bios, bit_M.offset + 0x09);
+ if (data) {
+ *ver = nv_ro08(bios, data + 0x00);
+ switch (*ver) {
+ case 0x10:
+ *hdr = nv_ro08(bios, data + 0x01);
+ *len = nv_ro08(bios, data + 0x02);
+ *ssz = nv_ro08(bios, data + 0x03);
+ *snr = 1;
+ *cnt = nv_ro08(bios, data + 0x04);
+ return data;
+ default:
+ break;
+ }
+ }
+ }
+
+ return 0x00000000;
+}
+
+u32
+nvbios_M0209Ee(struct nvkm_bios *bios, int idx,
+ u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
+{
+ u8 snr, ssz;
+ u32 data = nvbios_M0209Te(bios, ver, hdr, cnt, len, &snr, &ssz);
+ if (data && idx < *cnt) {
+ data = data + *hdr + idx * (*len + (snr * ssz));
+ *hdr = *len;
+ *cnt = snr;
+ *len = ssz;
+ return data;
+ }
+ return 0x00000000;
+}
+
+u32
+nvbios_M0209Ep(struct nvkm_bios *bios, int idx,
+ u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_M0209E *info)
+{
+ u32 data = nvbios_M0209Ee(bios, idx, ver, hdr, cnt, len);
+ memset(info, 0x00, sizeof(*info));
+ switch (!!data * *ver) {
+ case 0x10:
+ info->v00_40 = (nv_ro08(bios, data + 0x00) & 0x40) >> 6;
+ info->bits = nv_ro08(bios, data + 0x00) & 0x3f;
+ info->modulo = nv_ro08(bios, data + 0x01);
+ info->v02_40 = (nv_ro08(bios, data + 0x02) & 0x40) >> 6;
+ info->v02_07 = nv_ro08(bios, data + 0x02) & 0x07;
+ info->v03 = nv_ro08(bios, data + 0x03);
+ return data;
+ default:
+ break;
+ }
+ return 0x00000000;
+}
+
+u32
+nvbios_M0209Se(struct nvkm_bios *bios, int ent, int idx, u8 *ver, u8 *hdr)
+{
+
+ u8 cnt, len;
+ u32 data = nvbios_M0209Ee(bios, ent, ver, hdr, &cnt, &len);
+ if (data && idx < cnt) {
+ data = data + *hdr + idx * len;
+ *hdr = len;
+ return data;
+ }
+ return 0x00000000;
+}
+
+u32
+nvbios_M0209Sp(struct nvkm_bios *bios, int ent, int idx, u8 *ver, u8 *hdr,
+ struct nvbios_M0209S *info)
+{
+ struct nvbios_M0209E M0209E;
+ u8 cnt, len;
+ u32 data = nvbios_M0209Ep(bios, ent, ver, hdr, &cnt, &len, &M0209E);
+ if (data) {
+ u32 i, data = nvbios_M0209Se(bios, ent, idx, ver, hdr);
+ memset(info, 0x00, sizeof(*info));
+ switch (!!data * *ver) {
+ case 0x10:
+ for (i = 0; i < ARRAY_SIZE(info->data); i++) {
+ u32 bits = (i % M0209E.modulo) * M0209E.bits;
+ u32 mask = (1ULL << M0209E.bits) - 1;
+ u16 off = bits / 8;
+ u8 mod = bits % 8;
+ info->data[i] = nv_ro32(bios, data + off);
+ info->data[i] = info->data[i] >> mod;
+ info->data[i] = info->data[i] & mask;
+ }
+ return data;
+ default:
+ break;
+ }
+ }
+ return 0x00000000;
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/P0260.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/P0260.c
new file mode 100644
index 000000000000..b72edcf849b6
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/P0260.c
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include <subdev/bios.h>
+#include <subdev/bios/bit.h>
+#include <subdev/bios/P0260.h>
+
+u32
+nvbios_P0260Te(struct nvkm_bios *bios,
+ u8 *ver, u8 *hdr, u8 *cnt, u8 *len, u8 *xnr, u8 *xsz)
+{
+ struct bit_entry bit_P;
+ u32 data = 0x00000000;
+
+ if (!bit_entry(bios, 'P', &bit_P)) {
+ if (bit_P.version == 2 && bit_P.length > 0x63)
+ data = nv_ro32(bios, bit_P.offset + 0x60);
+ if (data) {
+ *ver = nv_ro08(bios, data + 0);
+ switch (*ver) {
+ case 0x10:
+ *hdr = nv_ro08(bios, data + 1);
+ *cnt = nv_ro08(bios, data + 2);
+ *len = 4;
+ *xnr = nv_ro08(bios, data + 3);
+ *xsz = 4;
+ return data;
+ default:
+ break;
+ }
+ }
+ }
+
+ return 0x00000000;
+}
+
+u32
+nvbios_P0260Ee(struct nvkm_bios *bios, int idx, u8 *ver, u8 *len)
+{
+ u8 hdr, cnt, xnr, xsz;
+ u32 data = nvbios_P0260Te(bios, ver, &hdr, &cnt, len, &xnr, &xsz);
+ if (data && idx < cnt)
+ return data + hdr + (idx * *len);
+ return 0x00000000;
+}
+
+u32
+nvbios_P0260Ep(struct nvkm_bios *bios, int idx, u8 *ver, u8 *len,
+ struct nvbios_P0260E *info)
+{
+ u32 data = nvbios_P0260Ee(bios, idx, ver, len);
+ memset(info, 0x00, sizeof(*info));
+ switch (!!data * *ver) {
+ case 0x10:
+ info->data = nv_ro32(bios, data);
+ return data;
+ default:
+ break;
+ }
+ return 0x00000000;
+}
+
+u32
+nvbios_P0260Xe(struct nvkm_bios *bios, int idx, u8 *ver, u8 *xsz)
+{
+ u8 hdr, cnt, len, xnr;
+ u32 data = nvbios_P0260Te(bios, ver, &hdr, &cnt, &len, &xnr, xsz);
+ if (data && idx < xnr)
+ return data + hdr + (cnt * len) + (idx * *xsz);
+ return 0x00000000;
+}
+
+u32
+nvbios_P0260Xp(struct nvkm_bios *bios, int idx, u8 *ver, u8 *hdr,
+ struct nvbios_P0260X *info)
+{
+ u32 data = nvbios_P0260Xe(bios, idx, ver, hdr);
+ memset(info, 0x00, sizeof(*info));
+ switch (!!data * *ver) {
+ case 0x10:
+ info->data = nv_ro32(bios, data);
+ return data;
+ default:
+ break;
+ }
+ return 0x00000000;
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/base.c
new file mode 100644
index 000000000000..8db204f92ed3
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/base.c
@@ -0,0 +1,206 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "priv.h"
+
+#include <subdev/bios.h>
+#include <subdev/bios/bmp.h>
+#include <subdev/bios/bit.h>
+
+u8
+nvbios_checksum(const u8 *data, int size)
+{
+ u8 sum = 0;
+ while (size--)
+ sum += *data++;
+ return sum;
+}
+
+u16
+nvbios_findstr(const u8 *data, int size, const char *str, int len)
+{
+ int i, j;
+
+ for (i = 0; i <= (size - len); i++) {
+ for (j = 0; j < len; j++)
+ if ((char)data[i + j] != str[j])
+ break;
+ if (j == len)
+ return i;
+ }
+
+ return 0;
+}
+
+int
+nvbios_extend(struct nvkm_bios *bios, u32 length)
+{
+ if (bios->size < length) {
+ u8 *prev = bios->data;
+ if (!(bios->data = kmalloc(length, GFP_KERNEL))) {
+ bios->data = prev;
+ return -ENOMEM;
+ }
+ memcpy(bios->data, prev, bios->size);
+ bios->size = length;
+ kfree(prev);
+ return 1;
+ }
+ return 0;
+}
+
+static u8
+nvkm_bios_rd08(struct nvkm_object *object, u64 addr)
+{
+ struct nvkm_bios *bios = (void *)object;
+ return bios->data[addr];
+}
+
+static u16
+nvkm_bios_rd16(struct nvkm_object *object, u64 addr)
+{
+ struct nvkm_bios *bios = (void *)object;
+ return get_unaligned_le16(&bios->data[addr]);
+}
+
+static u32
+nvkm_bios_rd32(struct nvkm_object *object, u64 addr)
+{
+ struct nvkm_bios *bios = (void *)object;
+ return get_unaligned_le32(&bios->data[addr]);
+}
+
+static void
+nvkm_bios_wr08(struct nvkm_object *object, u64 addr, u8 data)
+{
+ struct nvkm_bios *bios = (void *)object;
+ bios->data[addr] = data;
+}
+
+static void
+nvkm_bios_wr16(struct nvkm_object *object, u64 addr, u16 data)
+{
+ struct nvkm_bios *bios = (void *)object;
+ put_unaligned_le16(data, &bios->data[addr]);
+}
+
+static void
+nvkm_bios_wr32(struct nvkm_object *object, u64 addr, u32 data)
+{
+ struct nvkm_bios *bios = (void *)object;
+ put_unaligned_le32(data, &bios->data[addr]);
+}
+
+static int
+nvkm_bios_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 size,
+ struct nvkm_object **pobject)
+{
+ struct nvkm_bios *bios;
+ struct bit_entry bit_i;
+ int ret;
+
+ ret = nvkm_subdev_create(parent, engine, oclass, 0,
+ "VBIOS", "bios", &bios);
+ *pobject = nv_object(bios);
+ if (ret)
+ return ret;
+
+ ret = nvbios_shadow(bios);
+ if (ret)
+ return ret;
+
+ /* detect type of vbios we're dealing with */
+ bios->bmp_offset = nvbios_findstr(bios->data, bios->size,
+ "\xff\x7f""NV\0", 5);
+ if (bios->bmp_offset) {
+ nv_info(bios, "BMP version %x.%x\n",
+ bmp_version(bios) >> 8,
+ bmp_version(bios) & 0xff);
+ }
+
+ bios->bit_offset = nvbios_findstr(bios->data, bios->size,
+ "\xff\xb8""BIT", 5);
+ if (bios->bit_offset)
+ nv_info(bios, "BIT signature found\n");
+
+ /* determine the vbios version number */
+ if (!bit_entry(bios, 'i', &bit_i) && bit_i.length >= 4) {
+ bios->version.major = nv_ro08(bios, bit_i.offset + 3);
+ bios->version.chip = nv_ro08(bios, bit_i.offset + 2);
+ bios->version.minor = nv_ro08(bios, bit_i.offset + 1);
+ bios->version.micro = nv_ro08(bios, bit_i.offset + 0);
+ bios->version.patch = nv_ro08(bios, bit_i.offset + 4);
+ } else
+ if (bmp_version(bios)) {
+ bios->version.major = nv_ro08(bios, bios->bmp_offset + 13);
+ bios->version.chip = nv_ro08(bios, bios->bmp_offset + 12);
+ bios->version.minor = nv_ro08(bios, bios->bmp_offset + 11);
+ bios->version.micro = nv_ro08(bios, bios->bmp_offset + 10);
+ }
+
+ nv_info(bios, "version %02x.%02x.%02x.%02x.%02x\n",
+ bios->version.major, bios->version.chip,
+ bios->version.minor, bios->version.micro, bios->version.patch);
+
+ return 0;
+}
+
+static void
+nvkm_bios_dtor(struct nvkm_object *object)
+{
+ struct nvkm_bios *bios = (void *)object;
+ kfree(bios->data);
+ nvkm_subdev_destroy(&bios->base);
+}
+
+static int
+nvkm_bios_init(struct nvkm_object *object)
+{
+ struct nvkm_bios *bios = (void *)object;
+ return nvkm_subdev_init(&bios->base);
+}
+
+static int
+nvkm_bios_fini(struct nvkm_object *object, bool suspend)
+{
+ struct nvkm_bios *bios = (void *)object;
+ return nvkm_subdev_fini(&bios->base, suspend);
+}
+
+struct nvkm_oclass
+nvkm_bios_oclass = {
+ .handle = NV_SUBDEV(VBIOS, 0x00),
+ .ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nvkm_bios_ctor,
+ .dtor = nvkm_bios_dtor,
+ .init = nvkm_bios_init,
+ .fini = nvkm_bios_fini,
+ .rd08 = nvkm_bios_rd08,
+ .rd16 = nvkm_bios_rd16,
+ .rd32 = nvkm_bios_rd32,
+ .wr08 = nvkm_bios_wr08,
+ .wr16 = nvkm_bios_wr16,
+ .wr32 = nvkm_bios_wr32,
+ },
+};
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/bit.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/bit.c
new file mode 100644
index 000000000000..eab540496cdf
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/bit.c
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include <subdev/bios.h>
+#include <subdev/bios/bit.h>
+
+int
+bit_entry(struct nvkm_bios *bios, u8 id, struct bit_entry *bit)
+{
+ if (likely(bios->bit_offset)) {
+ u8 entries = nv_ro08(bios, bios->bit_offset + 10);
+ u32 entry = bios->bit_offset + 12;
+ while (entries--) {
+ if (nv_ro08(bios, entry + 0) == id) {
+ bit->id = nv_ro08(bios, entry + 0);
+ bit->version = nv_ro08(bios, entry + 1);
+ bit->length = nv_ro16(bios, entry + 2);
+ bit->offset = nv_ro16(bios, entry + 4);
+ return 0;
+ }
+
+ entry += nv_ro08(bios, bios->bit_offset + 9);
+ }
+
+ return -ENOENT;
+ }
+
+ return -EINVAL;
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/boost.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/boost.c
new file mode 100644
index 000000000000..12e958533f46
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/boost.c
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include <subdev/bios.h>
+#include <subdev/bios/bit.h>
+#include <subdev/bios/boost.h>
+
+u16
+nvbios_boostTe(struct nvkm_bios *bios,
+ u8 *ver, u8 *hdr, u8 *cnt, u8 *len, u8 *snr, u8 *ssz)
+{
+ struct bit_entry bit_P;
+ u16 boost = 0x0000;
+
+ if (!bit_entry(bios, 'P', &bit_P)) {
+ if (bit_P.version == 2)
+ boost = nv_ro16(bios, bit_P.offset + 0x30);
+
+ if (boost) {
+ *ver = nv_ro08(bios, boost + 0);
+ switch (*ver) {
+ case 0x11:
+ *hdr = nv_ro08(bios, boost + 1);
+ *cnt = nv_ro08(bios, boost + 5);
+ *len = nv_ro08(bios, boost + 2);
+ *snr = nv_ro08(bios, boost + 4);
+ *ssz = nv_ro08(bios, boost + 3);
+ return boost;
+ default:
+ break;
+ }
+ }
+ }
+
+ return 0x0000;
+}
+
+u16
+nvbios_boostEe(struct nvkm_bios *bios, int idx,
+ u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
+{
+ u8 snr, ssz;
+ u16 data = nvbios_boostTe(bios, ver, hdr, cnt, len, &snr, &ssz);
+ if (data && idx < *cnt) {
+ data = data + *hdr + (idx * (*len + (snr * ssz)));
+ *hdr = *len;
+ *cnt = snr;
+ *len = ssz;
+ return data;
+ }
+ return 0x0000;
+}
+
+u16
+nvbios_boostEp(struct nvkm_bios *bios, int idx,
+ u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_boostE *info)
+{
+ u16 data = nvbios_boostEe(bios, idx, ver, hdr, cnt, len);
+ memset(info, 0x00, sizeof(*info));
+ if (data) {
+ info->pstate = (nv_ro16(bios, data + 0x00) & 0x01e0) >> 5;
+ info->min = nv_ro16(bios, data + 0x02) * 1000;
+ info->max = nv_ro16(bios, data + 0x04) * 1000;
+ }
+ return data;
+}
+
+u16
+nvbios_boostEm(struct nvkm_bios *bios, u8 pstate,
+ u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_boostE *info)
+{
+ u32 data, idx = 0;
+ while ((data = nvbios_boostEp(bios, idx++, ver, hdr, cnt, len, info))) {
+ if (info->pstate == pstate)
+ break;
+ }
+ return data;
+}
+
+u16
+nvbios_boostSe(struct nvkm_bios *bios, int idx,
+ u16 data, u8 *ver, u8 *hdr, u8 cnt, u8 len)
+{
+ if (data && idx < cnt) {
+ data = data + *hdr + (idx * len);
+ *hdr = len;
+ return data;
+ }
+ return 0x0000;
+}
+
+u16
+nvbios_boostSp(struct nvkm_bios *bios, int idx,
+ u16 data, u8 *ver, u8 *hdr, u8 cnt, u8 len,
+ struct nvbios_boostS *info)
+{
+ data = nvbios_boostSe(bios, idx, data, ver, hdr, cnt, len);
+ memset(info, 0x00, sizeof(*info));
+ if (data) {
+ info->domain = nv_ro08(bios, data + 0x00);
+ info->percent = nv_ro08(bios, data + 0x01);
+ info->min = nv_ro16(bios, data + 0x02) * 1000;
+ info->max = nv_ro16(bios, data + 0x04) * 1000;
+ }
+ return data;
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/conn.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/conn.c
new file mode 100644
index 000000000000..706a1650a4f2
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/conn.c
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include <subdev/bios.h>
+#include <subdev/bios/dcb.h>
+#include <subdev/bios/conn.h>
+
+u32
+nvbios_connTe(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
+{
+ u32 dcb = dcb_table(bios, ver, hdr, cnt, len);
+ if (dcb && *ver >= 0x30 && *hdr >= 0x16) {
+ u32 data = nv_ro16(bios, dcb + 0x14);
+ if (data) {
+ *ver = nv_ro08(bios, data + 0);
+ *hdr = nv_ro08(bios, data + 1);
+ *cnt = nv_ro08(bios, data + 2);
+ *len = nv_ro08(bios, data + 3);
+ return data;
+ }
+ }
+ return 0x00000000;
+}
+
+u32
+nvbios_connTp(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
+ struct nvbios_connT *info)
+{
+ u32 data = nvbios_connTe(bios, ver, hdr, cnt, len);
+ memset(info, 0x00, sizeof(*info));
+ switch (!!data * *ver) {
+ case 0x30:
+ case 0x40:
+ return data;
+ default:
+ break;
+ }
+ return 0x00000000;
+}
+
+u32
+nvbios_connEe(struct nvkm_bios *bios, u8 idx, u8 *ver, u8 *len)
+{
+ u8 hdr, cnt;
+ u32 data = nvbios_connTe(bios, ver, &hdr, &cnt, len);
+ if (data && idx < cnt)
+ return data + hdr + (idx * *len);
+ return 0x00000000;
+}
+
+u32
+nvbios_connEp(struct nvkm_bios *bios, u8 idx, u8 *ver, u8 *len,
+ struct nvbios_connE *info)
+{
+ u32 data = nvbios_connEe(bios, idx, ver, len);
+ memset(info, 0x00, sizeof(*info));
+ switch (!!data * *ver) {
+ case 0x30:
+ case 0x40:
+ info->type = nv_ro08(bios, data + 0x00);
+ info->location = nv_ro08(bios, data + 0x01) & 0x0f;
+ info->hpd = (nv_ro08(bios, data + 0x01) & 0x30) >> 4;
+ info->dp = (nv_ro08(bios, data + 0x01) & 0xc0) >> 6;
+ if (*len < 4)
+ return data;
+ info->hpd |= (nv_ro08(bios, data + 0x02) & 0x03) << 2;
+ info->dp |= nv_ro08(bios, data + 0x02) & 0x0c;
+ info->di = (nv_ro08(bios, data + 0x02) & 0xf0) >> 4;
+ info->hpd |= (nv_ro08(bios, data + 0x03) & 0x07) << 4;
+ info->sr = (nv_ro08(bios, data + 0x03) & 0x08) >> 3;
+ info->lcdid = (nv_ro08(bios, data + 0x03) & 0x70) >> 4;
+ return data;
+ default:
+ break;
+ }
+ return 0x00000000;
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/cstep.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/cstep.c
new file mode 100644
index 000000000000..16f7ad8a4f80
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/cstep.c
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include <subdev/bios.h>
+#include <subdev/bios/bit.h>
+#include <subdev/bios/cstep.h>
+
+u16
+nvbios_cstepTe(struct nvkm_bios *bios,
+ u8 *ver, u8 *hdr, u8 *cnt, u8 *len, u8 *xnr, u8 *xsz)
+{
+ struct bit_entry bit_P;
+ u16 cstep = 0x0000;
+
+ if (!bit_entry(bios, 'P', &bit_P)) {
+ if (bit_P.version == 2)
+ cstep = nv_ro16(bios, bit_P.offset + 0x34);
+
+ if (cstep) {
+ *ver = nv_ro08(bios, cstep + 0);
+ switch (*ver) {
+ case 0x10:
+ *hdr = nv_ro08(bios, cstep + 1);
+ *cnt = nv_ro08(bios, cstep + 3);
+ *len = nv_ro08(bios, cstep + 2);
+ *xnr = nv_ro08(bios, cstep + 5);
+ *xsz = nv_ro08(bios, cstep + 4);
+ return cstep;
+ default:
+ break;
+ }
+ }
+ }
+
+ return 0x0000;
+}
+
+u16
+nvbios_cstepEe(struct nvkm_bios *bios, int idx, u8 *ver, u8 *hdr)
+{
+ u8 cnt, len, xnr, xsz;
+ u16 data = nvbios_cstepTe(bios, ver, hdr, &cnt, &len, &xnr, &xsz);
+ if (data && idx < cnt) {
+ data = data + *hdr + (idx * len);
+ *hdr = len;
+ return data;
+ }
+ return 0x0000;
+}
+
+u16
+nvbios_cstepEp(struct nvkm_bios *bios, int idx, u8 *ver, u8 *hdr,
+ struct nvbios_cstepE *info)
+{
+ u16 data = nvbios_cstepEe(bios, idx, ver, hdr);
+ memset(info, 0x00, sizeof(*info));
+ if (data) {
+ info->pstate = (nv_ro16(bios, data + 0x00) & 0x01e0) >> 5;
+ info->index = nv_ro08(bios, data + 0x03);
+ }
+ return data;
+}
+
+u16
+nvbios_cstepEm(struct nvkm_bios *bios, u8 pstate, u8 *ver, u8 *hdr,
+ struct nvbios_cstepE *info)
+{
+ u32 data, idx = 0;
+ while ((data = nvbios_cstepEp(bios, idx++, ver, hdr, info))) {
+ if (info->pstate == pstate)
+ break;
+ }
+ return data;
+}
+
+u16
+nvbios_cstepXe(struct nvkm_bios *bios, int idx, u8 *ver, u8 *hdr)
+{
+ u8 cnt, len, xnr, xsz;
+ u16 data = nvbios_cstepTe(bios, ver, hdr, &cnt, &len, &xnr, &xsz);
+ if (data && idx < xnr) {
+ data = data + *hdr + (cnt * len) + (idx * xsz);
+ *hdr = xsz;
+ return data;
+ }
+ return 0x0000;
+}
+
+u16
+nvbios_cstepXp(struct nvkm_bios *bios, int idx, u8 *ver, u8 *hdr,
+ struct nvbios_cstepX *info)
+{
+ u16 data = nvbios_cstepXe(bios, idx, ver, hdr);
+ memset(info, 0x00, sizeof(*info));
+ if (data) {
+ info->freq = nv_ro16(bios, data + 0x00) * 1000;
+ info->unkn[0] = nv_ro08(bios, data + 0x02);
+ info->unkn[1] = nv_ro08(bios, data + 0x03);
+ info->voltage = nv_ro08(bios, data + 0x04);
+ }
+ return data;
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/dcb.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/dcb.c
new file mode 100644
index 000000000000..8d78140f9401
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/dcb.c
@@ -0,0 +1,234 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include <subdev/bios.h>
+#include <subdev/bios/dcb.h>
+
+#include <core/device.h>
+
+u16
+dcb_table(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
+{
+ struct nvkm_device *device = nv_device(bios);
+ u16 dcb = 0x0000;
+
+ if (device->card_type > NV_04)
+ dcb = nv_ro16(bios, 0x36);
+ if (!dcb) {
+ nv_warn(bios, "DCB table not found\n");
+ return dcb;
+ }
+
+ *ver = nv_ro08(bios, dcb);
+
+ if (*ver >= 0x42) {
+ nv_warn(bios, "DCB version 0x%02x unknown\n", *ver);
+ return 0x0000;
+ } else
+ if (*ver >= 0x30) {
+ if (nv_ro32(bios, dcb + 6) == 0x4edcbdcb) {
+ *hdr = nv_ro08(bios, dcb + 1);
+ *cnt = nv_ro08(bios, dcb + 2);
+ *len = nv_ro08(bios, dcb + 3);
+ return dcb;
+ }
+ } else
+ if (*ver >= 0x20) {
+ if (nv_ro32(bios, dcb + 4) == 0x4edcbdcb) {
+ u16 i2c = nv_ro16(bios, dcb + 2);
+ *hdr = 8;
+ *cnt = (i2c - dcb) / 8;
+ *len = 8;
+ return dcb;
+ }
+ } else
+ if (*ver >= 0x15) {
+ if (!nv_memcmp(bios, dcb - 7, "DEV_REC", 7)) {
+ u16 i2c = nv_ro16(bios, dcb + 2);
+ *hdr = 4;
+ *cnt = (i2c - dcb) / 10;
+ *len = 10;
+ return dcb;
+ }
+ } else {
+ /*
+ * v1.4 (some NV15/16, NV11+) seems the same as v1.5, but
+ * always has the same single (crt) entry, even when tv-out
+ * present, so the conclusion is this version cannot really
+ * be used.
+ *
+ * v1.2 tables (some NV6/10, and NV15+) normally have the
+ * same 5 entries, which are not specific to the card and so
+ * no use.
+ *
+ * v1.2 does have an I2C table that read_dcb_i2c_table can
+ * handle, but cards exist (nv11 in #14821) with a bad i2c
+ * table pointer, so use the indices parsed in
+ * parse_bmp_structure.
+ *
+ * v1.1 (NV5+, maybe some NV4) is entirely unhelpful
+ */
+ nv_warn(bios, "DCB contains no useful data\n");
+ return 0x0000;
+ }
+
+ nv_warn(bios, "DCB header validation failed\n");
+ return 0x0000;
+}
+
+u16
+dcb_outp(struct nvkm_bios *bios, u8 idx, u8 *ver, u8 *len)
+{
+ u8 hdr, cnt;
+ u16 dcb = dcb_table(bios, ver, &hdr, &cnt, len);
+ if (dcb && idx < cnt)
+ return dcb + hdr + (idx * *len);
+ return 0x0000;
+}
+
+static inline u16
+dcb_outp_hasht(struct dcb_output *outp)
+{
+ return (outp->extdev << 8) | (outp->location << 4) | outp->type;
+}
+
+static inline u16
+dcb_outp_hashm(struct dcb_output *outp)
+{
+ return (outp->heads << 8) | (outp->link << 6) | outp->or;
+}
+
+u16
+dcb_outp_parse(struct nvkm_bios *bios, u8 idx, u8 *ver, u8 *len,
+ struct dcb_output *outp)
+{
+ u16 dcb = dcb_outp(bios, idx, ver, len);
+ memset(outp, 0x00, sizeof(*outp));
+ if (dcb) {
+ if (*ver >= 0x20) {
+ u32 conn = nv_ro32(bios, dcb + 0x00);
+ outp->or = (conn & 0x0f000000) >> 24;
+ outp->location = (conn & 0x00300000) >> 20;
+ outp->bus = (conn & 0x000f0000) >> 16;
+ outp->connector = (conn & 0x0000f000) >> 12;
+ outp->heads = (conn & 0x00000f00) >> 8;
+ outp->i2c_index = (conn & 0x000000f0) >> 4;
+ outp->type = (conn & 0x0000000f);
+ outp->link = 0;
+ } else {
+ dcb = 0x0000;
+ }
+
+ if (*ver >= 0x40) {
+ u32 conf = nv_ro32(bios, dcb + 0x04);
+ switch (outp->type) {
+ case DCB_OUTPUT_DP:
+ switch (conf & 0x00e00000) {
+ case 0x00000000:
+ outp->dpconf.link_bw = 0x06;
+ break;
+ case 0x00200000:
+ outp->dpconf.link_bw = 0x0a;
+ break;
+ case 0x00400000:
+ default:
+ outp->dpconf.link_bw = 0x14;
+ break;
+ }
+
+ outp->dpconf.link_nr = (conf & 0x0f000000) >> 24;
+ if (*ver < 0x41) {
+ switch (outp->dpconf.link_nr) {
+ case 0x0f:
+ outp->dpconf.link_nr = 4;
+ break;
+ case 0x03:
+ outp->dpconf.link_nr = 2;
+ break;
+ case 0x01:
+ default:
+ outp->dpconf.link_nr = 1;
+ break;
+ }
+ }
+
+ /* fall-through... */
+ case DCB_OUTPUT_TMDS:
+ case DCB_OUTPUT_LVDS:
+ outp->link = (conf & 0x00000030) >> 4;
+ outp->sorconf.link = outp->link; /*XXX*/
+ outp->extdev = 0x00;
+ if (outp->location != 0)
+ outp->extdev = (conf & 0x0000ff00) >> 8;
+ break;
+ default:
+ break;
+ }
+ }
+
+ outp->hasht = dcb_outp_hasht(outp);
+ outp->hashm = dcb_outp_hashm(outp);
+ }
+ return dcb;
+}
+
+u16
+dcb_outp_match(struct nvkm_bios *bios, u16 type, u16 mask,
+ u8 *ver, u8 *len, struct dcb_output *outp)
+{
+ u16 dcb, idx = 0;
+ while ((dcb = dcb_outp_parse(bios, idx++, ver, len, outp))) {
+ if ((dcb_outp_hasht(outp) & 0x00ff) == (type & 0x00ff)) {
+ if ((dcb_outp_hashm(outp) & mask) == mask)
+ break;
+ }
+ }
+ return dcb;
+}
+
+int
+dcb_outp_foreach(struct nvkm_bios *bios, void *data,
+ int (*exec)(struct nvkm_bios *, void *, int, u16))
+{
+ int ret, idx = -1;
+ u8 ver, len;
+ u16 outp;
+
+ while ((outp = dcb_outp(bios, ++idx, &ver, &len))) {
+ if (nv_ro32(bios, outp) == 0x00000000)
+ break; /* seen on an NV11 with DCB v1.5 */
+ if (nv_ro32(bios, outp) == 0xffffffff)
+ break; /* seen on an NV17 with DCB v2.0 */
+
+ if (nv_ro08(bios, outp) == DCB_OUTPUT_UNUSED)
+ continue;
+ if (nv_ro08(bios, outp) == DCB_OUTPUT_EOL)
+ break;
+
+ ret = exec(bios, data, idx, outp);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/disp.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/disp.c
new file mode 100644
index 000000000000..262c410b7ee2
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/disp.c
@@ -0,0 +1,172 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include <subdev/bios.h>
+#include <subdev/bios/bit.h>
+#include <subdev/bios/disp.h>
+
+u16
+nvbios_disp_table(struct nvkm_bios *bios,
+ u8 *ver, u8 *hdr, u8 *cnt, u8 *len, u8 *sub)
+{
+ struct bit_entry U;
+
+ if (!bit_entry(bios, 'U', &U)) {
+ if (U.version == 1) {
+ u16 data = nv_ro16(bios, U.offset);
+ if (data) {
+ *ver = nv_ro08(bios, data + 0x00);
+ switch (*ver) {
+ case 0x20:
+ case 0x21:
+ case 0x22:
+ *hdr = nv_ro08(bios, data + 0x01);
+ *len = nv_ro08(bios, data + 0x02);
+ *cnt = nv_ro08(bios, data + 0x03);
+ *sub = nv_ro08(bios, data + 0x04);
+ return data;
+ default:
+ break;
+ }
+ }
+ }
+ }
+
+ return 0x0000;
+}
+
+u16
+nvbios_disp_entry(struct nvkm_bios *bios, u8 idx, u8 *ver, u8 *len, u8 *sub)
+{
+ u8 hdr, cnt;
+ u16 data = nvbios_disp_table(bios, ver, &hdr, &cnt, len, sub);
+ if (data && idx < cnt)
+ return data + hdr + (idx * *len);
+ *ver = 0x00;
+ return 0x0000;
+}
+
+u16
+nvbios_disp_parse(struct nvkm_bios *bios, u8 idx, u8 *ver, u8 *len, u8 *sub,
+ struct nvbios_disp *info)
+{
+ u16 data = nvbios_disp_entry(bios, idx, ver, len, sub);
+ if (data && *len >= 2) {
+ info->data = nv_ro16(bios, data + 0);
+ return data;
+ }
+ return 0x0000;
+}
+
+u16
+nvbios_outp_entry(struct nvkm_bios *bios, u8 idx,
+ u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
+{
+ struct nvbios_disp info;
+ u16 data = nvbios_disp_parse(bios, idx, ver, len, hdr, &info);
+ if (data) {
+ *cnt = nv_ro08(bios, info.data + 0x05);
+ *len = 0x06;
+ data = info.data;
+ }
+ return data;
+}
+
+u16
+nvbios_outp_parse(struct nvkm_bios *bios, u8 idx,
+ u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_outp *info)
+{
+ u16 data = nvbios_outp_entry(bios, idx, ver, hdr, cnt, len);
+ if (data && *hdr >= 0x0a) {
+ info->type = nv_ro16(bios, data + 0x00);
+ info->mask = nv_ro32(bios, data + 0x02);
+ if (*ver <= 0x20) /* match any link */
+ info->mask |= 0x00c0;
+ info->script[0] = nv_ro16(bios, data + 0x06);
+ info->script[1] = nv_ro16(bios, data + 0x08);
+ info->script[2] = 0x0000;
+ if (*hdr >= 0x0c)
+ info->script[2] = nv_ro16(bios, data + 0x0a);
+ return data;
+ }
+ return 0x0000;
+}
+
+u16
+nvbios_outp_match(struct nvkm_bios *bios, u16 type, u16 mask,
+ u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_outp *info)
+{
+ u16 data, idx = 0;
+ while ((data = nvbios_outp_parse(bios, idx++, ver, hdr, cnt, len, info)) || *ver) {
+ if (data && info->type == type) {
+ if ((info->mask & mask) == mask)
+ break;
+ }
+ }
+ return data;
+}
+
+u16
+nvbios_ocfg_entry(struct nvkm_bios *bios, u16 outp, u8 idx,
+ u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
+{
+ if (idx < *cnt)
+ return outp + *hdr + (idx * *len);
+ return 0x0000;
+}
+
+u16
+nvbios_ocfg_parse(struct nvkm_bios *bios, u16 outp, u8 idx,
+ u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_ocfg *info)
+{
+ u16 data = nvbios_ocfg_entry(bios, outp, idx, ver, hdr, cnt, len);
+ if (data) {
+ info->match = nv_ro16(bios, data + 0x00);
+ info->clkcmp[0] = nv_ro16(bios, data + 0x02);
+ info->clkcmp[1] = nv_ro16(bios, data + 0x04);
+ }
+ return data;
+}
+
+u16
+nvbios_ocfg_match(struct nvkm_bios *bios, u16 outp, u16 type,
+ u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_ocfg *info)
+{
+ u16 data, idx = 0;
+ while ((data = nvbios_ocfg_parse(bios, outp, idx++, ver, hdr, cnt, len, info))) {
+ if (info->match == type)
+ break;
+ }
+ return data;
+}
+
+u16
+nvbios_oclk_match(struct nvkm_bios *bios, u16 cmp, u32 khz)
+{
+ while (cmp) {
+ if (khz / 10 >= nv_ro16(bios, cmp + 0x00))
+ return nv_ro16(bios, cmp + 0x02);
+ cmp += 0x04;
+ }
+ return 0x0000;
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/dp.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/dp.c
new file mode 100644
index 000000000000..95970faae6c8
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/dp.c
@@ -0,0 +1,215 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include <subdev/bios.h>
+#include <subdev/bios/bit.h>
+#include <subdev/bios/dp.h>
+
+static u16
+nvbios_dp_table(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
+{
+ struct bit_entry d;
+
+ if (!bit_entry(bios, 'd', &d)) {
+ if (d.version == 1 && d.length >= 2) {
+ u16 data = nv_ro16(bios, d.offset);
+ if (data) {
+ *ver = nv_ro08(bios, data + 0x00);
+ switch (*ver) {
+ case 0x21:
+ case 0x30:
+ case 0x40:
+ case 0x41:
+ *hdr = nv_ro08(bios, data + 0x01);
+ *len = nv_ro08(bios, data + 0x02);
+ *cnt = nv_ro08(bios, data + 0x03);
+ return data;
+ default:
+ break;
+ }
+ }
+ }
+ }
+
+ return 0x0000;
+}
+
+static u16
+nvbios_dpout_entry(struct nvkm_bios *bios, u8 idx,
+ u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
+{
+ u16 data = nvbios_dp_table(bios, ver, hdr, cnt, len);
+ if (data && idx < *cnt) {
+ u16 outp = nv_ro16(bios, data + *hdr + idx * *len);
+ switch (*ver * !!outp) {
+ case 0x21:
+ case 0x30:
+ *hdr = nv_ro08(bios, data + 0x04);
+ *len = nv_ro08(bios, data + 0x05);
+ *cnt = nv_ro08(bios, outp + 0x04);
+ break;
+ case 0x40:
+ case 0x41:
+ *hdr = nv_ro08(bios, data + 0x04);
+ *cnt = 0;
+ *len = 0;
+ break;
+ default:
+ break;
+ }
+ return outp;
+ }
+ *ver = 0x00;
+ return 0x0000;
+}
+
+u16
+nvbios_dpout_parse(struct nvkm_bios *bios, u8 idx,
+ u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
+ struct nvbios_dpout *info)
+{
+ u16 data = nvbios_dpout_entry(bios, idx, ver, hdr, cnt, len);
+ memset(info, 0x00, sizeof(*info));
+ if (data && *ver) {
+ info->type = nv_ro16(bios, data + 0x00);
+ info->mask = nv_ro16(bios, data + 0x02);
+ switch (*ver) {
+ case 0x21:
+ case 0x30:
+ info->flags = nv_ro08(bios, data + 0x05);
+ info->script[0] = nv_ro16(bios, data + 0x06);
+ info->script[1] = nv_ro16(bios, data + 0x08);
+ info->lnkcmp = nv_ro16(bios, data + 0x0a);
+ if (*len >= 0x0f) {
+ info->script[2] = nv_ro16(bios, data + 0x0c);
+ info->script[3] = nv_ro16(bios, data + 0x0e);
+ }
+ if (*len >= 0x11)
+ info->script[4] = nv_ro16(bios, data + 0x10);
+ break;
+ case 0x40:
+ case 0x41:
+ info->flags = nv_ro08(bios, data + 0x04);
+ info->script[0] = nv_ro16(bios, data + 0x05);
+ info->script[1] = nv_ro16(bios, data + 0x07);
+ info->lnkcmp = nv_ro16(bios, data + 0x09);
+ info->script[2] = nv_ro16(bios, data + 0x0b);
+ info->script[3] = nv_ro16(bios, data + 0x0d);
+ info->script[4] = nv_ro16(bios, data + 0x0f);
+ break;
+ default:
+ data = 0x0000;
+ break;
+ }
+ }
+ return data;
+}
+
+u16
+nvbios_dpout_match(struct nvkm_bios *bios, u16 type, u16 mask,
+ u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
+ struct nvbios_dpout *info)
+{
+ u16 data, idx = 0;
+ while ((data = nvbios_dpout_parse(bios, idx++, ver, hdr, cnt, len, info)) || *ver) {
+ if (data && info->type == type) {
+ if ((info->mask & mask) == mask)
+ break;
+ }
+ }
+ return data;
+}
+
+static u16
+nvbios_dpcfg_entry(struct nvkm_bios *bios, u16 outp, u8 idx,
+ u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
+{
+ if (*ver >= 0x40) {
+ outp = nvbios_dp_table(bios, ver, hdr, cnt, len);
+ *hdr = *hdr + (*len * * cnt);
+ *len = nv_ro08(bios, outp + 0x06);
+ *cnt = nv_ro08(bios, outp + 0x07);
+ }
+
+ if (idx < *cnt)
+ return outp + *hdr + (idx * *len);
+
+ return 0x0000;
+}
+
+u16
+nvbios_dpcfg_parse(struct nvkm_bios *bios, u16 outp, u8 idx,
+ u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
+ struct nvbios_dpcfg *info)
+{
+ u16 data = nvbios_dpcfg_entry(bios, outp, idx, ver, hdr, cnt, len);
+ memset(info, 0x00, sizeof(*info));
+ if (data) {
+ switch (*ver) {
+ case 0x21:
+ info->dc = nv_ro08(bios, data + 0x02);
+ info->pe = nv_ro08(bios, data + 0x03);
+ info->tx_pu = nv_ro08(bios, data + 0x04);
+ break;
+ case 0x30:
+ case 0x40:
+ case 0x41:
+ info->pc = nv_ro08(bios, data + 0x00);
+ info->dc = nv_ro08(bios, data + 0x01);
+ info->pe = nv_ro08(bios, data + 0x02);
+ info->tx_pu = nv_ro08(bios, data + 0x03) & 0x0f;
+ break;
+ default:
+ data = 0x0000;
+ break;
+ }
+ }
+ return data;
+}
+
+u16
+nvbios_dpcfg_match(struct nvkm_bios *bios, u16 outp, u8 pc, u8 vs, u8 pe,
+ u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
+ struct nvbios_dpcfg *info)
+{
+ u8 idx = 0xff;
+ u16 data;
+
+ if (*ver >= 0x30) {
+ /*XXX: there's a second set of these on at least 4.1, that
+ * i've witnessed nvidia using instead of the first
+ * on gm204. figure out what/why
+ */
+ const u8 vsoff[] = { 0, 4, 7, 9 };
+ idx = (pc * 10) + vsoff[vs] + pe;
+ } else {
+ while ((data = nvbios_dpcfg_entry(bios, outp, ++idx,
+ ver, hdr, cnt, len))) {
+ if (nv_ro08(bios, data + 0x00) == vs &&
+ nv_ro08(bios, data + 0x01) == pe)
+ break;
+ }
+ }
+
+ return nvbios_dpcfg_parse(bios, outp, idx, ver, hdr, cnt, len, info);
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/extdev.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/extdev.c
new file mode 100644
index 000000000000..a8503a1854c4
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/extdev.c
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2012 Nouveau Community
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Martin Peres
+ */
+#include <subdev/bios.h>
+#include <subdev/bios/dcb.h>
+#include <subdev/bios/extdev.h>
+
+static u16
+extdev_table(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *len, u8 *cnt)
+{
+ u8 dcb_ver, dcb_hdr, dcb_cnt, dcb_len;
+ u16 dcb, extdev = 0;
+
+ dcb = dcb_table(bios, &dcb_ver, &dcb_hdr, &dcb_cnt, &dcb_len);
+ if (!dcb || (dcb_ver != 0x30 && dcb_ver != 0x40))
+ return 0x0000;
+
+ extdev = nv_ro16(bios, dcb + 18);
+ if (!extdev)
+ return 0x0000;
+
+ *ver = nv_ro08(bios, extdev + 0);
+ *hdr = nv_ro08(bios, extdev + 1);
+ *cnt = nv_ro08(bios, extdev + 2);
+ *len = nv_ro08(bios, extdev + 3);
+ return extdev + *hdr;
+}
+
+static u16
+nvbios_extdev_entry(struct nvkm_bios *bios, int idx, u8 *ver, u8 *len)
+{
+ u8 hdr, cnt;
+ u16 extdev = extdev_table(bios, ver, &hdr, len, &cnt);
+ if (extdev && idx < cnt)
+ return extdev + idx * *len;
+ return 0x0000;
+}
+
+static void
+extdev_parse_entry(struct nvkm_bios *bios, u16 offset,
+ struct nvbios_extdev_func *entry)
+{
+ entry->type = nv_ro08(bios, offset + 0);
+ entry->addr = nv_ro08(bios, offset + 1);
+ entry->bus = (nv_ro08(bios, offset + 2) >> 4) & 1;
+}
+
+int
+nvbios_extdev_parse(struct nvkm_bios *bios, int idx,
+ struct nvbios_extdev_func *func)
+{
+ u8 ver, len;
+ u16 entry;
+
+ if (!(entry = nvbios_extdev_entry(bios, idx, &ver, &len)))
+ return -EINVAL;
+
+ extdev_parse_entry(bios, entry, func);
+ return 0;
+}
+
+int
+nvbios_extdev_find(struct nvkm_bios *bios, enum nvbios_extdev_type type,
+ struct nvbios_extdev_func *func)
+{
+ u8 ver, len, i;
+ u16 entry;
+
+ i = 0;
+ while ((entry = nvbios_extdev_entry(bios, i++, &ver, &len))) {
+ extdev_parse_entry(bios, entry, func);
+ if (func->type == type)
+ return 0;
+ }
+
+ return -EINVAL;
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/fan.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/fan.c
new file mode 100644
index 000000000000..8dba70d9d9a9
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/fan.c
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2014 Martin Peres
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Martin Peres
+ */
+#include <subdev/bios.h>
+#include <subdev/bios/bit.h>
+#include <subdev/bios/fan.h>
+
+u16
+nvbios_fan_table(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
+{
+ struct bit_entry bit_P;
+ u16 fan = 0x0000;
+
+ if (!bit_entry(bios, 'P', &bit_P)) {
+ if (bit_P.version == 2 && bit_P.length >= 0x5a)
+ fan = nv_ro16(bios, bit_P.offset + 0x58);
+
+ if (fan) {
+ *ver = nv_ro08(bios, fan + 0);
+ switch (*ver) {
+ case 0x10:
+ *hdr = nv_ro08(bios, fan + 1);
+ *len = nv_ro08(bios, fan + 2);
+ *cnt = nv_ro08(bios, fan + 3);
+ return fan;
+ default:
+ break;
+ }
+ }
+ }
+
+ return 0x0000;
+}
+
+u16
+nvbios_fan_entry(struct nvkm_bios *bios, int idx, u8 *ver, u8 *hdr,
+ u8 *cnt, u8 *len)
+{
+ u16 data = nvbios_fan_table(bios, ver, hdr, cnt, len);
+ if (data && idx < *cnt)
+ return data + *hdr + (idx * (*len));
+ return 0x0000;
+}
+
+u16
+nvbios_fan_parse(struct nvkm_bios *bios, struct nvbios_therm_fan *fan)
+{
+ u8 ver, hdr, cnt, len;
+
+ u16 data = nvbios_fan_entry(bios, 0, &ver, &hdr, &cnt, &len);
+ if (data) {
+ u8 type = nv_ro08(bios, data + 0x00);
+ switch (type) {
+ case 0:
+ fan->type = NVBIOS_THERM_FAN_TOGGLE;
+ break;
+ case 1:
+ case 2:
+ /* TODO: Understand the difference between the two! */
+ fan->type = NVBIOS_THERM_FAN_PWM;
+ break;
+ default:
+ fan->type = NVBIOS_THERM_FAN_UNK;
+ }
+
+ fan->min_duty = nv_ro08(bios, data + 0x02);
+ fan->max_duty = nv_ro08(bios, data + 0x03);
+
+ fan->pwm_freq = nv_ro32(bios, data + 0x0b) & 0xffffff;
+ }
+
+ return data;
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/gpio.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/gpio.c
new file mode 100644
index 000000000000..8ce154d88f51
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/gpio.c
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include <subdev/bios.h>
+#include <subdev/bios/dcb.h>
+#include <subdev/bios/gpio.h>
+#include <subdev/bios/xpio.h>
+
+u16
+dcb_gpio_table(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
+{
+ u16 data = 0x0000;
+ u16 dcb = dcb_table(bios, ver, hdr, cnt, len);
+ if (dcb) {
+ if (*ver >= 0x30 && *hdr >= 0x0c)
+ data = nv_ro16(bios, dcb + 0x0a);
+ else
+ if (*ver >= 0x22 && nv_ro08(bios, dcb - 1) >= 0x13)
+ data = nv_ro16(bios, dcb - 0x0f);
+
+ if (data) {
+ *ver = nv_ro08(bios, data + 0x00);
+ if (*ver < 0x30) {
+ *hdr = 3;
+ *cnt = nv_ro08(bios, data + 0x02);
+ *len = nv_ro08(bios, data + 0x01);
+ } else
+ if (*ver <= 0x41) {
+ *hdr = nv_ro08(bios, data + 0x01);
+ *cnt = nv_ro08(bios, data + 0x02);
+ *len = nv_ro08(bios, data + 0x03);
+ } else {
+ data = 0x0000;
+ }
+ }
+ }
+ return data;
+}
+
+u16
+dcb_gpio_entry(struct nvkm_bios *bios, int idx, int ent, u8 *ver, u8 *len)
+{
+ u8 hdr, cnt, xver; /* use gpio version for xpio entry parsing */
+ u16 gpio;
+
+ if (!idx--)
+ gpio = dcb_gpio_table(bios, ver, &hdr, &cnt, len);
+ else
+ gpio = dcb_xpio_table(bios, idx, &xver, &hdr, &cnt, len);
+
+ if (gpio && ent < cnt)
+ return gpio + hdr + (ent * *len);
+
+ return 0x0000;
+}
+
+u16
+dcb_gpio_parse(struct nvkm_bios *bios, int idx, int ent, u8 *ver, u8 *len,
+ struct dcb_gpio_func *gpio)
+{
+ u16 data = dcb_gpio_entry(bios, idx, ent, ver, len);
+ if (data) {
+ if (*ver < 0x40) {
+ u16 info = nv_ro16(bios, data);
+ *gpio = (struct dcb_gpio_func) {
+ .line = (info & 0x001f) >> 0,
+ .func = (info & 0x07e0) >> 5,
+ .log[0] = (info & 0x1800) >> 11,
+ .log[1] = (info & 0x6000) >> 13,
+ .param = !!(info & 0x8000),
+ };
+ } else
+ if (*ver < 0x41) {
+ u32 info = nv_ro32(bios, data);
+ *gpio = (struct dcb_gpio_func) {
+ .line = (info & 0x0000001f) >> 0,
+ .func = (info & 0x0000ff00) >> 8,
+ .log[0] = (info & 0x18000000) >> 27,
+ .log[1] = (info & 0x60000000) >> 29,
+ .param = !!(info & 0x80000000),
+ };
+ } else {
+ u32 info = nv_ro32(bios, data + 0);
+ u8 info1 = nv_ro32(bios, data + 4);
+ *gpio = (struct dcb_gpio_func) {
+ .line = (info & 0x0000003f) >> 0,
+ .func = (info & 0x0000ff00) >> 8,
+ .log[0] = (info1 & 0x30) >> 4,
+ .log[1] = (info1 & 0xc0) >> 6,
+ .param = !!(info & 0x80000000),
+ };
+ }
+ }
+
+ return data;
+}
+
+u16
+dcb_gpio_match(struct nvkm_bios *bios, int idx, u8 func, u8 line,
+ u8 *ver, u8 *len, struct dcb_gpio_func *gpio)
+{
+ u8 hdr, cnt, i = 0;
+ u16 data;
+
+ while ((data = dcb_gpio_parse(bios, idx, i++, ver, len, gpio))) {
+ if ((line == 0xff || line == gpio->line) &&
+ (func == 0xff || func == gpio->func))
+ return data;
+ }
+
+ /* DCB 2.2, fixed TVDAC GPIO data */
+ if ((data = dcb_table(bios, ver, &hdr, &cnt, len))) {
+ if (*ver >= 0x22 && *ver < 0x30 && func == DCB_GPIO_TVDAC0) {
+ u8 conf = nv_ro08(bios, data - 5);
+ u8 addr = nv_ro08(bios, data - 4);
+ if (conf & 0x01) {
+ *gpio = (struct dcb_gpio_func) {
+ .func = DCB_GPIO_TVDAC0,
+ .line = addr >> 4,
+ .log[0] = !!(conf & 0x02),
+ .log[1] = !(conf & 0x02),
+ };
+ *ver = 0x00;
+ return data;
+ }
+ }
+ }
+
+ return 0x0000;
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/i2c.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/i2c.c
new file mode 100644
index 000000000000..d1a89b2bd5c1
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/i2c.c
@@ -0,0 +1,159 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include <subdev/bios.h>
+#include <subdev/bios/dcb.h>
+#include <subdev/bios/i2c.h>
+
+u16
+dcb_i2c_table(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
+{
+ u16 i2c = 0x0000;
+ u16 dcb = dcb_table(bios, ver, hdr, cnt, len);
+ if (dcb) {
+ if (*ver >= 0x15)
+ i2c = nv_ro16(bios, dcb + 2);
+ if (*ver >= 0x30)
+ i2c = nv_ro16(bios, dcb + 4);
+ }
+
+ if (i2c && *ver >= 0x42) {
+ nv_warn(bios, "ccb %02x not supported\n", *ver);
+ return 0x0000;
+ }
+
+ if (i2c && *ver >= 0x30) {
+ *ver = nv_ro08(bios, i2c + 0);
+ *hdr = nv_ro08(bios, i2c + 1);
+ *cnt = nv_ro08(bios, i2c + 2);
+ *len = nv_ro08(bios, i2c + 3);
+ } else {
+ *ver = *ver; /* use DCB version */
+ *hdr = 0;
+ *cnt = 16;
+ *len = 4;
+ }
+
+ return i2c;
+}
+
+u16
+dcb_i2c_entry(struct nvkm_bios *bios, u8 idx, u8 *ver, u8 *len)
+{
+ u8 hdr, cnt;
+ u16 i2c = dcb_i2c_table(bios, ver, &hdr, &cnt, len);
+ if (i2c && idx < cnt)
+ return i2c + hdr + (idx * *len);
+ return 0x0000;
+}
+
+int
+dcb_i2c_parse(struct nvkm_bios *bios, u8 idx, struct dcb_i2c_entry *info)
+{
+ u8 ver, len;
+ u16 ent = dcb_i2c_entry(bios, idx, &ver, &len);
+ if (ent) {
+ if (ver >= 0x41) {
+ if (!(nv_ro32(bios, ent) & 0x80000000))
+ info->type = DCB_I2C_UNUSED;
+ else
+ info->type = DCB_I2C_PMGR;
+ } else
+ if (ver >= 0x30) {
+ info->type = nv_ro08(bios, ent + 0x03);
+ } else {
+ info->type = nv_ro08(bios, ent + 0x03) & 0x07;
+ if (info->type == 0x07)
+ info->type = DCB_I2C_UNUSED;
+ }
+
+ info->drive = DCB_I2C_UNUSED;
+ info->sense = DCB_I2C_UNUSED;
+ info->share = DCB_I2C_UNUSED;
+ info->auxch = DCB_I2C_UNUSED;
+
+ switch (info->type) {
+ case DCB_I2C_NV04_BIT:
+ info->drive = nv_ro08(bios, ent + 0);
+ info->sense = nv_ro08(bios, ent + 1);
+ return 0;
+ case DCB_I2C_NV4E_BIT:
+ info->drive = nv_ro08(bios, ent + 1);
+ return 0;
+ case DCB_I2C_NVIO_BIT:
+ info->drive = nv_ro08(bios, ent + 0) & 0x0f;
+ if (nv_ro08(bios, ent + 1) & 0x01)
+ info->share = nv_ro08(bios, ent + 1) >> 1;
+ return 0;
+ case DCB_I2C_NVIO_AUX:
+ info->auxch = nv_ro08(bios, ent + 0) & 0x0f;
+ if (nv_ro08(bios, ent + 1) & 0x01)
+ info->share = info->auxch;
+ return 0;
+ case DCB_I2C_PMGR:
+ info->drive = (nv_ro16(bios, ent + 0) & 0x01f) >> 0;
+ if (info->drive == 0x1f)
+ info->drive = DCB_I2C_UNUSED;
+ info->auxch = (nv_ro16(bios, ent + 0) & 0x3e0) >> 5;
+ if (info->auxch == 0x1f)
+ info->auxch = DCB_I2C_UNUSED;
+ info->share = info->auxch;
+ return 0;
+ case DCB_I2C_UNUSED:
+ return 0;
+ default:
+ nv_warn(bios, "unknown i2c type %d\n", info->type);
+ info->type = DCB_I2C_UNUSED;
+ return 0;
+ }
+ }
+
+ if (bios->bmp_offset && idx < 2) {
+ /* BMP (from v4.0 has i2c info in the structure, it's in a
+ * fixed location on earlier VBIOS
+ */
+ if (nv_ro08(bios, bios->bmp_offset + 5) < 4)
+ ent = 0x0048;
+ else
+ ent = 0x0036 + bios->bmp_offset;
+
+ if (idx == 0) {
+ info->drive = nv_ro08(bios, ent + 4);
+ if (!info->drive) info->drive = 0x3f;
+ info->sense = nv_ro08(bios, ent + 5);
+ if (!info->sense) info->sense = 0x3e;
+ } else
+ if (idx == 1) {
+ info->drive = nv_ro08(bios, ent + 6);
+ if (!info->drive) info->drive = 0x37;
+ info->sense = nv_ro08(bios, ent + 7);
+ if (!info->sense) info->sense = 0x36;
+ }
+
+ info->type = DCB_I2C_NV04_BIT;
+ info->share = DCB_I2C_UNUSED;
+ return 0;
+ }
+
+ return -ENOENT;
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/image.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/image.c
new file mode 100644
index 000000000000..1815540a0e8b
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/image.c
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2014 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs <bskeggs@redhat.com>
+ */
+#include <subdev/bios.h>
+#include <subdev/bios/image.h>
+#include <subdev/bios/pcir.h>
+#include <subdev/bios/npde.h>
+
+static bool
+nvbios_imagen(struct nvkm_bios *bios, struct nvbios_image *image)
+{
+ struct nvbios_pcirT pcir;
+ struct nvbios_npdeT npde;
+ u8 ver;
+ u16 hdr;
+ u32 data;
+
+ switch ((data = nv_ro16(bios, image->base + 0x00))) {
+ case 0xaa55:
+ case 0xbb77:
+ case 0x4e56: /* NV */
+ break;
+ default:
+ nv_debug(bios, "%08x: ROM signature (%04x) unknown\n",
+ image->base, data);
+ return false;
+ }
+
+ if (!(data = nvbios_pcirTp(bios, image->base, &ver, &hdr, &pcir)))
+ return false;
+ image->size = pcir.image_size;
+ image->type = pcir.image_type;
+ image->last = pcir.last;
+
+ if (image->type != 0x70) {
+ if (!(data = nvbios_npdeTp(bios, image->base, &npde)))
+ return true;
+ image->size = npde.image_size;
+ image->last = npde.last;
+ } else {
+ image->last = true;
+ }
+
+ return true;
+}
+
+bool
+nvbios_image(struct nvkm_bios *bios, int idx, struct nvbios_image *image)
+{
+ memset(image, 0x00, sizeof(*image));
+ do {
+ image->base += image->size;
+ if (image->last || !nvbios_imagen(bios, image))
+ return false;
+ } while(idx--);
+ return true;
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/init.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/init.c
new file mode 100644
index 000000000000..f67cdae1e90a
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/init.c
@@ -0,0 +1,2247 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include <subdev/bios.h>
+#include <subdev/bios/bit.h>
+#include <subdev/bios/bmp.h>
+#include <subdev/bios/conn.h>
+#include <subdev/bios/dcb.h>
+#include <subdev/bios/dp.h>
+#include <subdev/bios/gpio.h>
+#include <subdev/bios/init.h>
+#include <subdev/bios/ramcfg.h>
+
+#include <core/device.h>
+#include <subdev/devinit.h>
+#include <subdev/gpio.h>
+#include <subdev/i2c.h>
+#include <subdev/vga.h>
+
+#define bioslog(lvl, fmt, args...) do { \
+ nv_printk(init->bios, lvl, "0x%04x[%c]: "fmt, init->offset, \
+ init_exec(init) ? '0' + (init->nested - 1) : ' ', ##args); \
+} while(0)
+#define cont(fmt, args...) do { \
+ if (nv_subdev(init->bios)->debug >= NV_DBG_TRACE) \
+ printk(fmt, ##args); \
+} while(0)
+#define trace(fmt, args...) bioslog(TRACE, fmt, ##args)
+#define warn(fmt, args...) bioslog(WARN, fmt, ##args)
+#define error(fmt, args...) bioslog(ERROR, fmt, ##args)
+
+/******************************************************************************
+ * init parser control flow helpers
+ *****************************************************************************/
+
+static inline bool
+init_exec(struct nvbios_init *init)
+{
+ return (init->execute == 1) || ((init->execute & 5) == 5);
+}
+
+static inline void
+init_exec_set(struct nvbios_init *init, bool exec)
+{
+ if (exec) init->execute &= 0xfd;
+ else init->execute |= 0x02;
+}
+
+static inline void
+init_exec_inv(struct nvbios_init *init)
+{
+ init->execute ^= 0x02;
+}
+
+static inline void
+init_exec_force(struct nvbios_init *init, bool exec)
+{
+ if (exec) init->execute |= 0x04;
+ else init->execute &= 0xfb;
+}
+
+/******************************************************************************
+ * init parser wrappers for normal register/i2c/whatever accessors
+ *****************************************************************************/
+
+static inline int
+init_or(struct nvbios_init *init)
+{
+ if (init_exec(init)) {
+ if (init->outp)
+ return ffs(init->outp->or) - 1;
+ error("script needs OR!!\n");
+ }
+ return 0;
+}
+
+static inline int
+init_link(struct nvbios_init *init)
+{
+ if (init_exec(init)) {
+ if (init->outp)
+ return !(init->outp->sorconf.link & 1);
+ error("script needs OR link\n");
+ }
+ return 0;
+}
+
+static inline int
+init_crtc(struct nvbios_init *init)
+{
+ if (init_exec(init)) {
+ if (init->crtc >= 0)
+ return init->crtc;
+ error("script needs crtc\n");
+ }
+ return 0;
+}
+
+static u8
+init_conn(struct nvbios_init *init)
+{
+ struct nvkm_bios *bios = init->bios;
+ struct nvbios_connE connE;
+ u8 ver, hdr;
+ u32 conn;
+
+ if (init_exec(init)) {
+ if (init->outp) {
+ conn = init->outp->connector;
+ conn = nvbios_connEp(bios, conn, &ver, &hdr, &connE);
+ if (conn)
+ return connE.type;
+ }
+
+ error("script needs connector type\n");
+ }
+
+ return 0xff;
+}
+
+static inline u32
+init_nvreg(struct nvbios_init *init, u32 reg)
+{
+ struct nvkm_devinit *devinit = nvkm_devinit(init->bios);
+
+ /* C51 (at least) sometimes has the lower bits set which the VBIOS
+ * interprets to mean that access needs to go through certain IO
+ * ports instead. The NVIDIA binary driver has been seen to access
+ * these through the NV register address, so lets assume we can
+ * do the same
+ */
+ reg &= ~0x00000003;
+
+ /* GF8+ display scripts need register addresses mangled a bit to
+ * select a specific CRTC/OR
+ */
+ if (nv_device(init->bios)->card_type >= NV_50) {
+ if (reg & 0x80000000) {
+ reg += init_crtc(init) * 0x800;
+ reg &= ~0x80000000;
+ }
+
+ if (reg & 0x40000000) {
+ reg += init_or(init) * 0x800;
+ reg &= ~0x40000000;
+ if (reg & 0x20000000) {
+ reg += init_link(init) * 0x80;
+ reg &= ~0x20000000;
+ }
+ }
+ }
+
+ if (reg & ~0x00fffffc)
+ warn("unknown bits in register 0x%08x\n", reg);
+
+ if (devinit->mmio)
+ reg = devinit->mmio(devinit, reg);
+ return reg;
+}
+
+static u32
+init_rd32(struct nvbios_init *init, u32 reg)
+{
+ reg = init_nvreg(init, reg);
+ if (reg != ~0 && init_exec(init))
+ return nv_rd32(init->subdev, reg);
+ return 0x00000000;
+}
+
+static void
+init_wr32(struct nvbios_init *init, u32 reg, u32 val)
+{
+ reg = init_nvreg(init, reg);
+ if (reg != ~0 && init_exec(init))
+ nv_wr32(init->subdev, reg, val);
+}
+
+static u32
+init_mask(struct nvbios_init *init, u32 reg, u32 mask, u32 val)
+{
+ reg = init_nvreg(init, reg);
+ if (reg != ~0 && init_exec(init)) {
+ u32 tmp = nv_rd32(init->subdev, reg);
+ nv_wr32(init->subdev, reg, (tmp & ~mask) | val);
+ return tmp;
+ }
+ return 0x00000000;
+}
+
+static u8
+init_rdport(struct nvbios_init *init, u16 port)
+{
+ if (init_exec(init))
+ return nv_rdport(init->subdev, init->crtc, port);
+ return 0x00;
+}
+
+static void
+init_wrport(struct nvbios_init *init, u16 port, u8 value)
+{
+ if (init_exec(init))
+ nv_wrport(init->subdev, init->crtc, port, value);
+}
+
+static u8
+init_rdvgai(struct nvbios_init *init, u16 port, u8 index)
+{
+ struct nvkm_subdev *subdev = init->subdev;
+ if (init_exec(init)) {
+ int head = init->crtc < 0 ? 0 : init->crtc;
+ return nv_rdvgai(subdev, head, port, index);
+ }
+ return 0x00;
+}
+
+static void
+init_wrvgai(struct nvbios_init *init, u16 port, u8 index, u8 value)
+{
+ /* force head 0 for updates to cr44, it only exists on first head */
+ if (nv_device(init->subdev)->card_type < NV_50) {
+ if (port == 0x03d4 && index == 0x44)
+ init->crtc = 0;
+ }
+
+ if (init_exec(init)) {
+ int head = init->crtc < 0 ? 0 : init->crtc;
+ nv_wrvgai(init->subdev, head, port, index, value);
+ }
+
+ /* select head 1 if cr44 write selected it */
+ if (nv_device(init->subdev)->card_type < NV_50) {
+ if (port == 0x03d4 && index == 0x44 && value == 3)
+ init->crtc = 1;
+ }
+}
+
+static struct nvkm_i2c_port *
+init_i2c(struct nvbios_init *init, int index)
+{
+ struct nvkm_i2c *i2c = nvkm_i2c(init->bios);
+
+ if (index == 0xff) {
+ index = NV_I2C_DEFAULT(0);
+ if (init->outp && init->outp->i2c_upper_default)
+ index = NV_I2C_DEFAULT(1);
+ } else
+ if (index < 0) {
+ if (!init->outp) {
+ if (init_exec(init))
+ error("script needs output for i2c\n");
+ return NULL;
+ }
+
+ if (index == -2 && init->outp->location) {
+ index = NV_I2C_TYPE_EXTAUX(init->outp->extdev);
+ return i2c->find_type(i2c, index);
+ }
+
+ index = init->outp->i2c_index;
+ if (init->outp->type == DCB_OUTPUT_DP)
+ index += NV_I2C_AUX(0);
+ }
+
+ return i2c->find(i2c, index);
+}
+
+static int
+init_rdi2cr(struct nvbios_init *init, u8 index, u8 addr, u8 reg)
+{
+ struct nvkm_i2c_port *port = init_i2c(init, index);
+ if (port && init_exec(init))
+ return nv_rdi2cr(port, addr, reg);
+ return -ENODEV;
+}
+
+static int
+init_wri2cr(struct nvbios_init *init, u8 index, u8 addr, u8 reg, u8 val)
+{
+ struct nvkm_i2c_port *port = init_i2c(init, index);
+ if (port && init_exec(init))
+ return nv_wri2cr(port, addr, reg, val);
+ return -ENODEV;
+}
+
+static u8
+init_rdauxr(struct nvbios_init *init, u32 addr)
+{
+ struct nvkm_i2c_port *port = init_i2c(init, -2);
+ u8 data;
+
+ if (port && init_exec(init)) {
+ int ret = nv_rdaux(port, addr, &data, 1);
+ if (ret == 0)
+ return data;
+ trace("auxch read failed with %d\n", ret);
+ }
+
+ return 0x00;
+}
+
+static int
+init_wrauxr(struct nvbios_init *init, u32 addr, u8 data)
+{
+ struct nvkm_i2c_port *port = init_i2c(init, -2);
+ if (port && init_exec(init)) {
+ int ret = nv_wraux(port, addr, &data, 1);
+ if (ret)
+ trace("auxch write failed with %d\n", ret);
+ return ret;
+ }
+ return -ENODEV;
+}
+
+static void
+init_prog_pll(struct nvbios_init *init, u32 id, u32 freq)
+{
+ struct nvkm_devinit *devinit = nvkm_devinit(init->bios);
+ if (devinit->pll_set && init_exec(init)) {
+ int ret = devinit->pll_set(devinit, id, freq);
+ if (ret)
+ warn("failed to prog pll 0x%08x to %dkHz\n", id, freq);
+ }
+}
+
+/******************************************************************************
+ * parsing of bios structures that are required to execute init tables
+ *****************************************************************************/
+
+static u16
+init_table(struct nvkm_bios *bios, u16 *len)
+{
+ struct bit_entry bit_I;
+
+ if (!bit_entry(bios, 'I', &bit_I)) {
+ *len = bit_I.length;
+ return bit_I.offset;
+ }
+
+ if (bmp_version(bios) >= 0x0510) {
+ *len = 14;
+ return bios->bmp_offset + 75;
+ }
+
+ return 0x0000;
+}
+
+static u16
+init_table_(struct nvbios_init *init, u16 offset, const char *name)
+{
+ struct nvkm_bios *bios = init->bios;
+ u16 len, data = init_table(bios, &len);
+ if (data) {
+ if (len >= offset + 2) {
+ data = nv_ro16(bios, data + offset);
+ if (data)
+ return data;
+
+ warn("%s pointer invalid\n", name);
+ return 0x0000;
+ }
+
+ warn("init data too short for %s pointer", name);
+ return 0x0000;
+ }
+
+ warn("init data not found\n");
+ return 0x0000;
+}
+
+#define init_script_table(b) init_table_((b), 0x00, "script table")
+#define init_macro_index_table(b) init_table_((b), 0x02, "macro index table")
+#define init_macro_table(b) init_table_((b), 0x04, "macro table")
+#define init_condition_table(b) init_table_((b), 0x06, "condition table")
+#define init_io_condition_table(b) init_table_((b), 0x08, "io condition table")
+#define init_io_flag_condition_table(b) init_table_((b), 0x0a, "io flag conditon table")
+#define init_function_table(b) init_table_((b), 0x0c, "function table")
+#define init_xlat_table(b) init_table_((b), 0x10, "xlat table");
+
+static u16
+init_script(struct nvkm_bios *bios, int index)
+{
+ struct nvbios_init init = { .bios = bios };
+ u16 bmp_ver = bmp_version(bios), data;
+
+ if (bmp_ver && bmp_ver < 0x0510) {
+ if (index > 1 || bmp_ver < 0x0100)
+ return 0x0000;
+
+ data = bios->bmp_offset + (bmp_ver < 0x0200 ? 14 : 18);
+ return nv_ro16(bios, data + (index * 2));
+ }
+
+ data = init_script_table(&init);
+ if (data)
+ return nv_ro16(bios, data + (index * 2));
+
+ return 0x0000;
+}
+
+static u16
+init_unknown_script(struct nvkm_bios *bios)
+{
+ u16 len, data = init_table(bios, &len);
+ if (data && len >= 16)
+ return nv_ro16(bios, data + 14);
+ return 0x0000;
+}
+
+static u8
+init_ram_restrict_group_count(struct nvbios_init *init)
+{
+ return nvbios_ramcfg_count(init->bios);
+}
+
+static u8
+init_ram_restrict(struct nvbios_init *init)
+{
+ /* This appears to be the behaviour of the VBIOS parser, and *is*
+ * important to cache the NV_PEXTDEV_BOOT0 on later chipsets to
+ * avoid fucking up the memory controller (somehow) by reading it
+ * on every INIT_RAM_RESTRICT_ZM_GROUP opcode.
+ *
+ * Preserving the non-caching behaviour on earlier chipsets just
+ * in case *not* re-reading the strap causes similar breakage.
+ */
+ if (!init->ramcfg || init->bios->version.major < 0x70)
+ init->ramcfg = 0x80000000 | nvbios_ramcfg_index(init->subdev);
+ return (init->ramcfg & 0x7fffffff);
+}
+
+static u8
+init_xlat_(struct nvbios_init *init, u8 index, u8 offset)
+{
+ struct nvkm_bios *bios = init->bios;
+ u16 table = init_xlat_table(init);
+ if (table) {
+ u16 data = nv_ro16(bios, table + (index * 2));
+ if (data)
+ return nv_ro08(bios, data + offset);
+ warn("xlat table pointer %d invalid\n", index);
+ }
+ return 0x00;
+}
+
+/******************************************************************************
+ * utility functions used by various init opcode handlers
+ *****************************************************************************/
+
+static bool
+init_condition_met(struct nvbios_init *init, u8 cond)
+{
+ struct nvkm_bios *bios = init->bios;
+ u16 table = init_condition_table(init);
+ if (table) {
+ u32 reg = nv_ro32(bios, table + (cond * 12) + 0);
+ u32 msk = nv_ro32(bios, table + (cond * 12) + 4);
+ u32 val = nv_ro32(bios, table + (cond * 12) + 8);
+ trace("\t[0x%02x] (R[0x%06x] & 0x%08x) == 0x%08x\n",
+ cond, reg, msk, val);
+ return (init_rd32(init, reg) & msk) == val;
+ }
+ return false;
+}
+
+static bool
+init_io_condition_met(struct nvbios_init *init, u8 cond)
+{
+ struct nvkm_bios *bios = init->bios;
+ u16 table = init_io_condition_table(init);
+ if (table) {
+ u16 port = nv_ro16(bios, table + (cond * 5) + 0);
+ u8 index = nv_ro08(bios, table + (cond * 5) + 2);
+ u8 mask = nv_ro08(bios, table + (cond * 5) + 3);
+ u8 value = nv_ro08(bios, table + (cond * 5) + 4);
+ trace("\t[0x%02x] (0x%04x[0x%02x] & 0x%02x) == 0x%02x\n",
+ cond, port, index, mask, value);
+ return (init_rdvgai(init, port, index) & mask) == value;
+ }
+ return false;
+}
+
+static bool
+init_io_flag_condition_met(struct nvbios_init *init, u8 cond)
+{
+ struct nvkm_bios *bios = init->bios;
+ u16 table = init_io_flag_condition_table(init);
+ if (table) {
+ u16 port = nv_ro16(bios, table + (cond * 9) + 0);
+ u8 index = nv_ro08(bios, table + (cond * 9) + 2);
+ u8 mask = nv_ro08(bios, table + (cond * 9) + 3);
+ u8 shift = nv_ro08(bios, table + (cond * 9) + 4);
+ u16 data = nv_ro16(bios, table + (cond * 9) + 5);
+ u8 dmask = nv_ro08(bios, table + (cond * 9) + 7);
+ u8 value = nv_ro08(bios, table + (cond * 9) + 8);
+ u8 ioval = (init_rdvgai(init, port, index) & mask) >> shift;
+ return (nv_ro08(bios, data + ioval) & dmask) == value;
+ }
+ return false;
+}
+
+static inline u32
+init_shift(u32 data, u8 shift)
+{
+ if (shift < 0x80)
+ return data >> shift;
+ return data << (0x100 - shift);
+}
+
+static u32
+init_tmds_reg(struct nvbios_init *init, u8 tmds)
+{
+ /* For mlv < 0x80, it is an index into a table of TMDS base addresses.
+ * For mlv == 0x80 use the "or" value of the dcb_entry indexed by
+ * CR58 for CR57 = 0 to index a table of offsets to the basic
+ * 0x6808b0 address.
+ * For mlv == 0x81 use the "or" value of the dcb_entry indexed by
+ * CR58 for CR57 = 0 to index a table of offsets to the basic
+ * 0x6808b0 address, and then flip the offset by 8.
+ */
+ const int pramdac_offset[13] = {
+ 0, 0, 0x8, 0, 0x2000, 0, 0, 0, 0x2008, 0, 0, 0, 0x2000 };
+ const u32 pramdac_table[4] = {
+ 0x6808b0, 0x6808b8, 0x6828b0, 0x6828b8 };
+
+ if (tmds >= 0x80) {
+ if (init->outp) {
+ u32 dacoffset = pramdac_offset[init->outp->or];
+ if (tmds == 0x81)
+ dacoffset ^= 8;
+ return 0x6808b0 + dacoffset;
+ }
+
+ if (init_exec(init))
+ error("tmds opcodes need dcb\n");
+ } else {
+ if (tmds < ARRAY_SIZE(pramdac_table))
+ return pramdac_table[tmds];
+
+ error("tmds selector 0x%02x unknown\n", tmds);
+ }
+
+ return 0;
+}
+
+/******************************************************************************
+ * init opcode handlers
+ *****************************************************************************/
+
+/**
+ * init_reserved - stub for various unknown/unused single-byte opcodes
+ *
+ */
+static void
+init_reserved(struct nvbios_init *init)
+{
+ u8 opcode = nv_ro08(init->bios, init->offset);
+ u8 length, i;
+
+ switch (opcode) {
+ case 0xaa:
+ length = 4;
+ break;
+ default:
+ length = 1;
+ break;
+ }
+
+ trace("RESERVED 0x%02x\t", opcode);
+ for (i = 1; i < length; i++)
+ cont(" 0x%02x", nv_ro08(init->bios, init->offset + i));
+ cont("\n");
+ init->offset += length;
+}
+
+/**
+ * INIT_DONE - opcode 0x71
+ *
+ */
+static void
+init_done(struct nvbios_init *init)
+{
+ trace("DONE\n");
+ init->offset = 0x0000;
+}
+
+/**
+ * INIT_IO_RESTRICT_PROG - opcode 0x32
+ *
+ */
+static void
+init_io_restrict_prog(struct nvbios_init *init)
+{
+ struct nvkm_bios *bios = init->bios;
+ u16 port = nv_ro16(bios, init->offset + 1);
+ u8 index = nv_ro08(bios, init->offset + 3);
+ u8 mask = nv_ro08(bios, init->offset + 4);
+ u8 shift = nv_ro08(bios, init->offset + 5);
+ u8 count = nv_ro08(bios, init->offset + 6);
+ u32 reg = nv_ro32(bios, init->offset + 7);
+ u8 conf, i;
+
+ trace("IO_RESTRICT_PROG\tR[0x%06x] = "
+ "((0x%04x[0x%02x] & 0x%02x) >> %d) [{\n",
+ reg, port, index, mask, shift);
+ init->offset += 11;
+
+ conf = (init_rdvgai(init, port, index) & mask) >> shift;
+ for (i = 0; i < count; i++) {
+ u32 data = nv_ro32(bios, init->offset);
+
+ if (i == conf) {
+ trace("\t0x%08x *\n", data);
+ init_wr32(init, reg, data);
+ } else {
+ trace("\t0x%08x\n", data);
+ }
+
+ init->offset += 4;
+ }
+ trace("}]\n");
+}
+
+/**
+ * INIT_REPEAT - opcode 0x33
+ *
+ */
+static void
+init_repeat(struct nvbios_init *init)
+{
+ struct nvkm_bios *bios = init->bios;
+ u8 count = nv_ro08(bios, init->offset + 1);
+ u16 repeat = init->repeat;
+
+ trace("REPEAT\t0x%02x\n", count);
+ init->offset += 2;
+
+ init->repeat = init->offset;
+ init->repend = init->offset;
+ while (count--) {
+ init->offset = init->repeat;
+ nvbios_exec(init);
+ if (count)
+ trace("REPEAT\t0x%02x\n", count);
+ }
+ init->offset = init->repend;
+ init->repeat = repeat;
+}
+
+/**
+ * INIT_IO_RESTRICT_PLL - opcode 0x34
+ *
+ */
+static void
+init_io_restrict_pll(struct nvbios_init *init)
+{
+ struct nvkm_bios *bios = init->bios;
+ u16 port = nv_ro16(bios, init->offset + 1);
+ u8 index = nv_ro08(bios, init->offset + 3);
+ u8 mask = nv_ro08(bios, init->offset + 4);
+ u8 shift = nv_ro08(bios, init->offset + 5);
+ s8 iofc = nv_ro08(bios, init->offset + 6);
+ u8 count = nv_ro08(bios, init->offset + 7);
+ u32 reg = nv_ro32(bios, init->offset + 8);
+ u8 conf, i;
+
+ trace("IO_RESTRICT_PLL\tR[0x%06x] =PLL= "
+ "((0x%04x[0x%02x] & 0x%02x) >> 0x%02x) IOFCOND 0x%02x [{\n",
+ reg, port, index, mask, shift, iofc);
+ init->offset += 12;
+
+ conf = (init_rdvgai(init, port, index) & mask) >> shift;
+ for (i = 0; i < count; i++) {
+ u32 freq = nv_ro16(bios, init->offset) * 10;
+
+ if (i == conf) {
+ trace("\t%dkHz *\n", freq);
+ if (iofc > 0 && init_io_flag_condition_met(init, iofc))
+ freq *= 2;
+ init_prog_pll(init, reg, freq);
+ } else {
+ trace("\t%dkHz\n", freq);
+ }
+
+ init->offset += 2;
+ }
+ trace("}]\n");
+}
+
+/**
+ * INIT_END_REPEAT - opcode 0x36
+ *
+ */
+static void
+init_end_repeat(struct nvbios_init *init)
+{
+ trace("END_REPEAT\n");
+ init->offset += 1;
+
+ if (init->repeat) {
+ init->repend = init->offset;
+ init->offset = 0;
+ }
+}
+
+/**
+ * INIT_COPY - opcode 0x37
+ *
+ */
+static void
+init_copy(struct nvbios_init *init)
+{
+ struct nvkm_bios *bios = init->bios;
+ u32 reg = nv_ro32(bios, init->offset + 1);
+ u8 shift = nv_ro08(bios, init->offset + 5);
+ u8 smask = nv_ro08(bios, init->offset + 6);
+ u16 port = nv_ro16(bios, init->offset + 7);
+ u8 index = nv_ro08(bios, init->offset + 9);
+ u8 mask = nv_ro08(bios, init->offset + 10);
+ u8 data;
+
+ trace("COPY\t0x%04x[0x%02x] &= 0x%02x |= "
+ "((R[0x%06x] %s 0x%02x) & 0x%02x)\n",
+ port, index, mask, reg, (shift & 0x80) ? "<<" : ">>",
+ (shift & 0x80) ? (0x100 - shift) : shift, smask);
+ init->offset += 11;
+
+ data = init_rdvgai(init, port, index) & mask;
+ data |= init_shift(init_rd32(init, reg), shift) & smask;
+ init_wrvgai(init, port, index, data);
+}
+
+/**
+ * INIT_NOT - opcode 0x38
+ *
+ */
+static void
+init_not(struct nvbios_init *init)
+{
+ trace("NOT\n");
+ init->offset += 1;
+ init_exec_inv(init);
+}
+
+/**
+ * INIT_IO_FLAG_CONDITION - opcode 0x39
+ *
+ */
+static void
+init_io_flag_condition(struct nvbios_init *init)
+{
+ struct nvkm_bios *bios = init->bios;
+ u8 cond = nv_ro08(bios, init->offset + 1);
+
+ trace("IO_FLAG_CONDITION\t0x%02x\n", cond);
+ init->offset += 2;
+
+ if (!init_io_flag_condition_met(init, cond))
+ init_exec_set(init, false);
+}
+
+/**
+ * INIT_DP_CONDITION - opcode 0x3a
+ *
+ */
+static void
+init_dp_condition(struct nvbios_init *init)
+{
+ struct nvkm_bios *bios = init->bios;
+ struct nvbios_dpout info;
+ u8 cond = nv_ro08(bios, init->offset + 1);
+ u8 unkn = nv_ro08(bios, init->offset + 2);
+ u8 ver, hdr, cnt, len;
+ u16 data;
+
+ trace("DP_CONDITION\t0x%02x 0x%02x\n", cond, unkn);
+ init->offset += 3;
+
+ switch (cond) {
+ case 0:
+ if (init_conn(init) != DCB_CONNECTOR_eDP)
+ init_exec_set(init, false);
+ break;
+ case 1:
+ case 2:
+ if ( init->outp &&
+ (data = nvbios_dpout_match(bios, DCB_OUTPUT_DP,
+ (init->outp->or << 0) |
+ (init->outp->sorconf.link << 6),
+ &ver, &hdr, &cnt, &len, &info)))
+ {
+ if (!(info.flags & cond))
+ init_exec_set(init, false);
+ break;
+ }
+
+ if (init_exec(init))
+ warn("script needs dp output table data\n");
+ break;
+ case 5:
+ if (!(init_rdauxr(init, 0x0d) & 1))
+ init_exec_set(init, false);
+ break;
+ default:
+ warn("unknown dp condition 0x%02x\n", cond);
+ break;
+ }
+}
+
+/**
+ * INIT_IO_MASK_OR - opcode 0x3b
+ *
+ */
+static void
+init_io_mask_or(struct nvbios_init *init)
+{
+ struct nvkm_bios *bios = init->bios;
+ u8 index = nv_ro08(bios, init->offset + 1);
+ u8 or = init_or(init);
+ u8 data;
+
+ trace("IO_MASK_OR\t0x03d4[0x%02x] &= ~(1 << 0x%02x)\n", index, or);
+ init->offset += 2;
+
+ data = init_rdvgai(init, 0x03d4, index);
+ init_wrvgai(init, 0x03d4, index, data &= ~(1 << or));
+}
+
+/**
+ * INIT_IO_OR - opcode 0x3c
+ *
+ */
+static void
+init_io_or(struct nvbios_init *init)
+{
+ struct nvkm_bios *bios = init->bios;
+ u8 index = nv_ro08(bios, init->offset + 1);
+ u8 or = init_or(init);
+ u8 data;
+
+ trace("IO_OR\t0x03d4[0x%02x] |= (1 << 0x%02x)\n", index, or);
+ init->offset += 2;
+
+ data = init_rdvgai(init, 0x03d4, index);
+ init_wrvgai(init, 0x03d4, index, data | (1 << or));
+}
+
+/**
+ * INIT_ANDN_REG - opcode 0x47
+ *
+ */
+static void
+init_andn_reg(struct nvbios_init *init)
+{
+ struct nvkm_bios *bios = init->bios;
+ u32 reg = nv_ro32(bios, init->offset + 1);
+ u32 mask = nv_ro32(bios, init->offset + 5);
+
+ trace("ANDN_REG\tR[0x%06x] &= ~0x%08x\n", reg, mask);
+ init->offset += 9;
+
+ init_mask(init, reg, mask, 0);
+}
+
+/**
+ * INIT_OR_REG - opcode 0x48
+ *
+ */
+static void
+init_or_reg(struct nvbios_init *init)
+{
+ struct nvkm_bios *bios = init->bios;
+ u32 reg = nv_ro32(bios, init->offset + 1);
+ u32 mask = nv_ro32(bios, init->offset + 5);
+
+ trace("OR_REG\tR[0x%06x] |= 0x%08x\n", reg, mask);
+ init->offset += 9;
+
+ init_mask(init, reg, 0, mask);
+}
+
+/**
+ * INIT_INDEX_ADDRESS_LATCHED - opcode 0x49
+ *
+ */
+static void
+init_idx_addr_latched(struct nvbios_init *init)
+{
+ struct nvkm_bios *bios = init->bios;
+ u32 creg = nv_ro32(bios, init->offset + 1);
+ u32 dreg = nv_ro32(bios, init->offset + 5);
+ u32 mask = nv_ro32(bios, init->offset + 9);
+ u32 data = nv_ro32(bios, init->offset + 13);
+ u8 count = nv_ro08(bios, init->offset + 17);
+
+ trace("INDEX_ADDRESS_LATCHED\tR[0x%06x] : R[0x%06x]\n", creg, dreg);
+ trace("\tCTRL &= 0x%08x |= 0x%08x\n", mask, data);
+ init->offset += 18;
+
+ while (count--) {
+ u8 iaddr = nv_ro08(bios, init->offset + 0);
+ u8 idata = nv_ro08(bios, init->offset + 1);
+
+ trace("\t[0x%02x] = 0x%02x\n", iaddr, idata);
+ init->offset += 2;
+
+ init_wr32(init, dreg, idata);
+ init_mask(init, creg, ~mask, data | iaddr);
+ }
+}
+
+/**
+ * INIT_IO_RESTRICT_PLL2 - opcode 0x4a
+ *
+ */
+static void
+init_io_restrict_pll2(struct nvbios_init *init)
+{
+ struct nvkm_bios *bios = init->bios;
+ u16 port = nv_ro16(bios, init->offset + 1);
+ u8 index = nv_ro08(bios, init->offset + 3);
+ u8 mask = nv_ro08(bios, init->offset + 4);
+ u8 shift = nv_ro08(bios, init->offset + 5);
+ u8 count = nv_ro08(bios, init->offset + 6);
+ u32 reg = nv_ro32(bios, init->offset + 7);
+ u8 conf, i;
+
+ trace("IO_RESTRICT_PLL2\t"
+ "R[0x%06x] =PLL= ((0x%04x[0x%02x] & 0x%02x) >> 0x%02x) [{\n",
+ reg, port, index, mask, shift);
+ init->offset += 11;
+
+ conf = (init_rdvgai(init, port, index) & mask) >> shift;
+ for (i = 0; i < count; i++) {
+ u32 freq = nv_ro32(bios, init->offset);
+ if (i == conf) {
+ trace("\t%dkHz *\n", freq);
+ init_prog_pll(init, reg, freq);
+ } else {
+ trace("\t%dkHz\n", freq);
+ }
+ init->offset += 4;
+ }
+ trace("}]\n");
+}
+
+/**
+ * INIT_PLL2 - opcode 0x4b
+ *
+ */
+static void
+init_pll2(struct nvbios_init *init)
+{
+ struct nvkm_bios *bios = init->bios;
+ u32 reg = nv_ro32(bios, init->offset + 1);
+ u32 freq = nv_ro32(bios, init->offset + 5);
+
+ trace("PLL2\tR[0x%06x] =PLL= %dkHz\n", reg, freq);
+ init->offset += 9;
+
+ init_prog_pll(init, reg, freq);
+}
+
+/**
+ * INIT_I2C_BYTE - opcode 0x4c
+ *
+ */
+static void
+init_i2c_byte(struct nvbios_init *init)
+{
+ struct nvkm_bios *bios = init->bios;
+ u8 index = nv_ro08(bios, init->offset + 1);
+ u8 addr = nv_ro08(bios, init->offset + 2) >> 1;
+ u8 count = nv_ro08(bios, init->offset + 3);
+
+ trace("I2C_BYTE\tI2C[0x%02x][0x%02x]\n", index, addr);
+ init->offset += 4;
+
+ while (count--) {
+ u8 reg = nv_ro08(bios, init->offset + 0);
+ u8 mask = nv_ro08(bios, init->offset + 1);
+ u8 data = nv_ro08(bios, init->offset + 2);
+ int val;
+
+ trace("\t[0x%02x] &= 0x%02x |= 0x%02x\n", reg, mask, data);
+ init->offset += 3;
+
+ val = init_rdi2cr(init, index, addr, reg);
+ if (val < 0)
+ continue;
+ init_wri2cr(init, index, addr, reg, (val & mask) | data);
+ }
+}
+
+/**
+ * INIT_ZM_I2C_BYTE - opcode 0x4d
+ *
+ */
+static void
+init_zm_i2c_byte(struct nvbios_init *init)
+{
+ struct nvkm_bios *bios = init->bios;
+ u8 index = nv_ro08(bios, init->offset + 1);
+ u8 addr = nv_ro08(bios, init->offset + 2) >> 1;
+ u8 count = nv_ro08(bios, init->offset + 3);
+
+ trace("ZM_I2C_BYTE\tI2C[0x%02x][0x%02x]\n", index, addr);
+ init->offset += 4;
+
+ while (count--) {
+ u8 reg = nv_ro08(bios, init->offset + 0);
+ u8 data = nv_ro08(bios, init->offset + 1);
+
+ trace("\t[0x%02x] = 0x%02x\n", reg, data);
+ init->offset += 2;
+
+ init_wri2cr(init, index, addr, reg, data);
+ }
+}
+
+/**
+ * INIT_ZM_I2C - opcode 0x4e
+ *
+ */
+static void
+init_zm_i2c(struct nvbios_init *init)
+{
+ struct nvkm_bios *bios = init->bios;
+ u8 index = nv_ro08(bios, init->offset + 1);
+ u8 addr = nv_ro08(bios, init->offset + 2) >> 1;
+ u8 count = nv_ro08(bios, init->offset + 3);
+ u8 data[256], i;
+
+ trace("ZM_I2C\tI2C[0x%02x][0x%02x]\n", index, addr);
+ init->offset += 4;
+
+ for (i = 0; i < count; i++) {
+ data[i] = nv_ro08(bios, init->offset);
+ trace("\t0x%02x\n", data[i]);
+ init->offset++;
+ }
+
+ if (init_exec(init)) {
+ struct nvkm_i2c_port *port = init_i2c(init, index);
+ struct i2c_msg msg = {
+ .addr = addr, .flags = 0, .len = count, .buf = data,
+ };
+ int ret;
+
+ if (port && (ret = i2c_transfer(&port->adapter, &msg, 1)) != 1)
+ warn("i2c wr failed, %d\n", ret);
+ }
+}
+
+/**
+ * INIT_TMDS - opcode 0x4f
+ *
+ */
+static void
+init_tmds(struct nvbios_init *init)
+{
+ struct nvkm_bios *bios = init->bios;
+ u8 tmds = nv_ro08(bios, init->offset + 1);
+ u8 addr = nv_ro08(bios, init->offset + 2);
+ u8 mask = nv_ro08(bios, init->offset + 3);
+ u8 data = nv_ro08(bios, init->offset + 4);
+ u32 reg = init_tmds_reg(init, tmds);
+
+ trace("TMDS\tT[0x%02x][0x%02x] &= 0x%02x |= 0x%02x\n",
+ tmds, addr, mask, data);
+ init->offset += 5;
+
+ if (reg == 0)
+ return;
+
+ init_wr32(init, reg + 0, addr | 0x00010000);
+ init_wr32(init, reg + 4, data | (init_rd32(init, reg + 4) & mask));
+ init_wr32(init, reg + 0, addr);
+}
+
+/**
+ * INIT_ZM_TMDS_GROUP - opcode 0x50
+ *
+ */
+static void
+init_zm_tmds_group(struct nvbios_init *init)
+{
+ struct nvkm_bios *bios = init->bios;
+ u8 tmds = nv_ro08(bios, init->offset + 1);
+ u8 count = nv_ro08(bios, init->offset + 2);
+ u32 reg = init_tmds_reg(init, tmds);
+
+ trace("TMDS_ZM_GROUP\tT[0x%02x]\n", tmds);
+ init->offset += 3;
+
+ while (count--) {
+ u8 addr = nv_ro08(bios, init->offset + 0);
+ u8 data = nv_ro08(bios, init->offset + 1);
+
+ trace("\t[0x%02x] = 0x%02x\n", addr, data);
+ init->offset += 2;
+
+ init_wr32(init, reg + 4, data);
+ init_wr32(init, reg + 0, addr);
+ }
+}
+
+/**
+ * INIT_CR_INDEX_ADDRESS_LATCHED - opcode 0x51
+ *
+ */
+static void
+init_cr_idx_adr_latch(struct nvbios_init *init)
+{
+ struct nvkm_bios *bios = init->bios;
+ u8 addr0 = nv_ro08(bios, init->offset + 1);
+ u8 addr1 = nv_ro08(bios, init->offset + 2);
+ u8 base = nv_ro08(bios, init->offset + 3);
+ u8 count = nv_ro08(bios, init->offset + 4);
+ u8 save0;
+
+ trace("CR_INDEX_ADDR C[%02x] C[%02x]\n", addr0, addr1);
+ init->offset += 5;
+
+ save0 = init_rdvgai(init, 0x03d4, addr0);
+ while (count--) {
+ u8 data = nv_ro08(bios, init->offset);
+
+ trace("\t\t[0x%02x] = 0x%02x\n", base, data);
+ init->offset += 1;
+
+ init_wrvgai(init, 0x03d4, addr0, base++);
+ init_wrvgai(init, 0x03d4, addr1, data);
+ }
+ init_wrvgai(init, 0x03d4, addr0, save0);
+}
+
+/**
+ * INIT_CR - opcode 0x52
+ *
+ */
+static void
+init_cr(struct nvbios_init *init)
+{
+ struct nvkm_bios *bios = init->bios;
+ u8 addr = nv_ro08(bios, init->offset + 1);
+ u8 mask = nv_ro08(bios, init->offset + 2);
+ u8 data = nv_ro08(bios, init->offset + 3);
+ u8 val;
+
+ trace("CR\t\tC[0x%02x] &= 0x%02x |= 0x%02x\n", addr, mask, data);
+ init->offset += 4;
+
+ val = init_rdvgai(init, 0x03d4, addr) & mask;
+ init_wrvgai(init, 0x03d4, addr, val | data);
+}
+
+/**
+ * INIT_ZM_CR - opcode 0x53
+ *
+ */
+static void
+init_zm_cr(struct nvbios_init *init)
+{
+ struct nvkm_bios *bios = init->bios;
+ u8 addr = nv_ro08(bios, init->offset + 1);
+ u8 data = nv_ro08(bios, init->offset + 2);
+
+ trace("ZM_CR\tC[0x%02x] = 0x%02x\n", addr, data);
+ init->offset += 3;
+
+ init_wrvgai(init, 0x03d4, addr, data);
+}
+
+/**
+ * INIT_ZM_CR_GROUP - opcode 0x54
+ *
+ */
+static void
+init_zm_cr_group(struct nvbios_init *init)
+{
+ struct nvkm_bios *bios = init->bios;
+ u8 count = nv_ro08(bios, init->offset + 1);
+
+ trace("ZM_CR_GROUP\n");
+ init->offset += 2;
+
+ while (count--) {
+ u8 addr = nv_ro08(bios, init->offset + 0);
+ u8 data = nv_ro08(bios, init->offset + 1);
+
+ trace("\t\tC[0x%02x] = 0x%02x\n", addr, data);
+ init->offset += 2;
+
+ init_wrvgai(init, 0x03d4, addr, data);
+ }
+}
+
+/**
+ * INIT_CONDITION_TIME - opcode 0x56
+ *
+ */
+static void
+init_condition_time(struct nvbios_init *init)
+{
+ struct nvkm_bios *bios = init->bios;
+ u8 cond = nv_ro08(bios, init->offset + 1);
+ u8 retry = nv_ro08(bios, init->offset + 2);
+ u8 wait = min((u16)retry * 50, 100);
+
+ trace("CONDITION_TIME\t0x%02x 0x%02x\n", cond, retry);
+ init->offset += 3;
+
+ if (!init_exec(init))
+ return;
+
+ while (wait--) {
+ if (init_condition_met(init, cond))
+ return;
+ mdelay(20);
+ }
+
+ init_exec_set(init, false);
+}
+
+/**
+ * INIT_LTIME - opcode 0x57
+ *
+ */
+static void
+init_ltime(struct nvbios_init *init)
+{
+ struct nvkm_bios *bios = init->bios;
+ u16 msec = nv_ro16(bios, init->offset + 1);
+
+ trace("LTIME\t0x%04x\n", msec);
+ init->offset += 3;
+
+ if (init_exec(init))
+ mdelay(msec);
+}
+
+/**
+ * INIT_ZM_REG_SEQUENCE - opcode 0x58
+ *
+ */
+static void
+init_zm_reg_sequence(struct nvbios_init *init)
+{
+ struct nvkm_bios *bios = init->bios;
+ u32 base = nv_ro32(bios, init->offset + 1);
+ u8 count = nv_ro08(bios, init->offset + 5);
+
+ trace("ZM_REG_SEQUENCE\t0x%02x\n", count);
+ init->offset += 6;
+
+ while (count--) {
+ u32 data = nv_ro32(bios, init->offset);
+
+ trace("\t\tR[0x%06x] = 0x%08x\n", base, data);
+ init->offset += 4;
+
+ init_wr32(init, base, data);
+ base += 4;
+ }
+}
+
+/**
+ * INIT_SUB_DIRECT - opcode 0x5b
+ *
+ */
+static void
+init_sub_direct(struct nvbios_init *init)
+{
+ struct nvkm_bios *bios = init->bios;
+ u16 addr = nv_ro16(bios, init->offset + 1);
+ u16 save;
+
+ trace("SUB_DIRECT\t0x%04x\n", addr);
+
+ if (init_exec(init)) {
+ save = init->offset;
+ init->offset = addr;
+ if (nvbios_exec(init)) {
+ error("error parsing sub-table\n");
+ return;
+ }
+ init->offset = save;
+ }
+
+ init->offset += 3;
+}
+
+/**
+ * INIT_JUMP - opcode 0x5c
+ *
+ */
+static void
+init_jump(struct nvbios_init *init)
+{
+ struct nvkm_bios *bios = init->bios;
+ u16 offset = nv_ro16(bios, init->offset + 1);
+
+ trace("JUMP\t0x%04x\n", offset);
+
+ if (init_exec(init))
+ init->offset = offset;
+ else
+ init->offset += 3;
+}
+
+/**
+ * INIT_I2C_IF - opcode 0x5e
+ *
+ */
+static void
+init_i2c_if(struct nvbios_init *init)
+{
+ struct nvkm_bios *bios = init->bios;
+ u8 index = nv_ro08(bios, init->offset + 1);
+ u8 addr = nv_ro08(bios, init->offset + 2);
+ u8 reg = nv_ro08(bios, init->offset + 3);
+ u8 mask = nv_ro08(bios, init->offset + 4);
+ u8 data = nv_ro08(bios, init->offset + 5);
+ u8 value;
+
+ trace("I2C_IF\tI2C[0x%02x][0x%02x][0x%02x] & 0x%02x == 0x%02x\n",
+ index, addr, reg, mask, data);
+ init->offset += 6;
+ init_exec_force(init, true);
+
+ value = init_rdi2cr(init, index, addr, reg);
+ if ((value & mask) != data)
+ init_exec_set(init, false);
+
+ init_exec_force(init, false);
+}
+
+/**
+ * INIT_COPY_NV_REG - opcode 0x5f
+ *
+ */
+static void
+init_copy_nv_reg(struct nvbios_init *init)
+{
+ struct nvkm_bios *bios = init->bios;
+ u32 sreg = nv_ro32(bios, init->offset + 1);
+ u8 shift = nv_ro08(bios, init->offset + 5);
+ u32 smask = nv_ro32(bios, init->offset + 6);
+ u32 sxor = nv_ro32(bios, init->offset + 10);
+ u32 dreg = nv_ro32(bios, init->offset + 14);
+ u32 dmask = nv_ro32(bios, init->offset + 18);
+ u32 data;
+
+ trace("COPY_NV_REG\tR[0x%06x] &= 0x%08x |= "
+ "((R[0x%06x] %s 0x%02x) & 0x%08x ^ 0x%08x)\n",
+ dreg, dmask, sreg, (shift & 0x80) ? "<<" : ">>",
+ (shift & 0x80) ? (0x100 - shift) : shift, smask, sxor);
+ init->offset += 22;
+
+ data = init_shift(init_rd32(init, sreg), shift);
+ init_mask(init, dreg, ~dmask, (data & smask) ^ sxor);
+}
+
+/**
+ * INIT_ZM_INDEX_IO - opcode 0x62
+ *
+ */
+static void
+init_zm_index_io(struct nvbios_init *init)
+{
+ struct nvkm_bios *bios = init->bios;
+ u16 port = nv_ro16(bios, init->offset + 1);
+ u8 index = nv_ro08(bios, init->offset + 3);
+ u8 data = nv_ro08(bios, init->offset + 4);
+
+ trace("ZM_INDEX_IO\tI[0x%04x][0x%02x] = 0x%02x\n", port, index, data);
+ init->offset += 5;
+
+ init_wrvgai(init, port, index, data);
+}
+
+/**
+ * INIT_COMPUTE_MEM - opcode 0x63
+ *
+ */
+static void
+init_compute_mem(struct nvbios_init *init)
+{
+ struct nvkm_devinit *devinit = nvkm_devinit(init->bios);
+
+ trace("COMPUTE_MEM\n");
+ init->offset += 1;
+
+ init_exec_force(init, true);
+ if (init_exec(init) && devinit->meminit)
+ devinit->meminit(devinit);
+ init_exec_force(init, false);
+}
+
+/**
+ * INIT_RESET - opcode 0x65
+ *
+ */
+static void
+init_reset(struct nvbios_init *init)
+{
+ struct nvkm_bios *bios = init->bios;
+ u32 reg = nv_ro32(bios, init->offset + 1);
+ u32 data1 = nv_ro32(bios, init->offset + 5);
+ u32 data2 = nv_ro32(bios, init->offset + 9);
+ u32 savepci19;
+
+ trace("RESET\tR[0x%08x] = 0x%08x, 0x%08x", reg, data1, data2);
+ init->offset += 13;
+ init_exec_force(init, true);
+
+ savepci19 = init_mask(init, 0x00184c, 0x00000f00, 0x00000000);
+ init_wr32(init, reg, data1);
+ udelay(10);
+ init_wr32(init, reg, data2);
+ init_wr32(init, 0x00184c, savepci19);
+ init_mask(init, 0x001850, 0x00000001, 0x00000000);
+
+ init_exec_force(init, false);
+}
+
+/**
+ * INIT_CONFIGURE_MEM - opcode 0x66
+ *
+ */
+static u16
+init_configure_mem_clk(struct nvbios_init *init)
+{
+ u16 mdata = bmp_mem_init_table(init->bios);
+ if (mdata)
+ mdata += (init_rdvgai(init, 0x03d4, 0x3c) >> 4) * 66;
+ return mdata;
+}
+
+static void
+init_configure_mem(struct nvbios_init *init)
+{
+ struct nvkm_bios *bios = init->bios;
+ u16 mdata, sdata;
+ u32 addr, data;
+
+ trace("CONFIGURE_MEM\n");
+ init->offset += 1;
+
+ if (bios->version.major > 2) {
+ init_done(init);
+ return;
+ }
+ init_exec_force(init, true);
+
+ mdata = init_configure_mem_clk(init);
+ sdata = bmp_sdr_seq_table(bios);
+ if (nv_ro08(bios, mdata) & 0x01)
+ sdata = bmp_ddr_seq_table(bios);
+ mdata += 6; /* skip to data */
+
+ data = init_rdvgai(init, 0x03c4, 0x01);
+ init_wrvgai(init, 0x03c4, 0x01, data | 0x20);
+
+ for (; (addr = nv_ro32(bios, sdata)) != 0xffffffff; sdata += 4) {
+ switch (addr) {
+ case 0x10021c: /* CKE_NORMAL */
+ case 0x1002d0: /* CMD_REFRESH */
+ case 0x1002d4: /* CMD_PRECHARGE */
+ data = 0x00000001;
+ break;
+ default:
+ data = nv_ro32(bios, mdata);
+ mdata += 4;
+ if (data == 0xffffffff)
+ continue;
+ break;
+ }
+
+ init_wr32(init, addr, data);
+ }
+
+ init_exec_force(init, false);
+}
+
+/**
+ * INIT_CONFIGURE_CLK - opcode 0x67
+ *
+ */
+static void
+init_configure_clk(struct nvbios_init *init)
+{
+ struct nvkm_bios *bios = init->bios;
+ u16 mdata, clock;
+
+ trace("CONFIGURE_CLK\n");
+ init->offset += 1;
+
+ if (bios->version.major > 2) {
+ init_done(init);
+ return;
+ }
+ init_exec_force(init, true);
+
+ mdata = init_configure_mem_clk(init);
+
+ /* NVPLL */
+ clock = nv_ro16(bios, mdata + 4) * 10;
+ init_prog_pll(init, 0x680500, clock);
+
+ /* MPLL */
+ clock = nv_ro16(bios, mdata + 2) * 10;
+ if (nv_ro08(bios, mdata) & 0x01)
+ clock *= 2;
+ init_prog_pll(init, 0x680504, clock);
+
+ init_exec_force(init, false);
+}
+
+/**
+ * INIT_CONFIGURE_PREINIT - opcode 0x68
+ *
+ */
+static void
+init_configure_preinit(struct nvbios_init *init)
+{
+ struct nvkm_bios *bios = init->bios;
+ u32 strap;
+
+ trace("CONFIGURE_PREINIT\n");
+ init->offset += 1;
+
+ if (bios->version.major > 2) {
+ init_done(init);
+ return;
+ }
+ init_exec_force(init, true);
+
+ strap = init_rd32(init, 0x101000);
+ strap = ((strap << 2) & 0xf0) | ((strap & 0x40) >> 6);
+ init_wrvgai(init, 0x03d4, 0x3c, strap);
+
+ init_exec_force(init, false);
+}
+
+/**
+ * INIT_IO - opcode 0x69
+ *
+ */
+static void
+init_io(struct nvbios_init *init)
+{
+ struct nvkm_bios *bios = init->bios;
+ u16 port = nv_ro16(bios, init->offset + 1);
+ u8 mask = nv_ro16(bios, init->offset + 3);
+ u8 data = nv_ro16(bios, init->offset + 4);
+ u8 value;
+
+ trace("IO\t\tI[0x%04x] &= 0x%02x |= 0x%02x\n", port, mask, data);
+ init->offset += 5;
+
+ /* ummm.. yes.. should really figure out wtf this is and why it's
+ * needed some day.. it's almost certainly wrong, but, it also
+ * somehow makes things work...
+ */
+ if (nv_device(init->bios)->card_type >= NV_50 &&
+ port == 0x03c3 && data == 0x01) {
+ init_mask(init, 0x614100, 0xf0800000, 0x00800000);
+ init_mask(init, 0x00e18c, 0x00020000, 0x00020000);
+ init_mask(init, 0x614900, 0xf0800000, 0x00800000);
+ init_mask(init, 0x000200, 0x40000000, 0x00000000);
+ mdelay(10);
+ init_mask(init, 0x00e18c, 0x00020000, 0x00000000);
+ init_mask(init, 0x000200, 0x40000000, 0x40000000);
+ init_wr32(init, 0x614100, 0x00800018);
+ init_wr32(init, 0x614900, 0x00800018);
+ mdelay(10);
+ init_wr32(init, 0x614100, 0x10000018);
+ init_wr32(init, 0x614900, 0x10000018);
+ }
+
+ value = init_rdport(init, port) & mask;
+ init_wrport(init, port, data | value);
+}
+
+/**
+ * INIT_SUB - opcode 0x6b
+ *
+ */
+static void
+init_sub(struct nvbios_init *init)
+{
+ struct nvkm_bios *bios = init->bios;
+ u8 index = nv_ro08(bios, init->offset + 1);
+ u16 addr, save;
+
+ trace("SUB\t0x%02x\n", index);
+
+ addr = init_script(bios, index);
+ if (addr && init_exec(init)) {
+ save = init->offset;
+ init->offset = addr;
+ if (nvbios_exec(init)) {
+ error("error parsing sub-table\n");
+ return;
+ }
+ init->offset = save;
+ }
+
+ init->offset += 2;
+}
+
+/**
+ * INIT_RAM_CONDITION - opcode 0x6d
+ *
+ */
+static void
+init_ram_condition(struct nvbios_init *init)
+{
+ struct nvkm_bios *bios = init->bios;
+ u8 mask = nv_ro08(bios, init->offset + 1);
+ u8 value = nv_ro08(bios, init->offset + 2);
+
+ trace("RAM_CONDITION\t"
+ "(R[0x100000] & 0x%02x) == 0x%02x\n", mask, value);
+ init->offset += 3;
+
+ if ((init_rd32(init, 0x100000) & mask) != value)
+ init_exec_set(init, false);
+}
+
+/**
+ * INIT_NV_REG - opcode 0x6e
+ *
+ */
+static void
+init_nv_reg(struct nvbios_init *init)
+{
+ struct nvkm_bios *bios = init->bios;
+ u32 reg = nv_ro32(bios, init->offset + 1);
+ u32 mask = nv_ro32(bios, init->offset + 5);
+ u32 data = nv_ro32(bios, init->offset + 9);
+
+ trace("NV_REG\tR[0x%06x] &= 0x%08x |= 0x%08x\n", reg, mask, data);
+ init->offset += 13;
+
+ init_mask(init, reg, ~mask, data);
+}
+
+/**
+ * INIT_MACRO - opcode 0x6f
+ *
+ */
+static void
+init_macro(struct nvbios_init *init)
+{
+ struct nvkm_bios *bios = init->bios;
+ u8 macro = nv_ro08(bios, init->offset + 1);
+ u16 table;
+
+ trace("MACRO\t0x%02x\n", macro);
+
+ table = init_macro_table(init);
+ if (table) {
+ u32 addr = nv_ro32(bios, table + (macro * 8) + 0);
+ u32 data = nv_ro32(bios, table + (macro * 8) + 4);
+ trace("\t\tR[0x%06x] = 0x%08x\n", addr, data);
+ init_wr32(init, addr, data);
+ }
+
+ init->offset += 2;
+}
+
+/**
+ * INIT_RESUME - opcode 0x72
+ *
+ */
+static void
+init_resume(struct nvbios_init *init)
+{
+ trace("RESUME\n");
+ init->offset += 1;
+ init_exec_set(init, true);
+}
+
+/**
+ * INIT_TIME - opcode 0x74
+ *
+ */
+static void
+init_time(struct nvbios_init *init)
+{
+ struct nvkm_bios *bios = init->bios;
+ u16 usec = nv_ro16(bios, init->offset + 1);
+
+ trace("TIME\t0x%04x\n", usec);
+ init->offset += 3;
+
+ if (init_exec(init)) {
+ if (usec < 1000)
+ udelay(usec);
+ else
+ mdelay((usec + 900) / 1000);
+ }
+}
+
+/**
+ * INIT_CONDITION - opcode 0x75
+ *
+ */
+static void
+init_condition(struct nvbios_init *init)
+{
+ struct nvkm_bios *bios = init->bios;
+ u8 cond = nv_ro08(bios, init->offset + 1);
+
+ trace("CONDITION\t0x%02x\n", cond);
+ init->offset += 2;
+
+ if (!init_condition_met(init, cond))
+ init_exec_set(init, false);
+}
+
+/**
+ * INIT_IO_CONDITION - opcode 0x76
+ *
+ */
+static void
+init_io_condition(struct nvbios_init *init)
+{
+ struct nvkm_bios *bios = init->bios;
+ u8 cond = nv_ro08(bios, init->offset + 1);
+
+ trace("IO_CONDITION\t0x%02x\n", cond);
+ init->offset += 2;
+
+ if (!init_io_condition_met(init, cond))
+ init_exec_set(init, false);
+}
+
+/**
+ * INIT_INDEX_IO - opcode 0x78
+ *
+ */
+static void
+init_index_io(struct nvbios_init *init)
+{
+ struct nvkm_bios *bios = init->bios;
+ u16 port = nv_ro16(bios, init->offset + 1);
+ u8 index = nv_ro16(bios, init->offset + 3);
+ u8 mask = nv_ro08(bios, init->offset + 4);
+ u8 data = nv_ro08(bios, init->offset + 5);
+ u8 value;
+
+ trace("INDEX_IO\tI[0x%04x][0x%02x] &= 0x%02x |= 0x%02x\n",
+ port, index, mask, data);
+ init->offset += 6;
+
+ value = init_rdvgai(init, port, index) & mask;
+ init_wrvgai(init, port, index, data | value);
+}
+
+/**
+ * INIT_PLL - opcode 0x79
+ *
+ */
+static void
+init_pll(struct nvbios_init *init)
+{
+ struct nvkm_bios *bios = init->bios;
+ u32 reg = nv_ro32(bios, init->offset + 1);
+ u32 freq = nv_ro16(bios, init->offset + 5) * 10;
+
+ trace("PLL\tR[0x%06x] =PLL= %dkHz\n", reg, freq);
+ init->offset += 7;
+
+ init_prog_pll(init, reg, freq);
+}
+
+/**
+ * INIT_ZM_REG - opcode 0x7a
+ *
+ */
+static void
+init_zm_reg(struct nvbios_init *init)
+{
+ struct nvkm_bios *bios = init->bios;
+ u32 addr = nv_ro32(bios, init->offset + 1);
+ u32 data = nv_ro32(bios, init->offset + 5);
+
+ trace("ZM_REG\tR[0x%06x] = 0x%08x\n", addr, data);
+ init->offset += 9;
+
+ if (addr == 0x000200)
+ data |= 0x00000001;
+
+ init_wr32(init, addr, data);
+}
+
+/**
+ * INIT_RAM_RESTRICT_PLL - opcde 0x87
+ *
+ */
+static void
+init_ram_restrict_pll(struct nvbios_init *init)
+{
+ struct nvkm_bios *bios = init->bios;
+ u8 type = nv_ro08(bios, init->offset + 1);
+ u8 count = init_ram_restrict_group_count(init);
+ u8 strap = init_ram_restrict(init);
+ u8 cconf;
+
+ trace("RAM_RESTRICT_PLL\t0x%02x\n", type);
+ init->offset += 2;
+
+ for (cconf = 0; cconf < count; cconf++) {
+ u32 freq = nv_ro32(bios, init->offset);
+
+ if (cconf == strap) {
+ trace("%dkHz *\n", freq);
+ init_prog_pll(init, type, freq);
+ } else {
+ trace("%dkHz\n", freq);
+ }
+
+ init->offset += 4;
+ }
+}
+
+/**
+ * INIT_GPIO - opcode 0x8e
+ *
+ */
+static void
+init_gpio(struct nvbios_init *init)
+{
+ struct nvkm_gpio *gpio = nvkm_gpio(init->bios);
+
+ trace("GPIO\n");
+ init->offset += 1;
+
+ if (init_exec(init) && gpio && gpio->reset)
+ gpio->reset(gpio, DCB_GPIO_UNUSED);
+}
+
+/**
+ * INIT_RAM_RESTRICT_ZM_GROUP - opcode 0x8f
+ *
+ */
+static void
+init_ram_restrict_zm_reg_group(struct nvbios_init *init)
+{
+ struct nvkm_bios *bios = init->bios;
+ u32 addr = nv_ro32(bios, init->offset + 1);
+ u8 incr = nv_ro08(bios, init->offset + 5);
+ u8 num = nv_ro08(bios, init->offset + 6);
+ u8 count = init_ram_restrict_group_count(init);
+ u8 index = init_ram_restrict(init);
+ u8 i, j;
+
+ trace("RAM_RESTRICT_ZM_REG_GROUP\t"
+ "R[0x%08x] 0x%02x 0x%02x\n", addr, incr, num);
+ init->offset += 7;
+
+ for (i = 0; i < num; i++) {
+ trace("\tR[0x%06x] = {\n", addr);
+ for (j = 0; j < count; j++) {
+ u32 data = nv_ro32(bios, init->offset);
+
+ if (j == index) {
+ trace("\t\t0x%08x *\n", data);
+ init_wr32(init, addr, data);
+ } else {
+ trace("\t\t0x%08x\n", data);
+ }
+
+ init->offset += 4;
+ }
+ trace("\t}\n");
+ addr += incr;
+ }
+}
+
+/**
+ * INIT_COPY_ZM_REG - opcode 0x90
+ *
+ */
+static void
+init_copy_zm_reg(struct nvbios_init *init)
+{
+ struct nvkm_bios *bios = init->bios;
+ u32 sreg = nv_ro32(bios, init->offset + 1);
+ u32 dreg = nv_ro32(bios, init->offset + 5);
+
+ trace("COPY_ZM_REG\tR[0x%06x] = R[0x%06x]\n", dreg, sreg);
+ init->offset += 9;
+
+ init_wr32(init, dreg, init_rd32(init, sreg));
+}
+
+/**
+ * INIT_ZM_REG_GROUP - opcode 0x91
+ *
+ */
+static void
+init_zm_reg_group(struct nvbios_init *init)
+{
+ struct nvkm_bios *bios = init->bios;
+ u32 addr = nv_ro32(bios, init->offset + 1);
+ u8 count = nv_ro08(bios, init->offset + 5);
+
+ trace("ZM_REG_GROUP\tR[0x%06x] =\n", addr);
+ init->offset += 6;
+
+ while (count--) {
+ u32 data = nv_ro32(bios, init->offset);
+ trace("\t0x%08x\n", data);
+ init_wr32(init, addr, data);
+ init->offset += 4;
+ }
+}
+
+/**
+ * INIT_XLAT - opcode 0x96
+ *
+ */
+static void
+init_xlat(struct nvbios_init *init)
+{
+ struct nvkm_bios *bios = init->bios;
+ u32 saddr = nv_ro32(bios, init->offset + 1);
+ u8 sshift = nv_ro08(bios, init->offset + 5);
+ u8 smask = nv_ro08(bios, init->offset + 6);
+ u8 index = nv_ro08(bios, init->offset + 7);
+ u32 daddr = nv_ro32(bios, init->offset + 8);
+ u32 dmask = nv_ro32(bios, init->offset + 12);
+ u8 shift = nv_ro08(bios, init->offset + 16);
+ u32 data;
+
+ trace("INIT_XLAT\tR[0x%06x] &= 0x%08x |= "
+ "(X%02x((R[0x%06x] %s 0x%02x) & 0x%02x) << 0x%02x)\n",
+ daddr, dmask, index, saddr, (sshift & 0x80) ? "<<" : ">>",
+ (sshift & 0x80) ? (0x100 - sshift) : sshift, smask, shift);
+ init->offset += 17;
+
+ data = init_shift(init_rd32(init, saddr), sshift) & smask;
+ data = init_xlat_(init, index, data) << shift;
+ init_mask(init, daddr, ~dmask, data);
+}
+
+/**
+ * INIT_ZM_MASK_ADD - opcode 0x97
+ *
+ */
+static void
+init_zm_mask_add(struct nvbios_init *init)
+{
+ struct nvkm_bios *bios = init->bios;
+ u32 addr = nv_ro32(bios, init->offset + 1);
+ u32 mask = nv_ro32(bios, init->offset + 5);
+ u32 add = nv_ro32(bios, init->offset + 9);
+ u32 data;
+
+ trace("ZM_MASK_ADD\tR[0x%06x] &= 0x%08x += 0x%08x\n", addr, mask, add);
+ init->offset += 13;
+
+ data = init_rd32(init, addr);
+ data = (data & mask) | ((data + add) & ~mask);
+ init_wr32(init, addr, data);
+}
+
+/**
+ * INIT_AUXCH - opcode 0x98
+ *
+ */
+static void
+init_auxch(struct nvbios_init *init)
+{
+ struct nvkm_bios *bios = init->bios;
+ u32 addr = nv_ro32(bios, init->offset + 1);
+ u8 count = nv_ro08(bios, init->offset + 5);
+
+ trace("AUXCH\tAUX[0x%08x] 0x%02x\n", addr, count);
+ init->offset += 6;
+
+ while (count--) {
+ u8 mask = nv_ro08(bios, init->offset + 0);
+ u8 data = nv_ro08(bios, init->offset + 1);
+ trace("\tAUX[0x%08x] &= 0x%02x |= 0x%02x\n", addr, mask, data);
+ mask = init_rdauxr(init, addr) & mask;
+ init_wrauxr(init, addr, mask | data);
+ init->offset += 2;
+ }
+}
+
+/**
+ * INIT_AUXCH - opcode 0x99
+ *
+ */
+static void
+init_zm_auxch(struct nvbios_init *init)
+{
+ struct nvkm_bios *bios = init->bios;
+ u32 addr = nv_ro32(bios, init->offset + 1);
+ u8 count = nv_ro08(bios, init->offset + 5);
+
+ trace("ZM_AUXCH\tAUX[0x%08x] 0x%02x\n", addr, count);
+ init->offset += 6;
+
+ while (count--) {
+ u8 data = nv_ro08(bios, init->offset + 0);
+ trace("\tAUX[0x%08x] = 0x%02x\n", addr, data);
+ init_wrauxr(init, addr, data);
+ init->offset += 1;
+ }
+}
+
+/**
+ * INIT_I2C_LONG_IF - opcode 0x9a
+ *
+ */
+static void
+init_i2c_long_if(struct nvbios_init *init)
+{
+ struct nvkm_bios *bios = init->bios;
+ u8 index = nv_ro08(bios, init->offset + 1);
+ u8 addr = nv_ro08(bios, init->offset + 2) >> 1;
+ u8 reglo = nv_ro08(bios, init->offset + 3);
+ u8 reghi = nv_ro08(bios, init->offset + 4);
+ u8 mask = nv_ro08(bios, init->offset + 5);
+ u8 data = nv_ro08(bios, init->offset + 6);
+ struct nvkm_i2c_port *port;
+
+ trace("I2C_LONG_IF\t"
+ "I2C[0x%02x][0x%02x][0x%02x%02x] & 0x%02x == 0x%02x\n",
+ index, addr, reglo, reghi, mask, data);
+ init->offset += 7;
+
+ port = init_i2c(init, index);
+ if (port) {
+ u8 i[2] = { reghi, reglo };
+ u8 o[1] = {};
+ struct i2c_msg msg[] = {
+ { .addr = addr, .flags = 0, .len = 2, .buf = i },
+ { .addr = addr, .flags = I2C_M_RD, .len = 1, .buf = o }
+ };
+ int ret;
+
+ ret = i2c_transfer(&port->adapter, msg, 2);
+ if (ret == 2 && ((o[0] & mask) == data))
+ return;
+ }
+
+ init_exec_set(init, false);
+}
+
+/**
+ * INIT_GPIO_NE - opcode 0xa9
+ *
+ */
+static void
+init_gpio_ne(struct nvbios_init *init)
+{
+ struct nvkm_bios *bios = init->bios;
+ struct nvkm_gpio *gpio = nvkm_gpio(bios);
+ struct dcb_gpio_func func;
+ u8 count = nv_ro08(bios, init->offset + 1);
+ u8 idx = 0, ver, len;
+ u16 data, i;
+
+ trace("GPIO_NE\t");
+ init->offset += 2;
+
+ for (i = init->offset; i < init->offset + count; i++)
+ cont("0x%02x ", nv_ro08(bios, i));
+ cont("\n");
+
+ while ((data = dcb_gpio_parse(bios, 0, idx++, &ver, &len, &func))) {
+ if (func.func != DCB_GPIO_UNUSED) {
+ for (i = init->offset; i < init->offset + count; i++) {
+ if (func.func == nv_ro08(bios, i))
+ break;
+ }
+
+ trace("\tFUNC[0x%02x]", func.func);
+ if (i == (init->offset + count)) {
+ cont(" *");
+ if (init_exec(init) && gpio && gpio->reset)
+ gpio->reset(gpio, func.func);
+ }
+ cont("\n");
+ }
+ }
+
+ init->offset += count;
+}
+
+static struct nvbios_init_opcode {
+ void (*exec)(struct nvbios_init *);
+} init_opcode[] = {
+ [0x32] = { init_io_restrict_prog },
+ [0x33] = { init_repeat },
+ [0x34] = { init_io_restrict_pll },
+ [0x36] = { init_end_repeat },
+ [0x37] = { init_copy },
+ [0x38] = { init_not },
+ [0x39] = { init_io_flag_condition },
+ [0x3a] = { init_dp_condition },
+ [0x3b] = { init_io_mask_or },
+ [0x3c] = { init_io_or },
+ [0x47] = { init_andn_reg },
+ [0x48] = { init_or_reg },
+ [0x49] = { init_idx_addr_latched },
+ [0x4a] = { init_io_restrict_pll2 },
+ [0x4b] = { init_pll2 },
+ [0x4c] = { init_i2c_byte },
+ [0x4d] = { init_zm_i2c_byte },
+ [0x4e] = { init_zm_i2c },
+ [0x4f] = { init_tmds },
+ [0x50] = { init_zm_tmds_group },
+ [0x51] = { init_cr_idx_adr_latch },
+ [0x52] = { init_cr },
+ [0x53] = { init_zm_cr },
+ [0x54] = { init_zm_cr_group },
+ [0x56] = { init_condition_time },
+ [0x57] = { init_ltime },
+ [0x58] = { init_zm_reg_sequence },
+ [0x5b] = { init_sub_direct },
+ [0x5c] = { init_jump },
+ [0x5e] = { init_i2c_if },
+ [0x5f] = { init_copy_nv_reg },
+ [0x62] = { init_zm_index_io },
+ [0x63] = { init_compute_mem },
+ [0x65] = { init_reset },
+ [0x66] = { init_configure_mem },
+ [0x67] = { init_configure_clk },
+ [0x68] = { init_configure_preinit },
+ [0x69] = { init_io },
+ [0x6b] = { init_sub },
+ [0x6d] = { init_ram_condition },
+ [0x6e] = { init_nv_reg },
+ [0x6f] = { init_macro },
+ [0x71] = { init_done },
+ [0x72] = { init_resume },
+ [0x74] = { init_time },
+ [0x75] = { init_condition },
+ [0x76] = { init_io_condition },
+ [0x78] = { init_index_io },
+ [0x79] = { init_pll },
+ [0x7a] = { init_zm_reg },
+ [0x87] = { init_ram_restrict_pll },
+ [0x8c] = { init_reserved },
+ [0x8d] = { init_reserved },
+ [0x8e] = { init_gpio },
+ [0x8f] = { init_ram_restrict_zm_reg_group },
+ [0x90] = { init_copy_zm_reg },
+ [0x91] = { init_zm_reg_group },
+ [0x92] = { init_reserved },
+ [0x96] = { init_xlat },
+ [0x97] = { init_zm_mask_add },
+ [0x98] = { init_auxch },
+ [0x99] = { init_zm_auxch },
+ [0x9a] = { init_i2c_long_if },
+ [0xa9] = { init_gpio_ne },
+ [0xaa] = { init_reserved },
+};
+
+#define init_opcode_nr (sizeof(init_opcode) / sizeof(init_opcode[0]))
+
+int
+nvbios_exec(struct nvbios_init *init)
+{
+ init->nested++;
+ while (init->offset) {
+ u8 opcode = nv_ro08(init->bios, init->offset);
+ if (opcode >= init_opcode_nr || !init_opcode[opcode].exec) {
+ error("unknown opcode 0x%02x\n", opcode);
+ return -EINVAL;
+ }
+
+ init_opcode[opcode].exec(init);
+ }
+ init->nested--;
+ return 0;
+}
+
+int
+nvbios_init(struct nvkm_subdev *subdev, bool execute)
+{
+ struct nvkm_bios *bios = nvkm_bios(subdev);
+ int ret = 0;
+ int i = -1;
+ u16 data;
+
+ if (execute)
+ nv_info(bios, "running init tables\n");
+ while (!ret && (data = (init_script(bios, ++i)))) {
+ struct nvbios_init init = {
+ .subdev = subdev,
+ .bios = bios,
+ .offset = data,
+ .outp = NULL,
+ .crtc = -1,
+ .execute = execute ? 1 : 0,
+ };
+
+ ret = nvbios_exec(&init);
+ }
+
+ /* the vbios parser will run this right after the normal init
+ * tables, whereas the binary driver appears to run it later.
+ */
+ if (!ret && (data = init_unknown_script(bios))) {
+ struct nvbios_init init = {
+ .subdev = subdev,
+ .bios = bios,
+ .offset = data,
+ .outp = NULL,
+ .crtc = -1,
+ .execute = execute ? 1 : 0,
+ };
+
+ ret = nvbios_exec(&init);
+ }
+
+ return ret;
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/mxm.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/mxm.c
new file mode 100644
index 000000000000..c4087df4f85e
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/mxm.c
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include <subdev/bios.h>
+#include <subdev/bios/bit.h>
+#include <subdev/bios/mxm.h>
+
+u16
+mxm_table(struct nvkm_bios *bios, u8 *ver, u8 *hdr)
+{
+ struct bit_entry x;
+
+ if (bit_entry(bios, 'x', &x)) {
+ nv_debug(bios, "BIT 'x' table not present\n");
+ return 0x0000;
+ }
+
+ *ver = x.version;
+ *hdr = x.length;
+ if (*ver != 1 || *hdr < 3) {
+ nv_warn(bios, "BIT 'x' table %d/%d unknown\n", *ver, *hdr);
+ return 0x0000;
+ }
+
+ return x.offset;
+}
+
+/* These map MXM v2.x digital connection values to the appropriate SOR/link,
+ * hopefully they're correct for all boards within the same chipset...
+ *
+ * MXM v3.x VBIOS are nicer and provide pointers to these tables.
+ */
+static u8 g84_sor_map[16] = {
+ 0x00, 0x12, 0x22, 0x11, 0x32, 0x31, 0x11, 0x31,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static u8 g92_sor_map[16] = {
+ 0x00, 0x12, 0x22, 0x11, 0x32, 0x31, 0x11, 0x31,
+ 0x11, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static u8 g94_sor_map[16] = {
+ 0x00, 0x14, 0x24, 0x11, 0x34, 0x31, 0x11, 0x31,
+ 0x11, 0x31, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static u8 g98_sor_map[16] = {
+ 0x00, 0x14, 0x12, 0x11, 0x00, 0x31, 0x11, 0x31,
+ 0x11, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+u8
+mxm_sor_map(struct nvkm_bios *bios, u8 conn)
+{
+ u8 ver, hdr;
+ u16 mxm = mxm_table(bios, &ver, &hdr);
+ if (mxm && hdr >= 6) {
+ u16 map = nv_ro16(bios, mxm + 4);
+ if (map) {
+ ver = nv_ro08(bios, map);
+ if (ver == 0x10) {
+ if (conn < nv_ro08(bios, map + 3)) {
+ map += nv_ro08(bios, map + 1);
+ map += conn;
+ return nv_ro08(bios, map);
+ }
+
+ return 0x00;
+ }
+
+ nv_warn(bios, "unknown sor map v%02x\n", ver);
+ }
+ }
+
+ if (bios->version.chip == 0x84 || bios->version.chip == 0x86)
+ return g84_sor_map[conn];
+ if (bios->version.chip == 0x92)
+ return g92_sor_map[conn];
+ if (bios->version.chip == 0x94 || bios->version.chip == 0x96)
+ return g94_sor_map[conn];
+ if (bios->version.chip == 0x98)
+ return g98_sor_map[conn];
+
+ nv_warn(bios, "missing sor map\n");
+ return 0x00;
+}
+
+u8
+mxm_ddc_map(struct nvkm_bios *bios, u8 port)
+{
+ u8 ver, hdr;
+ u16 mxm = mxm_table(bios, &ver, &hdr);
+ if (mxm && hdr >= 8) {
+ u16 map = nv_ro16(bios, mxm + 6);
+ if (map) {
+ ver = nv_ro08(bios, map);
+ if (ver == 0x10) {
+ if (port < nv_ro08(bios, map + 3)) {
+ map += nv_ro08(bios, map + 1);
+ map += port;
+ return nv_ro08(bios, map);
+ }
+
+ return 0x00;
+ }
+
+ nv_warn(bios, "unknown ddc map v%02x\n", ver);
+ }
+ }
+
+ /* v2.x: directly write port as dcb i2cidx */
+ return (port << 4) | port;
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/npde.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/npde.c
new file mode 100644
index 000000000000..fd7dd718b2bf
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/npde.c
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2014 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs <bskeggs@redhat.com>
+ */
+#include <subdev/bios.h>
+#include <subdev/bios/npde.h>
+#include <subdev/bios/pcir.h>
+
+u32
+nvbios_npdeTe(struct nvkm_bios *bios, u32 base)
+{
+ struct nvbios_pcirT pcir;
+ u8 ver; u16 hdr;
+ u32 data = nvbios_pcirTp(bios, base, &ver, &hdr, &pcir);
+ if (data = (data + hdr + 0x0f) & ~0x0f, data) {
+ switch (nv_ro32(bios, data + 0x00)) {
+ case 0x4544504e: /* NPDE */
+ break;
+ default:
+ nv_debug(bios, "%08x: NPDE signature (%08x) unknown\n",
+ data, nv_ro32(bios, data + 0x00));
+ data = 0;
+ break;
+ }
+ }
+ return data;
+}
+
+u32
+nvbios_npdeTp(struct nvkm_bios *bios, u32 base, struct nvbios_npdeT *info)
+{
+ u32 data = nvbios_npdeTe(bios, base);
+ memset(info, 0x00, sizeof(*info));
+ if (data) {
+ info->image_size = nv_ro16(bios, data + 0x08) * 512;
+ info->last = nv_ro08(bios, data + 0x0a) & 0x80;
+ }
+ return data;
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/pcir.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/pcir.c
new file mode 100644
index 000000000000..df5978753ae8
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/pcir.c
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2014 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs <bskeggs@redhat.com>
+ */
+#include <subdev/bios.h>
+#include <subdev/bios/pcir.h>
+
+u32
+nvbios_pcirTe(struct nvkm_bios *bios, u32 base, u8 *ver, u16 *hdr)
+{
+ u32 data = nv_ro16(bios, base + 0x18);
+ if (data) {
+ data += base;
+ switch (nv_ro32(bios, data + 0x00)) {
+ case 0x52494350: /* PCIR */
+ case 0x53494752: /* RGIS */
+ case 0x5344504e: /* NPDS */
+ *hdr = nv_ro16(bios, data + 0x0a);
+ *ver = nv_ro08(bios, data + 0x0c);
+ break;
+ default:
+ nv_debug(bios, "%08x: PCIR signature (%08x) unknown\n",
+ data, nv_ro32(bios, data + 0x00));
+ data = 0;
+ break;
+ }
+ }
+ return data;
+}
+
+u32
+nvbios_pcirTp(struct nvkm_bios *bios, u32 base, u8 *ver, u16 *hdr,
+ struct nvbios_pcirT *info)
+{
+ u32 data = nvbios_pcirTe(bios, base, ver, hdr);
+ memset(info, 0x00, sizeof(*info));
+ if (data) {
+ info->vendor_id = nv_ro16(bios, data + 0x04);
+ info->device_id = nv_ro16(bios, data + 0x06);
+ info->class_code[0] = nv_ro08(bios, data + 0x0d);
+ info->class_code[1] = nv_ro08(bios, data + 0x0e);
+ info->class_code[2] = nv_ro08(bios, data + 0x0f);
+ info->image_size = nv_ro16(bios, data + 0x10) * 512;
+ info->image_rev = nv_ro16(bios, data + 0x12);
+ info->image_type = nv_ro08(bios, data + 0x14);
+ info->last = nv_ro08(bios, data + 0x15) & 0x80;
+ }
+ return data;
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/perf.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/perf.c
new file mode 100644
index 000000000000..382ae9cdbf58
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/perf.c
@@ -0,0 +1,201 @@
+/*
+ * Copyright 2012 Nouveau Community
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Martin Peres
+ */
+#include <subdev/bios.h>
+#include <subdev/bios/bit.h>
+#include <subdev/bios/perf.h>
+
+#include <core/device.h>
+
+u16
+nvbios_perf_table(struct nvkm_bios *bios, u8 *ver, u8 *hdr,
+ u8 *cnt, u8 *len, u8 *snr, u8 *ssz)
+{
+ struct bit_entry bit_P;
+ u16 perf = 0x0000;
+
+ if (!bit_entry(bios, 'P', &bit_P)) {
+ if (bit_P.version <= 2) {
+ perf = nv_ro16(bios, bit_P.offset + 0);
+ if (perf) {
+ *ver = nv_ro08(bios, perf + 0);
+ *hdr = nv_ro08(bios, perf + 1);
+ if (*ver >= 0x40 && *ver < 0x41) {
+ *cnt = nv_ro08(bios, perf + 5);
+ *len = nv_ro08(bios, perf + 2);
+ *snr = nv_ro08(bios, perf + 4);
+ *ssz = nv_ro08(bios, perf + 3);
+ return perf;
+ } else
+ if (*ver >= 0x20 && *ver < 0x40) {
+ *cnt = nv_ro08(bios, perf + 2);
+ *len = nv_ro08(bios, perf + 3);
+ *snr = nv_ro08(bios, perf + 4);
+ *ssz = nv_ro08(bios, perf + 5);
+ return perf;
+ }
+ }
+ }
+ }
+
+ if (bios->bmp_offset) {
+ if (nv_ro08(bios, bios->bmp_offset + 6) >= 0x25) {
+ perf = nv_ro16(bios, bios->bmp_offset + 0x94);
+ if (perf) {
+ *hdr = nv_ro08(bios, perf + 0);
+ *ver = nv_ro08(bios, perf + 1);
+ *cnt = nv_ro08(bios, perf + 2);
+ *len = nv_ro08(bios, perf + 3);
+ *snr = 0;
+ *ssz = 0;
+ return perf;
+ }
+ }
+ }
+
+ return 0x0000;
+}
+
+u16
+nvbios_perf_entry(struct nvkm_bios *bios, int idx,
+ u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
+{
+ u8 snr, ssz;
+ u16 perf = nvbios_perf_table(bios, ver, hdr, cnt, len, &snr, &ssz);
+ if (perf && idx < *cnt) {
+ perf = perf + *hdr + (idx * (*len + (snr * ssz)));
+ *hdr = *len;
+ *cnt = snr;
+ *len = ssz;
+ return perf;
+ }
+ return 0x0000;
+}
+
+u16
+nvbios_perfEp(struct nvkm_bios *bios, int idx,
+ u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_perfE *info)
+{
+ u16 perf = nvbios_perf_entry(bios, idx, ver, hdr, cnt, len);
+ memset(info, 0x00, sizeof(*info));
+ info->pstate = nv_ro08(bios, perf + 0x00);
+ switch (!!perf * *ver) {
+ case 0x12:
+ case 0x13:
+ case 0x14:
+ info->core = nv_ro32(bios, perf + 0x01) * 10;
+ info->memory = nv_ro32(bios, perf + 0x05) * 20;
+ info->fanspeed = nv_ro08(bios, perf + 0x37);
+ if (*hdr > 0x38)
+ info->voltage = nv_ro08(bios, perf + 0x38);
+ break;
+ case 0x21:
+ case 0x23:
+ case 0x24:
+ info->fanspeed = nv_ro08(bios, perf + 0x04);
+ info->voltage = nv_ro08(bios, perf + 0x05);
+ info->shader = nv_ro16(bios, perf + 0x06) * 1000;
+ info->core = info->shader + (signed char)
+ nv_ro08(bios, perf + 0x08) * 1000;
+ switch (nv_device(bios)->chipset) {
+ case 0x49:
+ case 0x4b:
+ info->memory = nv_ro16(bios, perf + 0x0b) * 1000;
+ break;
+ default:
+ info->memory = nv_ro16(bios, perf + 0x0b) * 2000;
+ break;
+ }
+ break;
+ case 0x25:
+ info->fanspeed = nv_ro08(bios, perf + 0x04);
+ info->voltage = nv_ro08(bios, perf + 0x05);
+ info->core = nv_ro16(bios, perf + 0x06) * 1000;
+ info->shader = nv_ro16(bios, perf + 0x0a) * 1000;
+ info->memory = nv_ro16(bios, perf + 0x0c) * 1000;
+ break;
+ case 0x30:
+ info->script = nv_ro16(bios, perf + 0x02);
+ case 0x35:
+ info->fanspeed = nv_ro08(bios, perf + 0x06);
+ info->voltage = nv_ro08(bios, perf + 0x07);
+ info->core = nv_ro16(bios, perf + 0x08) * 1000;
+ info->shader = nv_ro16(bios, perf + 0x0a) * 1000;
+ info->memory = nv_ro16(bios, perf + 0x0c) * 1000;
+ info->vdec = nv_ro16(bios, perf + 0x10) * 1000;
+ info->disp = nv_ro16(bios, perf + 0x14) * 1000;
+ break;
+ case 0x40:
+ info->voltage = nv_ro08(bios, perf + 0x02);
+ break;
+ default:
+ return 0x0000;
+ }
+ return perf;
+}
+
+u32
+nvbios_perfSe(struct nvkm_bios *bios, u32 perfE, int idx,
+ u8 *ver, u8 *hdr, u8 cnt, u8 len)
+{
+ u32 data = 0x00000000;
+ if (idx < cnt) {
+ data = perfE + *hdr + (idx * len);
+ *hdr = len;
+ }
+ return data;
+}
+
+u32
+nvbios_perfSp(struct nvkm_bios *bios, u32 perfE, int idx,
+ u8 *ver, u8 *hdr, u8 cnt, u8 len,
+ struct nvbios_perfS *info)
+{
+ u32 data = nvbios_perfSe(bios, perfE, idx, ver, hdr, cnt, len);
+ memset(info, 0x00, sizeof(*info));
+ switch (!!data * *ver) {
+ case 0x40:
+ info->v40.freq = (nv_ro16(bios, data + 0x00) & 0x3fff) * 1000;
+ break;
+ default:
+ break;
+ }
+ return data;
+}
+
+int
+nvbios_perf_fan_parse(struct nvkm_bios *bios,
+ struct nvbios_perf_fan *fan)
+{
+ u8 ver, hdr, cnt, len, snr, ssz;
+ u16 perf = nvbios_perf_table(bios, &ver, &hdr, &cnt, &len, &snr, &ssz);
+ if (!perf)
+ return -ENODEV;
+
+ if (ver >= 0x20 && ver < 0x40 && hdr > 6)
+ fan->pwm_divisor = nv_ro16(bios, perf + 6);
+ else
+ fan->pwm_divisor = 0;
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/pll.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/pll.c
new file mode 100644
index 000000000000..ebd402e19dbf
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/pll.c
@@ -0,0 +1,417 @@
+/*
+ * Copyright 2005-2006 Erik Waling
+ * Copyright 2006 Stephane Marchesin
+ * Copyright 2007-2009 Stuart Bennett
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
+ * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include <subdev/bios.h>
+#include <subdev/bios/bit.h>
+#include <subdev/bios/bmp.h>
+#include <subdev/bios/pll.h>
+#include <subdev/vga.h>
+
+#include <core/device.h>
+
+struct pll_mapping {
+ u8 type;
+ u32 reg;
+};
+
+static struct pll_mapping
+nv04_pll_mapping[] = {
+ { PLL_CORE , 0x680500 },
+ { PLL_MEMORY, 0x680504 },
+ { PLL_VPLL0 , 0x680508 },
+ { PLL_VPLL1 , 0x680520 },
+ {}
+};
+
+static struct pll_mapping
+nv40_pll_mapping[] = {
+ { PLL_CORE , 0x004000 },
+ { PLL_MEMORY, 0x004020 },
+ { PLL_VPLL0 , 0x680508 },
+ { PLL_VPLL1 , 0x680520 },
+ {}
+};
+
+static struct pll_mapping
+nv50_pll_mapping[] = {
+ { PLL_CORE , 0x004028 },
+ { PLL_SHADER, 0x004020 },
+ { PLL_UNK03 , 0x004000 },
+ { PLL_MEMORY, 0x004008 },
+ { PLL_UNK40 , 0x00e810 },
+ { PLL_UNK41 , 0x00e818 },
+ { PLL_UNK42 , 0x00e824 },
+ { PLL_VPLL0 , 0x614100 },
+ { PLL_VPLL1 , 0x614900 },
+ {}
+};
+
+static struct pll_mapping
+g84_pll_mapping[] = {
+ { PLL_CORE , 0x004028 },
+ { PLL_SHADER, 0x004020 },
+ { PLL_MEMORY, 0x004008 },
+ { PLL_VDEC , 0x004030 },
+ { PLL_UNK41 , 0x00e818 },
+ { PLL_VPLL0 , 0x614100 },
+ { PLL_VPLL1 , 0x614900 },
+ {}
+};
+
+static u16
+pll_limits_table(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
+{
+ struct bit_entry bit_C;
+
+ if (!bit_entry(bios, 'C', &bit_C) && bit_C.length >= 10) {
+ u16 data = nv_ro16(bios, bit_C.offset + 8);
+ if (data) {
+ *ver = nv_ro08(bios, data + 0);
+ *hdr = nv_ro08(bios, data + 1);
+ *len = nv_ro08(bios, data + 2);
+ *cnt = nv_ro08(bios, data + 3);
+ return data;
+ }
+ }
+
+ if (bmp_version(bios) >= 0x0524) {
+ u16 data = nv_ro16(bios, bios->bmp_offset + 142);
+ if (data) {
+ *ver = nv_ro08(bios, data + 0);
+ *hdr = 1;
+ *cnt = 1;
+ *len = 0x18;
+ return data;
+ }
+ }
+
+ *ver = 0x00;
+ return 0x0000;
+}
+
+static struct pll_mapping *
+pll_map(struct nvkm_bios *bios)
+{
+ switch (nv_device(bios)->card_type) {
+ case NV_04:
+ case NV_10:
+ case NV_11:
+ case NV_20:
+ case NV_30:
+ return nv04_pll_mapping;
+ break;
+ case NV_40:
+ return nv40_pll_mapping;
+ case NV_50:
+ if (nv_device(bios)->chipset == 0x50)
+ return nv50_pll_mapping;
+ else
+ if (nv_device(bios)->chipset < 0xa3 ||
+ nv_device(bios)->chipset == 0xaa ||
+ nv_device(bios)->chipset == 0xac)
+ return g84_pll_mapping;
+ default:
+ return NULL;
+ }
+}
+
+static u16
+pll_map_reg(struct nvkm_bios *bios, u32 reg, u32 *type, u8 *ver, u8 *len)
+{
+ struct pll_mapping *map;
+ u8 hdr, cnt;
+ u16 data;
+
+ data = pll_limits_table(bios, ver, &hdr, &cnt, len);
+ if (data && *ver >= 0x30) {
+ data += hdr;
+ while (cnt--) {
+ if (nv_ro32(bios, data + 3) == reg) {
+ *type = nv_ro08(bios, data + 0);
+ return data;
+ }
+ data += *len;
+ }
+ return 0x0000;
+ }
+
+ map = pll_map(bios);
+ while (map->reg) {
+ if (map->reg == reg && *ver >= 0x20) {
+ u16 addr = (data += hdr);
+ *type = map->type;
+ while (cnt--) {
+ if (nv_ro32(bios, data) == map->reg)
+ return data;
+ data += *len;
+ }
+ return addr;
+ } else
+ if (map->reg == reg) {
+ *type = map->type;
+ return data + 1;
+ }
+ map++;
+ }
+
+ return 0x0000;
+}
+
+static u16
+pll_map_type(struct nvkm_bios *bios, u8 type, u32 *reg, u8 *ver, u8 *len)
+{
+ struct pll_mapping *map;
+ u8 hdr, cnt;
+ u16 data;
+
+ data = pll_limits_table(bios, ver, &hdr, &cnt, len);
+ if (data && *ver >= 0x30) {
+ data += hdr;
+ while (cnt--) {
+ if (nv_ro08(bios, data + 0) == type) {
+ *reg = nv_ro32(bios, data + 3);
+ return data;
+ }
+ data += *len;
+ }
+ return 0x0000;
+ }
+
+ map = pll_map(bios);
+ while (map->reg) {
+ if (map->type == type && *ver >= 0x20) {
+ u16 addr = (data += hdr);
+ *reg = map->reg;
+ while (cnt--) {
+ if (nv_ro32(bios, data) == map->reg)
+ return data;
+ data += *len;
+ }
+ return addr;
+ } else
+ if (map->type == type) {
+ *reg = map->reg;
+ return data + 1;
+ }
+ map++;
+ }
+
+ return 0x0000;
+}
+
+int
+nvbios_pll_parse(struct nvkm_bios *bios, u32 type, struct nvbios_pll *info)
+{
+ u8 ver, len;
+ u32 reg = type;
+ u16 data;
+
+ if (type > PLL_MAX) {
+ reg = type;
+ data = pll_map_reg(bios, reg, &type, &ver, &len);
+ } else {
+ data = pll_map_type(bios, type, &reg, &ver, &len);
+ }
+
+ if (ver && !data)
+ return -ENOENT;
+
+ memset(info, 0, sizeof(*info));
+ info->type = type;
+ info->reg = reg;
+
+ switch (ver) {
+ case 0x00:
+ break;
+ case 0x10:
+ case 0x11:
+ info->vco1.min_freq = nv_ro32(bios, data + 0);
+ info->vco1.max_freq = nv_ro32(bios, data + 4);
+ info->vco2.min_freq = nv_ro32(bios, data + 8);
+ info->vco2.max_freq = nv_ro32(bios, data + 12);
+ info->vco1.min_inputfreq = nv_ro32(bios, data + 16);
+ info->vco2.min_inputfreq = nv_ro32(bios, data + 20);
+ info->vco1.max_inputfreq = INT_MAX;
+ info->vco2.max_inputfreq = INT_MAX;
+
+ info->max_p = 0x7;
+ info->max_p_usable = 0x6;
+
+ /* these values taken from nv30/31/36 */
+ switch (bios->version.chip) {
+ case 0x36:
+ info->vco1.min_n = 0x5;
+ break;
+ default:
+ info->vco1.min_n = 0x1;
+ break;
+ }
+ info->vco1.max_n = 0xff;
+ info->vco1.min_m = 0x1;
+ info->vco1.max_m = 0xd;
+
+ /*
+ * On nv30, 31, 36 (i.e. all cards with two stage PLLs with this
+ * table version (apart from nv35)), N2 is compared to
+ * maxN2 (0x46) and 10 * maxM2 (0x4), so set maxN2 to 0x28 and
+ * save a comparison
+ */
+ info->vco2.min_n = 0x4;
+ switch (bios->version.chip) {
+ case 0x30:
+ case 0x35:
+ info->vco2.max_n = 0x1f;
+ break;
+ default:
+ info->vco2.max_n = 0x28;
+ break;
+ }
+ info->vco2.min_m = 0x1;
+ info->vco2.max_m = 0x4;
+ break;
+ case 0x20:
+ case 0x21:
+ info->vco1.min_freq = nv_ro16(bios, data + 4) * 1000;
+ info->vco1.max_freq = nv_ro16(bios, data + 6) * 1000;
+ info->vco2.min_freq = nv_ro16(bios, data + 8) * 1000;
+ info->vco2.max_freq = nv_ro16(bios, data + 10) * 1000;
+ info->vco1.min_inputfreq = nv_ro16(bios, data + 12) * 1000;
+ info->vco2.min_inputfreq = nv_ro16(bios, data + 14) * 1000;
+ info->vco1.max_inputfreq = nv_ro16(bios, data + 16) * 1000;
+ info->vco2.max_inputfreq = nv_ro16(bios, data + 18) * 1000;
+ info->vco1.min_n = nv_ro08(bios, data + 20);
+ info->vco1.max_n = nv_ro08(bios, data + 21);
+ info->vco1.min_m = nv_ro08(bios, data + 22);
+ info->vco1.max_m = nv_ro08(bios, data + 23);
+ info->vco2.min_n = nv_ro08(bios, data + 24);
+ info->vco2.max_n = nv_ro08(bios, data + 25);
+ info->vco2.min_m = nv_ro08(bios, data + 26);
+ info->vco2.max_m = nv_ro08(bios, data + 27);
+
+ info->max_p = nv_ro08(bios, data + 29);
+ info->max_p_usable = info->max_p;
+ if (bios->version.chip < 0x60)
+ info->max_p_usable = 0x6;
+ info->bias_p = nv_ro08(bios, data + 30);
+
+ if (len > 0x22)
+ info->refclk = nv_ro32(bios, data + 31);
+ break;
+ case 0x30:
+ data = nv_ro16(bios, data + 1);
+
+ info->vco1.min_freq = nv_ro16(bios, data + 0) * 1000;
+ info->vco1.max_freq = nv_ro16(bios, data + 2) * 1000;
+ info->vco2.min_freq = nv_ro16(bios, data + 4) * 1000;
+ info->vco2.max_freq = nv_ro16(bios, data + 6) * 1000;
+ info->vco1.min_inputfreq = nv_ro16(bios, data + 8) * 1000;
+ info->vco2.min_inputfreq = nv_ro16(bios, data + 10) * 1000;
+ info->vco1.max_inputfreq = nv_ro16(bios, data + 12) * 1000;
+ info->vco2.max_inputfreq = nv_ro16(bios, data + 14) * 1000;
+ info->vco1.min_n = nv_ro08(bios, data + 16);
+ info->vco1.max_n = nv_ro08(bios, data + 17);
+ info->vco1.min_m = nv_ro08(bios, data + 18);
+ info->vco1.max_m = nv_ro08(bios, data + 19);
+ info->vco2.min_n = nv_ro08(bios, data + 20);
+ info->vco2.max_n = nv_ro08(bios, data + 21);
+ info->vco2.min_m = nv_ro08(bios, data + 22);
+ info->vco2.max_m = nv_ro08(bios, data + 23);
+ info->max_p_usable = info->max_p = nv_ro08(bios, data + 25);
+ info->bias_p = nv_ro08(bios, data + 27);
+ info->refclk = nv_ro32(bios, data + 28);
+ break;
+ case 0x40:
+ info->refclk = nv_ro16(bios, data + 9) * 1000;
+ data = nv_ro16(bios, data + 1);
+
+ info->vco1.min_freq = nv_ro16(bios, data + 0) * 1000;
+ info->vco1.max_freq = nv_ro16(bios, data + 2) * 1000;
+ info->vco1.min_inputfreq = nv_ro16(bios, data + 4) * 1000;
+ info->vco1.max_inputfreq = nv_ro16(bios, data + 6) * 1000;
+ info->vco1.min_m = nv_ro08(bios, data + 8);
+ info->vco1.max_m = nv_ro08(bios, data + 9);
+ info->vco1.min_n = nv_ro08(bios, data + 10);
+ info->vco1.max_n = nv_ro08(bios, data + 11);
+ info->min_p = nv_ro08(bios, data + 12);
+ info->max_p = nv_ro08(bios, data + 13);
+ break;
+ default:
+ nv_error(bios, "unknown pll limits version 0x%02x\n", ver);
+ return -EINVAL;
+ }
+
+ if (!info->refclk) {
+ info->refclk = nv_device(bios)->crystal;
+ if (bios->version.chip == 0x51) {
+ u32 sel_clk = nv_rd32(bios, 0x680524);
+ if ((info->reg == 0x680508 && sel_clk & 0x20) ||
+ (info->reg == 0x680520 && sel_clk & 0x80)) {
+ if (nv_rdvgac(bios, 0, 0x27) < 0xa3)
+ info->refclk = 200000;
+ else
+ info->refclk = 25000;
+ }
+ }
+ }
+
+ /*
+ * By now any valid limit table ought to have set a max frequency for
+ * vco1, so if it's zero it's either a pre limit table bios, or one
+ * with an empty limit table (seen on nv18)
+ */
+ if (!info->vco1.max_freq) {
+ info->vco1.max_freq = nv_ro32(bios, bios->bmp_offset + 67);
+ info->vco1.min_freq = nv_ro32(bios, bios->bmp_offset + 71);
+ if (bmp_version(bios) < 0x0506) {
+ info->vco1.max_freq = 256000;
+ info->vco1.min_freq = 128000;
+ }
+
+ info->vco1.min_inputfreq = 0;
+ info->vco1.max_inputfreq = INT_MAX;
+ info->vco1.min_n = 0x1;
+ info->vco1.max_n = 0xff;
+ info->vco1.min_m = 0x1;
+
+ if (nv_device(bios)->crystal == 13500) {
+ /* nv05 does this, nv11 doesn't, nv10 unknown */
+ if (bios->version.chip < 0x11)
+ info->vco1.min_m = 0x7;
+ info->vco1.max_m = 0xd;
+ } else {
+ if (bios->version.chip < 0x11)
+ info->vco1.min_m = 0x8;
+ info->vco1.max_m = 0xe;
+ }
+
+ if (bios->version.chip < 0x17 ||
+ bios->version.chip == 0x1a ||
+ bios->version.chip == 0x20)
+ info->max_p = 4;
+ else
+ info->max_p = 5;
+ info->max_p_usable = info->max_p;
+ }
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/pmu.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/pmu.c
new file mode 100644
index 000000000000..20c5ce0cd573
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/pmu.c
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2014 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs <bskeggs@redhat.com>
+ */
+#include <subdev/bios.h>
+#include <subdev/bios/bit.h>
+#include <subdev/bios/image.h>
+#include <subdev/bios/pmu.h>
+
+static u32
+weirdo_pointer(struct nvkm_bios *bios, u32 data)
+{
+ struct nvbios_image image;
+ int idx = 0;
+ if (nvbios_image(bios, idx++, &image)) {
+ data -= image.size;
+ while (nvbios_image(bios, idx++, &image)) {
+ if (image.type == 0xe0)
+ return image.base + data;
+ }
+ }
+ return 0;
+}
+
+u32
+nvbios_pmuTe(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
+{
+ struct bit_entry bit_p;
+ u32 data = 0;
+
+ if (!bit_entry(bios, 'p', &bit_p)) {
+ if (bit_p.version == 2 && bit_p.length >= 4)
+ data = nv_ro32(bios, bit_p.offset + 0x00);
+ if ((data = weirdo_pointer(bios, data))) {
+ *ver = nv_ro08(bios, data + 0x00); /* maybe? */
+ *hdr = nv_ro08(bios, data + 0x01);
+ *len = nv_ro08(bios, data + 0x02);
+ *cnt = nv_ro08(bios, data + 0x03);
+ }
+ }
+
+ return data;
+}
+
+u32
+nvbios_pmuTp(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
+ struct nvbios_pmuT *info)
+{
+ u32 data = nvbios_pmuTe(bios, ver, hdr, cnt, len);
+ memset(info, 0x00, sizeof(*info));
+ switch (!!data * *ver) {
+ default:
+ break;
+ }
+ return data;
+}
+
+u32
+nvbios_pmuEe(struct nvkm_bios *bios, int idx, u8 *ver, u8 *hdr)
+{
+ u8 cnt, len;
+ u32 data = nvbios_pmuTe(bios, ver, hdr, &cnt, &len);
+ if (data && idx < cnt) {
+ data = data + *hdr + (idx * len);
+ *hdr = len;
+ return data;
+ }
+ return 0;
+}
+
+u32
+nvbios_pmuEp(struct nvkm_bios *bios, int idx, u8 *ver, u8 *hdr,
+ struct nvbios_pmuE *info)
+{
+ u32 data = nvbios_pmuEe(bios, idx, ver, hdr);
+ memset(info, 0x00, sizeof(*info));
+ switch (!!data * *ver) {
+ default:
+ info->type = nv_ro08(bios, data + 0x00);
+ info->data = nv_ro32(bios, data + 0x02);
+ break;
+ }
+ return data;
+}
+
+bool
+nvbios_pmuRm(struct nvkm_bios *bios, u8 type, struct nvbios_pmuR *info)
+{
+ struct nvbios_pmuE pmuE;
+ u8 ver, hdr, idx = 0;
+ u32 data;
+ memset(info, 0x00, sizeof(*info));
+ while ((data = nvbios_pmuEp(bios, idx++, &ver, &hdr, &pmuE))) {
+ if ( pmuE.type == type &&
+ (data = weirdo_pointer(bios, pmuE.data))) {
+ info->init_addr_pmu = nv_ro32(bios, data + 0x08);
+ info->args_addr_pmu = nv_ro32(bios, data + 0x0c);
+ info->boot_addr = data + 0x30;
+ info->boot_addr_pmu = nv_ro32(bios, data + 0x10) +
+ nv_ro32(bios, data + 0x18);
+ info->boot_size = nv_ro32(bios, data + 0x1c) -
+ nv_ro32(bios, data + 0x18);
+ info->code_addr = info->boot_addr + info->boot_size;
+ info->code_addr_pmu = info->boot_addr_pmu +
+ info->boot_size;
+ info->code_size = nv_ro32(bios, data + 0x20);
+ info->data_addr = data + 0x30 +
+ nv_ro32(bios, data + 0x24);
+ info->data_addr_pmu = nv_ro32(bios, data + 0x28);
+ info->data_size = nv_ro32(bios, data + 0x2c);
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/priv.h b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/priv.h
new file mode 100644
index 000000000000..95e4fa1531d6
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/priv.h
@@ -0,0 +1,23 @@
+#ifndef __NVKM_BIOS_PRIV_H__
+#define __NVKM_BIOS_PRIV_H__
+#include <subdev/bios.h>
+
+struct nvbios_source {
+ const char *name;
+ void *(*init)(struct nvkm_bios *, const char *);
+ void (*fini)(void *);
+ u32 (*read)(void *, u32 offset, u32 length, struct nvkm_bios *);
+ bool rw;
+};
+
+int nvbios_extend(struct nvkm_bios *, u32 length);
+int nvbios_shadow(struct nvkm_bios *);
+
+extern const struct nvbios_source nvbios_rom;
+extern const struct nvbios_source nvbios_ramin;
+extern const struct nvbios_source nvbios_acpi_fast;
+extern const struct nvbios_source nvbios_acpi_slow;
+extern const struct nvbios_source nvbios_pcirom;
+extern const struct nvbios_source nvbios_platform;
+extern const struct nvbios_source nvbios_of;
+#endif
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/ramcfg.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/ramcfg.c
new file mode 100644
index 000000000000..a17b221119b2
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/ramcfg.c
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs <bskeggs@redhat.com>
+ */
+#include <subdev/bios.h>
+#include <subdev/bios/bit.h>
+#include <subdev/bios/ramcfg.h>
+#include <subdev/bios/M0203.h>
+
+static u8
+nvbios_ramcfg_strap(struct nvkm_subdev *subdev)
+{
+ return (nv_rd32(subdev, 0x101000) & 0x0000003c) >> 2;
+}
+
+u8
+nvbios_ramcfg_count(struct nvkm_bios *bios)
+{
+ struct bit_entry bit_M;
+
+ if (!bit_entry(bios, 'M', &bit_M)) {
+ if (bit_M.version == 1 && bit_M.length >= 5)
+ return nv_ro08(bios, bit_M.offset + 2);
+ if (bit_M.version == 2 && bit_M.length >= 3)
+ return nv_ro08(bios, bit_M.offset + 0);
+ }
+
+ return 0x00;
+}
+
+u8
+nvbios_ramcfg_index(struct nvkm_subdev *subdev)
+{
+ struct nvkm_bios *bios = nvkm_bios(subdev);
+ u8 strap = nvbios_ramcfg_strap(subdev);
+ u32 xlat = 0x00000000;
+ struct bit_entry bit_M;
+ struct nvbios_M0203E M0203E;
+ u8 ver, hdr;
+
+ if (!bit_entry(bios, 'M', &bit_M)) {
+ if (bit_M.version == 1 && bit_M.length >= 5)
+ xlat = nv_ro16(bios, bit_M.offset + 3);
+ if (bit_M.version == 2 && bit_M.length >= 3) {
+ /*XXX: is M ever shorter than this?
+ * if not - what is xlat used for now?
+ * also - sigh..
+ */
+ if (bit_M.length >= 7 &&
+ nvbios_M0203Em(bios, strap, &ver, &hdr, &M0203E))
+ return M0203E.group;
+ xlat = nv_ro16(bios, bit_M.offset + 1);
+ }
+ }
+
+ if (xlat)
+ strap = nv_ro08(bios, xlat + strap);
+ return strap;
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/rammap.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/rammap.c
new file mode 100644
index 000000000000..8b17bb4b220c
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/rammap.c
@@ -0,0 +1,211 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include <subdev/bios.h>
+#include <subdev/bios/bit.h>
+#include <subdev/bios/rammap.h>
+
+u32
+nvbios_rammapTe(struct nvkm_bios *bios, u8 *ver, u8 *hdr,
+ u8 *cnt, u8 *len, u8 *snr, u8 *ssz)
+{
+ struct bit_entry bit_P;
+ u16 rammap = 0x0000;
+
+ if (!bit_entry(bios, 'P', &bit_P)) {
+ if (bit_P.version == 2)
+ rammap = nv_ro16(bios, bit_P.offset + 4);
+
+ if (rammap) {
+ *ver = nv_ro08(bios, rammap + 0);
+ switch (*ver) {
+ case 0x10:
+ case 0x11:
+ *hdr = nv_ro08(bios, rammap + 1);
+ *cnt = nv_ro08(bios, rammap + 5);
+ *len = nv_ro08(bios, rammap + 2);
+ *snr = nv_ro08(bios, rammap + 4);
+ *ssz = nv_ro08(bios, rammap + 3);
+ return rammap;
+ default:
+ break;
+ }
+ }
+ }
+
+ return 0x0000;
+}
+
+u32
+nvbios_rammapEe(struct nvkm_bios *bios, int idx,
+ u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
+{
+ u8 snr, ssz;
+ u16 rammap = nvbios_rammapTe(bios, ver, hdr, cnt, len, &snr, &ssz);
+ if (rammap && idx < *cnt) {
+ rammap = rammap + *hdr + (idx * (*len + (snr * ssz)));
+ *hdr = *len;
+ *cnt = snr;
+ *len = ssz;
+ return rammap;
+ }
+ return 0x0000;
+}
+
+u32
+nvbios_rammapEp(struct nvkm_bios *bios, int idx,
+ u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_ramcfg *p)
+{
+ u32 data = nvbios_rammapEe(bios, idx, ver, hdr, cnt, len), temp;
+ memset(p, 0x00, sizeof(*p));
+ p->rammap_ver = *ver;
+ p->rammap_hdr = *hdr;
+ switch (!!data * *ver) {
+ case 0x10:
+ p->rammap_min = nv_ro16(bios, data + 0x00);
+ p->rammap_max = nv_ro16(bios, data + 0x02);
+ p->rammap_10_04_02 = (nv_ro08(bios, data + 0x04) & 0x02) >> 1;
+ p->rammap_10_04_08 = (nv_ro08(bios, data + 0x04) & 0x08) >> 3;
+ break;
+ case 0x11:
+ p->rammap_min = nv_ro16(bios, data + 0x00);
+ p->rammap_max = nv_ro16(bios, data + 0x02);
+ p->rammap_11_08_01 = (nv_ro08(bios, data + 0x08) & 0x01) >> 0;
+ p->rammap_11_08_0c = (nv_ro08(bios, data + 0x08) & 0x0c) >> 2;
+ p->rammap_11_08_10 = (nv_ro08(bios, data + 0x08) & 0x10) >> 4;
+ temp = nv_ro32(bios, data + 0x09);
+ p->rammap_11_09_01ff = (temp & 0x000001ff) >> 0;
+ p->rammap_11_0a_03fe = (temp & 0x0003fe00) >> 9;
+ p->rammap_11_0a_0400 = (temp & 0x00040000) >> 18;
+ p->rammap_11_0a_0800 = (temp & 0x00080000) >> 19;
+ p->rammap_11_0b_01f0 = (temp & 0x01f00000) >> 20;
+ p->rammap_11_0b_0200 = (temp & 0x02000000) >> 25;
+ p->rammap_11_0b_0400 = (temp & 0x04000000) >> 26;
+ p->rammap_11_0b_0800 = (temp & 0x08000000) >> 27;
+ p->rammap_11_0d = nv_ro08(bios, data + 0x0d);
+ p->rammap_11_0e = nv_ro08(bios, data + 0x0e);
+ p->rammap_11_0f = nv_ro08(bios, data + 0x0f);
+ p->rammap_11_11_0c = (nv_ro08(bios, data + 0x11) & 0x0c) >> 2;
+ break;
+ default:
+ data = 0;
+ break;
+ }
+ return data;
+}
+
+u32
+nvbios_rammapEm(struct nvkm_bios *bios, u16 mhz,
+ u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_ramcfg *info)
+{
+ int idx = 0;
+ u32 data;
+ while ((data = nvbios_rammapEp(bios, idx++, ver, hdr, cnt, len, info))) {
+ if (mhz >= info->rammap_min && mhz <= info->rammap_max)
+ break;
+ }
+ return data;
+}
+
+u32
+nvbios_rammapSe(struct nvkm_bios *bios, u32 data,
+ u8 ever, u8 ehdr, u8 ecnt, u8 elen, int idx, u8 *ver, u8 *hdr)
+{
+ if (idx < ecnt) {
+ data = data + ehdr + (idx * elen);
+ *ver = ever;
+ *hdr = elen;
+ return data;
+ }
+ return 0;
+}
+
+u32
+nvbios_rammapSp(struct nvkm_bios *bios, u32 data,
+ u8 ever, u8 ehdr, u8 ecnt, u8 elen, int idx,
+ u8 *ver, u8 *hdr, struct nvbios_ramcfg *p)
+{
+ data = nvbios_rammapSe(bios, data, ever, ehdr, ecnt, elen, idx, ver, hdr);
+ p->ramcfg_ver = *ver;
+ p->ramcfg_hdr = *hdr;
+ switch (!!data * *ver) {
+ case 0x10:
+ p->ramcfg_timing = nv_ro08(bios, data + 0x01);
+ p->ramcfg_10_02_01 = (nv_ro08(bios, data + 0x02) & 0x01) >> 0;
+ p->ramcfg_10_02_02 = (nv_ro08(bios, data + 0x02) & 0x02) >> 1;
+ p->ramcfg_10_02_04 = (nv_ro08(bios, data + 0x02) & 0x04) >> 2;
+ p->ramcfg_10_02_08 = (nv_ro08(bios, data + 0x02) & 0x08) >> 3;
+ p->ramcfg_10_02_10 = (nv_ro08(bios, data + 0x02) & 0x10) >> 4;
+ p->ramcfg_10_02_20 = (nv_ro08(bios, data + 0x02) & 0x20) >> 5;
+ p->ramcfg_10_DLLoff = (nv_ro08(bios, data + 0x02) & 0x40) >> 6;
+ p->ramcfg_10_03_0f = (nv_ro08(bios, data + 0x03) & 0x0f) >> 0;
+ p->ramcfg_10_04_01 = (nv_ro08(bios, data + 0x04) & 0x01) >> 0;
+ p->ramcfg_10_05 = (nv_ro08(bios, data + 0x05) & 0xff) >> 0;
+ p->ramcfg_10_06 = (nv_ro08(bios, data + 0x06) & 0xff) >> 0;
+ p->ramcfg_10_07 = (nv_ro08(bios, data + 0x07) & 0xff) >> 0;
+ p->ramcfg_10_08 = (nv_ro08(bios, data + 0x08) & 0xff) >> 0;
+ p->ramcfg_10_09_0f = (nv_ro08(bios, data + 0x09) & 0x0f) >> 0;
+ p->ramcfg_10_09_f0 = (nv_ro08(bios, data + 0x09) & 0xf0) >> 4;
+ break;
+ case 0x11:
+ p->ramcfg_timing = nv_ro08(bios, data + 0x00);
+ p->ramcfg_11_01_01 = (nv_ro08(bios, data + 0x01) & 0x01) >> 0;
+ p->ramcfg_11_01_02 = (nv_ro08(bios, data + 0x01) & 0x02) >> 1;
+ p->ramcfg_11_01_04 = (nv_ro08(bios, data + 0x01) & 0x04) >> 2;
+ p->ramcfg_11_01_08 = (nv_ro08(bios, data + 0x01) & 0x08) >> 3;
+ p->ramcfg_11_01_10 = (nv_ro08(bios, data + 0x01) & 0x10) >> 4;
+ p->ramcfg_11_01_20 = (nv_ro08(bios, data + 0x01) & 0x20) >> 5;
+ p->ramcfg_11_01_40 = (nv_ro08(bios, data + 0x01) & 0x40) >> 6;
+ p->ramcfg_11_01_80 = (nv_ro08(bios, data + 0x01) & 0x80) >> 7;
+ p->ramcfg_11_02_03 = (nv_ro08(bios, data + 0x02) & 0x03) >> 0;
+ p->ramcfg_11_02_04 = (nv_ro08(bios, data + 0x02) & 0x04) >> 2;
+ p->ramcfg_11_02_08 = (nv_ro08(bios, data + 0x02) & 0x08) >> 3;
+ p->ramcfg_11_02_10 = (nv_ro08(bios, data + 0x02) & 0x10) >> 4;
+ p->ramcfg_11_02_40 = (nv_ro08(bios, data + 0x02) & 0x40) >> 6;
+ p->ramcfg_11_02_80 = (nv_ro08(bios, data + 0x02) & 0x80) >> 7;
+ p->ramcfg_11_03_0f = (nv_ro08(bios, data + 0x03) & 0x0f) >> 0;
+ p->ramcfg_11_03_30 = (nv_ro08(bios, data + 0x03) & 0x30) >> 4;
+ p->ramcfg_11_03_c0 = (nv_ro08(bios, data + 0x03) & 0xc0) >> 6;
+ p->ramcfg_11_03_f0 = (nv_ro08(bios, data + 0x03) & 0xf0) >> 4;
+ p->ramcfg_11_04 = (nv_ro08(bios, data + 0x04) & 0xff) >> 0;
+ p->ramcfg_11_06 = (nv_ro08(bios, data + 0x06) & 0xff) >> 0;
+ p->ramcfg_11_07_02 = (nv_ro08(bios, data + 0x07) & 0x02) >> 1;
+ p->ramcfg_11_07_04 = (nv_ro08(bios, data + 0x07) & 0x04) >> 2;
+ p->ramcfg_11_07_08 = (nv_ro08(bios, data + 0x07) & 0x08) >> 3;
+ p->ramcfg_11_07_10 = (nv_ro08(bios, data + 0x07) & 0x10) >> 4;
+ p->ramcfg_11_07_40 = (nv_ro08(bios, data + 0x07) & 0x40) >> 6;
+ p->ramcfg_11_07_80 = (nv_ro08(bios, data + 0x07) & 0x80) >> 7;
+ p->ramcfg_11_08_01 = (nv_ro08(bios, data + 0x08) & 0x01) >> 0;
+ p->ramcfg_11_08_02 = (nv_ro08(bios, data + 0x08) & 0x02) >> 1;
+ p->ramcfg_11_08_04 = (nv_ro08(bios, data + 0x08) & 0x04) >> 2;
+ p->ramcfg_11_08_08 = (nv_ro08(bios, data + 0x08) & 0x08) >> 3;
+ p->ramcfg_11_08_10 = (nv_ro08(bios, data + 0x08) & 0x10) >> 4;
+ p->ramcfg_11_08_20 = (nv_ro08(bios, data + 0x08) & 0x20) >> 5;
+ p->ramcfg_11_09 = (nv_ro08(bios, data + 0x09) & 0xff) >> 0;
+ break;
+ default:
+ data = 0;
+ break;
+ }
+ return data;
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadow.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadow.c
new file mode 100644
index 000000000000..8c2b7cba5cff
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadow.c
@@ -0,0 +1,272 @@
+/*
+ * Copyright 2014 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs <bskeggs@redhat.com>
+ */
+#include "priv.h"
+
+#include <core/device.h>
+#include <core/option.h>
+#include <subdev/bios.h>
+#include <subdev/bios/image.h>
+
+struct shadow {
+ struct nvkm_oclass base;
+ u32 skip;
+ const struct nvbios_source *func;
+ void *data;
+ u32 size;
+ int score;
+};
+
+static bool
+shadow_fetch(struct nvkm_bios *bios, u32 upto)
+{
+ struct shadow *mthd = (void *)nv_object(bios)->oclass;
+ const u32 limit = (upto + 3) & ~3;
+ const u32 start = bios->size;
+ void *data = mthd->data;
+ if (nvbios_extend(bios, limit) > 0) {
+ u32 read = mthd->func->read(data, start, limit - start, bios);
+ bios->size = start + read;
+ }
+ return bios->size >= limit;
+}
+
+static u8
+shadow_rd08(struct nvkm_object *object, u64 addr)
+{
+ struct nvkm_bios *bios = (void *)object;
+ if (shadow_fetch(bios, addr + 1))
+ return bios->data[addr];
+ return 0x00;
+}
+
+static u16
+shadow_rd16(struct nvkm_object *object, u64 addr)
+{
+ struct nvkm_bios *bios = (void *)object;
+ if (shadow_fetch(bios, addr + 2))
+ return get_unaligned_le16(&bios->data[addr]);
+ return 0x0000;
+}
+
+static u32
+shadow_rd32(struct nvkm_object *object, u64 addr)
+{
+ struct nvkm_bios *bios = (void *)object;
+ if (shadow_fetch(bios, addr + 4))
+ return get_unaligned_le32(&bios->data[addr]);
+ return 0x00000000;
+}
+
+static struct nvkm_oclass
+shadow_class = {
+ .handle = NV_SUBDEV(VBIOS, 0x00),
+ .ofuncs = &(struct nvkm_ofuncs) {
+ .rd08 = shadow_rd08,
+ .rd16 = shadow_rd16,
+ .rd32 = shadow_rd32,
+ },
+};
+
+static int
+shadow_image(struct nvkm_bios *bios, int idx, struct shadow *mthd)
+{
+ struct nvbios_image image;
+ int score = 1;
+
+ if (!nvbios_image(bios, idx, &image)) {
+ nv_debug(bios, "image %d invalid\n", idx);
+ return 0;
+ }
+ nv_debug(bios, "%08x: type %02x, %d bytes\n",
+ image.base, image.type, image.size);
+
+ if (!shadow_fetch(bios, image.size)) {
+ nv_debug(bios, "%08x: fetch failed\n", image.base);
+ return 0;
+ }
+
+ switch (image.type) {
+ case 0x00:
+ if (nvbios_checksum(&bios->data[image.base], image.size)) {
+ nv_debug(bios, "%08x: checksum failed\n", image.base);
+ if (mthd->func->rw)
+ score += 1;
+ score += 1;
+ } else {
+ score += 3;
+ }
+ break;
+ default:
+ score += 3;
+ break;
+ }
+
+ if (!image.last)
+ score += shadow_image(bios, idx + 1, mthd);
+ return score;
+}
+
+static int
+shadow_score(struct nvkm_bios *bios, struct shadow *mthd)
+{
+ struct nvkm_oclass *oclass = nv_object(bios)->oclass;
+ int score;
+ nv_object(bios)->oclass = &mthd->base;
+ score = shadow_image(bios, 0, mthd);
+ nv_object(bios)->oclass = oclass;
+ return score;
+
+}
+
+static int
+shadow_method(struct nvkm_bios *bios, struct shadow *mthd, const char *name)
+{
+ const struct nvbios_source *func = mthd->func;
+ if (func->name) {
+ nv_debug(bios, "trying %s...\n", name ? name : func->name);
+ if (func->init) {
+ mthd->data = func->init(bios, name);
+ if (IS_ERR(mthd->data)) {
+ mthd->data = NULL;
+ return 0;
+ }
+ }
+ mthd->score = shadow_score(bios, mthd);
+ if (func->fini)
+ func->fini(mthd->data);
+ nv_debug(bios, "scored %d\n", mthd->score);
+ mthd->data = bios->data;
+ mthd->size = bios->size;
+ bios->data = NULL;
+ bios->size = 0;
+ }
+ return mthd->score;
+}
+
+static u32
+shadow_fw_read(void *data, u32 offset, u32 length, struct nvkm_bios *bios)
+{
+ const struct firmware *fw = data;
+ if (offset + length <= fw->size) {
+ memcpy(bios->data + offset, fw->data + offset, length);
+ return length;
+ }
+ return 0;
+}
+
+static void *
+shadow_fw_init(struct nvkm_bios *bios, const char *name)
+{
+ struct device *dev = &nv_device(bios)->pdev->dev;
+ const struct firmware *fw;
+ int ret = request_firmware(&fw, name, dev);
+ if (ret)
+ return ERR_PTR(-ENOENT);
+ return (void *)fw;
+}
+
+static const struct nvbios_source
+shadow_fw = {
+ .name = "firmware",
+ .init = shadow_fw_init,
+ .fini = (void(*)(void *))release_firmware,
+ .read = shadow_fw_read,
+ .rw = false,
+};
+
+int
+nvbios_shadow(struct nvkm_bios *bios)
+{
+ struct shadow mthds[] = {
+ { shadow_class, 0, &nvbios_of },
+ { shadow_class, 0, &nvbios_ramin },
+ { shadow_class, 0, &nvbios_rom },
+ { shadow_class, 0, &nvbios_acpi_fast },
+ { shadow_class, 4, &nvbios_acpi_slow },
+ { shadow_class, 1, &nvbios_pcirom },
+ { shadow_class, 1, &nvbios_platform },
+ { shadow_class }
+ }, *mthd = mthds, *best = NULL;
+ const char *optarg;
+ char *source;
+ int optlen;
+
+ /* handle user-specified bios source */
+ optarg = nvkm_stropt(nv_device(bios)->cfgopt, "NvBios", &optlen);
+ source = optarg ? kstrndup(optarg, optlen, GFP_KERNEL) : NULL;
+ if (source) {
+ /* try to match one of the built-in methods */
+ for (mthd = mthds; mthd->func; mthd++) {
+ if (mthd->func->name &&
+ !strcasecmp(source, mthd->func->name)) {
+ best = mthd;
+ if (shadow_method(bios, mthd, NULL))
+ break;
+ }
+ }
+
+ /* otherwise, attempt to load as firmware */
+ if (!best && (best = mthd)) {
+ mthd->func = &shadow_fw;
+ shadow_method(bios, mthd, source);
+ mthd->func = NULL;
+ }
+
+ if (!best->score) {
+ nv_error(bios, "%s invalid\n", source);
+ kfree(source);
+ source = NULL;
+ }
+ }
+
+ /* scan all potential bios sources, looking for best image */
+ if (!best || !best->score) {
+ for (mthd = mthds, best = mthd; mthd->func; mthd++) {
+ if (!mthd->skip || best->score < mthd->skip) {
+ if (shadow_method(bios, mthd, NULL)) {
+ if (mthd->score > best->score)
+ best = mthd;
+ }
+ }
+ }
+ }
+
+ /* cleanup the ones we didn't use */
+ for (mthd = mthds; mthd->func; mthd++) {
+ if (mthd != best)
+ kfree(mthd->data);
+ }
+
+ if (!best->score) {
+ nv_fatal(bios, "unable to locate usable image\n");
+ return -EINVAL;
+ }
+
+ nv_info(bios, "using image from %s\n", best->func ?
+ best->func->name : source);
+ bios->data = best->data;
+ bios->size = best->size;
+ kfree(source);
+ return 0;
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowacpi.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowacpi.c
new file mode 100644
index 000000000000..1fbd93bbb561
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowacpi.c
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#include "priv.h"
+
+#include <core/device.h>
+
+#if defined(CONFIG_ACPI) && defined(CONFIG_X86)
+int nouveau_acpi_get_bios_chunk(uint8_t *bios, int offset, int len);
+bool nouveau_acpi_rom_supported(struct pci_dev *pdev);
+#else
+static inline bool
+nouveau_acpi_rom_supported(struct pci_dev *pdev)
+{
+ return false;
+}
+
+static inline int
+nouveau_acpi_get_bios_chunk(uint8_t *bios, int offset, int len)
+{
+ return -EINVAL;
+}
+#endif
+
+/* This version of the shadow function disobeys the ACPI spec and tries
+ * to fetch in units of more than 4KiB at a time. This is a LOT faster
+ * on some systems, such as Lenovo W530.
+ */
+static u32
+acpi_read_fast(void *data, u32 offset, u32 length, struct nvkm_bios *bios)
+{
+ u32 limit = (offset + length + 0xfff) & ~0xfff;
+ u32 start = offset & ~0x00000fff;
+ u32 fetch = limit - start;
+
+ if (nvbios_extend(bios, limit) > 0) {
+ int ret = nouveau_acpi_get_bios_chunk(bios->data, start, fetch);
+ if (ret == fetch)
+ return fetch;
+ }
+
+ return 0;
+}
+
+/* Other systems, such as the one in fdo#55948, will report a success
+ * but only return 4KiB of data. The common bios fetching logic will
+ * detect an invalid image, and fall back to this version of the read
+ * function.
+ */
+static u32
+acpi_read_slow(void *data, u32 offset, u32 length, struct nvkm_bios *bios)
+{
+ u32 limit = (offset + length + 0xfff) & ~0xfff;
+ u32 start = offset & ~0xfff;
+ u32 fetch = 0;
+
+ if (nvbios_extend(bios, limit) > 0) {
+ while (start + fetch < limit) {
+ int ret = nouveau_acpi_get_bios_chunk(bios->data,
+ start + fetch,
+ 0x1000);
+ if (ret != 0x1000)
+ break;
+ fetch += 0x1000;
+ }
+ }
+
+ return fetch;
+}
+
+static void *
+acpi_init(struct nvkm_bios *bios, const char *name)
+{
+ if (!nouveau_acpi_rom_supported(nv_device(bios)->pdev))
+ return ERR_PTR(-ENODEV);
+ return NULL;
+}
+
+const struct nvbios_source
+nvbios_acpi_fast = {
+ .name = "ACPI",
+ .init = acpi_init,
+ .read = acpi_read_fast,
+ .rw = false,
+};
+
+const struct nvbios_source
+nvbios_acpi_slow = {
+ .name = "ACPI",
+ .init = acpi_init,
+ .read = acpi_read_slow,
+ .rw = false,
+};
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowof.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowof.c
new file mode 100644
index 000000000000..4c19a7dba803
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowof.c
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#include "priv.h"
+
+#include <core/device.h>
+
+#if defined(__powerpc__)
+struct priv {
+ const void __iomem *data;
+ int size;
+};
+
+static u32
+of_read(void *data, u32 offset, u32 length, struct nvkm_bios *bios)
+{
+ struct priv *priv = data;
+ if (offset + length <= priv->size) {
+ memcpy_fromio(bios->data + offset, priv->data + offset, length);
+ return length;
+ }
+ return 0;
+}
+
+static void *
+of_init(struct nvkm_bios *bios, const char *name)
+{
+ struct pci_dev *pdev = nv_device(bios)->pdev;
+ struct device_node *dn;
+ struct priv *priv;
+ if (!(dn = pci_device_to_OF_node(pdev)))
+ return ERR_PTR(-ENODEV);
+ if (!(priv = kzalloc(sizeof(*priv), GFP_KERNEL)))
+ return ERR_PTR(-ENOMEM);
+ if ((priv->data = of_get_property(dn, "NVDA,BMP", &priv->size)))
+ return priv;
+ kfree(priv);
+ return ERR_PTR(-EINVAL);
+}
+
+const struct nvbios_source
+nvbios_of = {
+ .name = "OpenFirmware",
+ .init = of_init,
+ .fini = (void(*)(void *))kfree,
+ .read = of_read,
+ .rw = false,
+};
+#else
+const struct nvbios_source
+nvbios_of = {
+};
+#endif
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowpci.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowpci.c
new file mode 100644
index 000000000000..1b045483dc87
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowpci.c
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#include "priv.h"
+
+#include <core/device.h>
+
+struct priv {
+ struct pci_dev *pdev;
+ void __iomem *rom;
+ size_t size;
+};
+
+static u32
+pcirom_read(void *data, u32 offset, u32 length, struct nvkm_bios *bios)
+{
+ struct priv *priv = data;
+ if (offset + length <= priv->size) {
+ memcpy_fromio(bios->data + offset, priv->rom + offset, length);
+ return length;
+ }
+ return 0;
+}
+
+static void
+pcirom_fini(void *data)
+{
+ struct priv *priv = data;
+ pci_unmap_rom(priv->pdev, priv->rom);
+ pci_disable_rom(priv->pdev);
+ kfree(priv);
+}
+
+static void *
+pcirom_init(struct nvkm_bios *bios, const char *name)
+{
+ struct pci_dev *pdev = nv_device(bios)->pdev;
+ struct priv *priv = NULL;
+ int ret;
+
+ if (!(ret = pci_enable_rom(pdev))) {
+ if (ret = -ENOMEM,
+ (priv = kmalloc(sizeof(*priv), GFP_KERNEL))) {
+ if (ret = -EFAULT,
+ (priv->rom = pci_map_rom(pdev, &priv->size))) {
+ priv->pdev = pdev;
+ return priv;
+ }
+ kfree(priv);
+ }
+ pci_disable_rom(pdev);
+ }
+
+ return ERR_PTR(ret);
+}
+
+const struct nvbios_source
+nvbios_pcirom = {
+ .name = "PCIROM",
+ .init = pcirom_init,
+ .fini = pcirom_fini,
+ .read = pcirom_read,
+ .rw = true,
+};
+
+static void *
+platform_init(struct nvkm_bios *bios, const char *name)
+{
+ struct pci_dev *pdev = nv_device(bios)->pdev;
+ struct priv *priv;
+ int ret = -ENOMEM;
+
+ if ((priv = kmalloc(sizeof(*priv), GFP_KERNEL))) {
+ if (ret = -ENODEV,
+ (priv->rom = pci_platform_rom(pdev, &priv->size)))
+ return priv;
+ kfree(priv);
+ }
+
+ return ERR_PTR(ret);
+}
+
+const struct nvbios_source
+nvbios_platform = {
+ .name = "PLATFORM",
+ .init = platform_init,
+ .fini = (void(*)(void *))kfree,
+ .read = pcirom_read,
+ .rw = true,
+};
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowramin.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowramin.c
new file mode 100644
index 000000000000..abe8ae4d3a9f
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowramin.c
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#include "priv.h"
+
+#include <core/device.h>
+
+struct priv {
+ struct nvkm_bios *bios;
+ u32 bar0;
+};
+
+static u32
+pramin_read(void *data, u32 offset, u32 length, struct nvkm_bios *bios)
+{
+ u32 i;
+ if (offset + length <= 0x00100000) {
+ for (i = offset; i < offset + length; i += 4)
+ *(u32 *)&bios->data[i] = nv_rd32(bios, 0x700000 + i);
+ return length;
+ }
+ return 0;
+}
+
+static void
+pramin_fini(void *data)
+{
+ struct priv *priv = data;
+ if (priv) {
+ nv_wr32(priv->bios, 0x001700, priv->bar0);
+ kfree(priv);
+ }
+}
+
+static void *
+pramin_init(struct nvkm_bios *bios, const char *name)
+{
+ struct priv *priv = NULL;
+ u64 addr = 0;
+
+ /* PRAMIN always potentially available prior to nv50 */
+ if (nv_device(bios)->card_type < NV_50)
+ return NULL;
+
+ /* we can't get the bios image pointer without PDISP */
+ if (nv_device(bios)->card_type >= GM100)
+ addr = nv_rd32(bios, 0x021c04);
+ else
+ if (nv_device(bios)->card_type >= NV_C0)
+ addr = nv_rd32(bios, 0x022500);
+ if (addr & 0x00000001) {
+ nv_debug(bios, "... display disabled\n");
+ return ERR_PTR(-ENODEV);
+ }
+
+ /* check that the window is enabled and in vram, particularly
+ * important as we don't want to be touching vram on an
+ * uninitialised board
+ */
+ addr = nv_rd32(bios, 0x619f04);
+ if (!(addr & 0x00000008)) {
+ nv_debug(bios, "... not enabled\n");
+ return ERR_PTR(-ENODEV);
+ }
+ if ( (addr & 0x00000003) != 1) {
+ nv_debug(bios, "... not in vram\n");
+ return ERR_PTR(-ENODEV);
+ }
+
+ /* some alternate method inherited from xf86-video-nv... */
+ addr = (addr & 0xffffff00) << 8;
+ if (!addr) {
+ addr = (u64)nv_rd32(bios, 0x001700) << 16;
+ addr += 0xf0000;
+ }
+
+ /* modify bar0 PRAMIN window to cover the bios image */
+ if (!(priv = kmalloc(sizeof(*priv), GFP_KERNEL))) {
+ nv_error(bios, "... out of memory\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ priv->bios = bios;
+ priv->bar0 = nv_rd32(bios, 0x001700);
+ nv_wr32(bios, 0x001700, addr >> 16);
+ return priv;
+}
+
+const struct nvbios_source
+nvbios_ramin = {
+ .name = "PRAMIN",
+ .init = pramin_init,
+ .fini = pramin_fini,
+ .read = pramin_read,
+ .rw = true,
+};
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowrom.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowrom.c
new file mode 100644
index 000000000000..6ec3b237925e
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowrom.c
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#include "priv.h"
+
+#include <core/device.h>
+
+static u32
+prom_read(void *data, u32 offset, u32 length, struct nvkm_bios *bios)
+{
+ u32 i;
+ if (offset + length <= 0x00100000) {
+ for (i = offset; i < offset + length; i += 4)
+ *(u32 *)&bios->data[i] = nv_rd32(bios, 0x300000 + i);
+ return length;
+ }
+ return 0;
+}
+
+static void
+prom_fini(void *data)
+{
+ struct nvkm_bios *bios = data;
+ if (nv_device(bios)->card_type < NV_50)
+ nv_mask(bios, 0x001850, 0x00000001, 0x00000001);
+ else
+ nv_mask(bios, 0x088050, 0x00000001, 0x00000001);
+}
+
+static void *
+prom_init(struct nvkm_bios *bios, const char *name)
+{
+ if (nv_device(bios)->card_type < NV_50) {
+ if (nv_device(bios)->card_type == NV_40 &&
+ nv_device(bios)->chipset >= 0x4c)
+ return ERR_PTR(-ENODEV);
+ nv_mask(bios, 0x001850, 0x00000001, 0x00000000);
+ } else {
+ nv_mask(bios, 0x088050, 0x00000001, 0x00000000);
+ }
+ return bios;
+}
+
+const struct nvbios_source
+nvbios_rom = {
+ .name = "PROM",
+ .init = prom_init,
+ .fini = prom_fini,
+ .read = prom_read,
+ .rw = false,
+};
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/therm.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/therm.c
new file mode 100644
index 000000000000..249ff6d583df
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/therm.c
@@ -0,0 +1,214 @@
+/*
+ * Copyright 2012 Nouveau Community
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Martin Peres
+ */
+#include <subdev/bios.h>
+#include <subdev/bios/bit.h>
+#include <subdev/bios/therm.h>
+
+#include <core/device.h>
+
+static u16
+therm_table(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *len, u8 *cnt)
+{
+ struct bit_entry bit_P;
+ u16 therm = 0;
+
+ if (!bit_entry(bios, 'P', &bit_P)) {
+ if (bit_P.version == 1)
+ therm = nv_ro16(bios, bit_P.offset + 12);
+ else if (bit_P.version == 2)
+ therm = nv_ro16(bios, bit_P.offset + 16);
+ else
+ nv_error(bios,
+ "unknown offset for thermal in BIT P %d\n",
+ bit_P.version);
+ }
+
+ /* exit now if we haven't found the thermal table */
+ if (!therm)
+ return 0x0000;
+
+ *ver = nv_ro08(bios, therm + 0);
+ *hdr = nv_ro08(bios, therm + 1);
+ *len = nv_ro08(bios, therm + 2);
+ *cnt = nv_ro08(bios, therm + 3);
+ return therm + nv_ro08(bios, therm + 1);
+}
+
+static u16
+nvbios_therm_entry(struct nvkm_bios *bios, int idx, u8 *ver, u8 *len)
+{
+ u8 hdr, cnt;
+ u16 therm = therm_table(bios, ver, &hdr, len, &cnt);
+ if (therm && idx < cnt)
+ return therm + idx * *len;
+ return 0x0000;
+}
+
+int
+nvbios_therm_sensor_parse(struct nvkm_bios *bios,
+ enum nvbios_therm_domain domain,
+ struct nvbios_therm_sensor *sensor)
+{
+ s8 thrs_section, sensor_section, offset;
+ u8 ver, len, i;
+ u16 entry;
+
+ /* we only support the core domain for now */
+ if (domain != NVBIOS_THERM_DOMAIN_CORE)
+ return -EINVAL;
+
+ /* Read the entries from the table */
+ thrs_section = 0;
+ sensor_section = -1;
+ i = 0;
+ while ((entry = nvbios_therm_entry(bios, i++, &ver, &len))) {
+ s16 value = nv_ro16(bios, entry + 1);
+
+ switch (nv_ro08(bios, entry + 0)) {
+ case 0x0:
+ thrs_section = value;
+ if (value > 0)
+ return 0; /* we do not try to support ambient */
+ break;
+ case 0x01:
+ sensor_section++;
+ if (sensor_section == 0) {
+ offset = ((s8) nv_ro08(bios, entry + 2)) / 2;
+ sensor->offset_constant = offset;
+ }
+ break;
+
+ case 0x04:
+ if (thrs_section == 0) {
+ sensor->thrs_critical.temp = (value & 0xff0) >> 4;
+ sensor->thrs_critical.hysteresis = value & 0xf;
+ }
+ break;
+
+ case 0x07:
+ if (thrs_section == 0) {
+ sensor->thrs_down_clock.temp = (value & 0xff0) >> 4;
+ sensor->thrs_down_clock.hysteresis = value & 0xf;
+ }
+ break;
+
+ case 0x08:
+ if (thrs_section == 0) {
+ sensor->thrs_fan_boost.temp = (value & 0xff0) >> 4;
+ sensor->thrs_fan_boost.hysteresis = value & 0xf;
+ }
+ break;
+
+ case 0x10:
+ if (sensor_section == 0)
+ sensor->offset_num = value;
+ break;
+
+ case 0x11:
+ if (sensor_section == 0)
+ sensor->offset_den = value;
+ break;
+
+ case 0x12:
+ if (sensor_section == 0)
+ sensor->slope_mult = value;
+ break;
+
+ case 0x13:
+ if (sensor_section == 0)
+ sensor->slope_div = value;
+ break;
+ case 0x32:
+ if (thrs_section == 0) {
+ sensor->thrs_shutdown.temp = (value & 0xff0) >> 4;
+ sensor->thrs_shutdown.hysteresis = value & 0xf;
+ }
+ break;
+ }
+ }
+
+ return 0;
+}
+
+int
+nvbios_therm_fan_parse(struct nvkm_bios *bios, struct nvbios_therm_fan *fan)
+{
+ struct nvbios_therm_trip_point *cur_trip = NULL;
+ u8 ver, len, i;
+ u16 entry;
+
+ uint8_t duty_lut[] = { 0, 0, 25, 0, 40, 0, 50, 0,
+ 75, 0, 85, 0, 100, 0, 100, 0 };
+
+ i = 0;
+ fan->nr_fan_trip = 0;
+ fan->fan_mode = NVBIOS_THERM_FAN_OTHER;
+ while ((entry = nvbios_therm_entry(bios, i++, &ver, &len))) {
+ s16 value = nv_ro16(bios, entry + 1);
+
+ switch (nv_ro08(bios, entry + 0)) {
+ case 0x22:
+ fan->min_duty = value & 0xff;
+ fan->max_duty = (value & 0xff00) >> 8;
+ break;
+ case 0x24:
+ fan->nr_fan_trip++;
+ if (fan->fan_mode > NVBIOS_THERM_FAN_TRIP)
+ fan->fan_mode = NVBIOS_THERM_FAN_TRIP;
+ cur_trip = &fan->trip[fan->nr_fan_trip - 1];
+ cur_trip->hysteresis = value & 0xf;
+ cur_trip->temp = (value & 0xff0) >> 4;
+ cur_trip->fan_duty = duty_lut[(value & 0xf000) >> 12];
+ break;
+ case 0x25:
+ cur_trip = &fan->trip[fan->nr_fan_trip - 1];
+ cur_trip->fan_duty = value;
+ break;
+ case 0x26:
+ if (!fan->pwm_freq)
+ fan->pwm_freq = value;
+ break;
+ case 0x3b:
+ fan->bump_period = value;
+ break;
+ case 0x3c:
+ fan->slow_down_period = value;
+ break;
+ case 0x46:
+ if (fan->fan_mode > NVBIOS_THERM_FAN_LINEAR)
+ fan->fan_mode = NVBIOS_THERM_FAN_LINEAR;
+ fan->linear_min_temp = nv_ro08(bios, entry + 1);
+ fan->linear_max_temp = nv_ro08(bios, entry + 2);
+ break;
+ }
+ }
+
+ /* starting from fermi, fan management is always linear */
+ if (nv_device(bios)->card_type >= NV_C0 &&
+ fan->fan_mode == NVBIOS_THERM_FAN_OTHER) {
+ fan->fan_mode = NVBIOS_THERM_FAN_LINEAR;
+ }
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/timing.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/timing.c
new file mode 100644
index 000000000000..763fd29a58f2
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/timing.c
@@ -0,0 +1,166 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include <subdev/bios.h>
+#include <subdev/bios/bit.h>
+#include <subdev/bios/timing.h>
+
+u16
+nvbios_timingTe(struct nvkm_bios *bios,
+ u8 *ver, u8 *hdr, u8 *cnt, u8 *len, u8 *snr, u8 *ssz)
+{
+ struct bit_entry bit_P;
+ u16 timing = 0x0000;
+
+ if (!bit_entry(bios, 'P', &bit_P)) {
+ if (bit_P.version == 1)
+ timing = nv_ro16(bios, bit_P.offset + 4);
+ else
+ if (bit_P.version == 2)
+ timing = nv_ro16(bios, bit_P.offset + 8);
+
+ if (timing) {
+ *ver = nv_ro08(bios, timing + 0);
+ switch (*ver) {
+ case 0x10:
+ *hdr = nv_ro08(bios, timing + 1);
+ *cnt = nv_ro08(bios, timing + 2);
+ *len = nv_ro08(bios, timing + 3);
+ *snr = 0;
+ *ssz = 0;
+ return timing;
+ case 0x20:
+ *hdr = nv_ro08(bios, timing + 1);
+ *cnt = nv_ro08(bios, timing + 5);
+ *len = nv_ro08(bios, timing + 2);
+ *snr = nv_ro08(bios, timing + 4);
+ *ssz = nv_ro08(bios, timing + 3);
+ return timing;
+ default:
+ break;
+ }
+ }
+ }
+
+ return 0x0000;
+}
+
+u16
+nvbios_timingEe(struct nvkm_bios *bios, int idx,
+ u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
+{
+ u8 snr, ssz;
+ u16 timing = nvbios_timingTe(bios, ver, hdr, cnt, len, &snr, &ssz);
+ if (timing && idx < *cnt) {
+ timing += *hdr + idx * (*len + (snr * ssz));
+ *hdr = *len;
+ *cnt = snr;
+ *len = ssz;
+ return timing;
+ }
+ return 0x0000;
+}
+
+u16
+nvbios_timingEp(struct nvkm_bios *bios, int idx,
+ u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_ramcfg *p)
+{
+ u16 data = nvbios_timingEe(bios, idx, ver, hdr, cnt, len), temp;
+ p->timing_ver = *ver;
+ p->timing_hdr = *hdr;
+ switch (!!data * *ver) {
+ case 0x10:
+ p->timing_10_WR = nv_ro08(bios, data + 0x00);
+ p->timing_10_WTR = nv_ro08(bios, data + 0x01);
+ p->timing_10_CL = nv_ro08(bios, data + 0x02);
+ p->timing_10_RC = nv_ro08(bios, data + 0x03);
+ p->timing_10_RFC = nv_ro08(bios, data + 0x05);
+ p->timing_10_RAS = nv_ro08(bios, data + 0x07);
+ p->timing_10_RP = nv_ro08(bios, data + 0x09);
+ p->timing_10_RCDRD = nv_ro08(bios, data + 0x0a);
+ p->timing_10_RCDWR = nv_ro08(bios, data + 0x0b);
+ p->timing_10_RRD = nv_ro08(bios, data + 0x0c);
+ p->timing_10_13 = nv_ro08(bios, data + 0x0d);
+ p->timing_10_ODT = nv_ro08(bios, data + 0x0e) & 0x07;
+
+ p->timing_10_24 = 0xff;
+ p->timing_10_21 = 0;
+ p->timing_10_20 = 0;
+ p->timing_10_CWL = 0;
+ p->timing_10_18 = 0;
+ p->timing_10_16 = 0;
+
+ switch (min_t(u8, *hdr, 25)) {
+ case 25:
+ p->timing_10_24 = nv_ro08(bios, data + 0x18);
+ case 24:
+ case 23:
+ case 22:
+ p->timing_10_21 = nv_ro08(bios, data + 0x15);
+ case 21:
+ p->timing_10_20 = nv_ro08(bios, data + 0x14);
+ case 20:
+ p->timing_10_CWL = nv_ro08(bios, data + 0x13);
+ case 19:
+ p->timing_10_18 = nv_ro08(bios, data + 0x12);
+ case 18:
+ case 17:
+ p->timing_10_16 = nv_ro08(bios, data + 0x10);
+ }
+
+ break;
+ case 0x20:
+ p->timing[0] = nv_ro32(bios, data + 0x00);
+ p->timing[1] = nv_ro32(bios, data + 0x04);
+ p->timing[2] = nv_ro32(bios, data + 0x08);
+ p->timing[3] = nv_ro32(bios, data + 0x0c);
+ p->timing[4] = nv_ro32(bios, data + 0x10);
+ p->timing[5] = nv_ro32(bios, data + 0x14);
+ p->timing[6] = nv_ro32(bios, data + 0x18);
+ p->timing[7] = nv_ro32(bios, data + 0x1c);
+ p->timing[8] = nv_ro32(bios, data + 0x20);
+ p->timing[9] = nv_ro32(bios, data + 0x24);
+ p->timing[10] = nv_ro32(bios, data + 0x28);
+ p->timing_20_2e_03 = (nv_ro08(bios, data + 0x2e) & 0x03) >> 0;
+ p->timing_20_2e_30 = (nv_ro08(bios, data + 0x2e) & 0x30) >> 4;
+ p->timing_20_2e_c0 = (nv_ro08(bios, data + 0x2e) & 0xc0) >> 6;
+ p->timing_20_2f_03 = (nv_ro08(bios, data + 0x2f) & 0x03) >> 0;
+ temp = nv_ro16(bios, data + 0x2c);
+ p->timing_20_2c_003f = (temp & 0x003f) >> 0;
+ p->timing_20_2c_1fc0 = (temp & 0x1fc0) >> 6;
+ p->timing_20_30_07 = (nv_ro08(bios, data + 0x30) & 0x07) >> 0;
+ p->timing_20_30_f8 = (nv_ro08(bios, data + 0x30) & 0xf8) >> 3;
+ temp = nv_ro16(bios, data + 0x31);
+ p->timing_20_31_0007 = (temp & 0x0007) >> 0;
+ p->timing_20_31_0078 = (temp & 0x0078) >> 3;
+ p->timing_20_31_0780 = (temp & 0x0780) >> 7;
+ p->timing_20_31_0800 = (temp & 0x0800) >> 11;
+ p->timing_20_31_7000 = (temp & 0x7000) >> 12;
+ p->timing_20_31_8000 = (temp & 0x8000) >> 15;
+ break;
+ default:
+ data = 0;
+ break;
+ }
+ return data;
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/vmap.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/vmap.c
new file mode 100644
index 000000000000..e95b69faa82e
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/vmap.c
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2012 Nouveau Community
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Martin Peres
+ */
+#include <subdev/bios.h>
+#include <subdev/bios/bit.h>
+#include <subdev/bios/vmap.h>
+
+u16
+nvbios_vmap_table(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
+{
+ struct bit_entry bit_P;
+ u16 vmap = 0x0000;
+
+ if (!bit_entry(bios, 'P', &bit_P)) {
+ if (bit_P.version == 2) {
+ vmap = nv_ro16(bios, bit_P.offset + 0x20);
+ if (vmap) {
+ *ver = nv_ro08(bios, vmap + 0);
+ switch (*ver) {
+ case 0x10:
+ case 0x20:
+ *hdr = nv_ro08(bios, vmap + 1);
+ *cnt = nv_ro08(bios, vmap + 3);
+ *len = nv_ro08(bios, vmap + 2);
+ return vmap;
+ default:
+ break;
+ }
+ }
+ }
+ }
+
+ return 0x0000;
+}
+
+u16
+nvbios_vmap_parse(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
+ struct nvbios_vmap *info)
+{
+ u16 vmap = nvbios_vmap_table(bios, ver, hdr, cnt, len);
+ memset(info, 0x00, sizeof(*info));
+ switch (!!vmap * *ver) {
+ case 0x10:
+ case 0x20:
+ break;
+ }
+ return vmap;
+}
+
+u16
+nvbios_vmap_entry(struct nvkm_bios *bios, int idx, u8 *ver, u8 *len)
+{
+ u8 hdr, cnt;
+ u16 vmap = nvbios_vmap_table(bios, ver, &hdr, &cnt, len);
+ if (vmap && idx < cnt) {
+ vmap = vmap + hdr + (idx * *len);
+ return vmap;
+ }
+ return 0x0000;
+}
+
+u16
+nvbios_vmap_entry_parse(struct nvkm_bios *bios, int idx, u8 *ver, u8 *len,
+ struct nvbios_vmap_entry *info)
+{
+ u16 vmap = nvbios_vmap_entry(bios, idx, ver, len);
+ memset(info, 0x00, sizeof(*info));
+ switch (!!vmap * *ver) {
+ case 0x10:
+ info->link = 0xff;
+ info->min = nv_ro32(bios, vmap + 0x00);
+ info->max = nv_ro32(bios, vmap + 0x04);
+ info->arg[0] = nv_ro32(bios, vmap + 0x08);
+ info->arg[1] = nv_ro32(bios, vmap + 0x0c);
+ info->arg[2] = nv_ro32(bios, vmap + 0x10);
+ break;
+ case 0x20:
+ info->unk0 = nv_ro08(bios, vmap + 0x00);
+ info->link = nv_ro08(bios, vmap + 0x01);
+ info->min = nv_ro32(bios, vmap + 0x02);
+ info->max = nv_ro32(bios, vmap + 0x06);
+ info->arg[0] = nv_ro32(bios, vmap + 0x0a);
+ info->arg[1] = nv_ro32(bios, vmap + 0x0e);
+ info->arg[2] = nv_ro32(bios, vmap + 0x12);
+ info->arg[3] = nv_ro32(bios, vmap + 0x16);
+ info->arg[4] = nv_ro32(bios, vmap + 0x1a);
+ info->arg[5] = nv_ro32(bios, vmap + 0x1e);
+ break;
+ }
+ return vmap;
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/volt.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/volt.c
new file mode 100644
index 000000000000..8454ab7c4a3d
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/volt.c
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2012 Nouveau Community
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Martin Peres
+ */
+#include <subdev/bios.h>
+#include <subdev/bios/bit.h>
+#include <subdev/bios/volt.h>
+
+u16
+nvbios_volt_table(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
+{
+ struct bit_entry bit_P;
+ u16 volt = 0x0000;
+
+ if (!bit_entry(bios, 'P', &bit_P)) {
+ if (bit_P.version == 2)
+ volt = nv_ro16(bios, bit_P.offset + 0x0c);
+ else
+ if (bit_P.version == 1)
+ volt = nv_ro16(bios, bit_P.offset + 0x10);
+
+ if (volt) {
+ *ver = nv_ro08(bios, volt + 0);
+ switch (*ver) {
+ case 0x12:
+ *hdr = 5;
+ *cnt = nv_ro08(bios, volt + 2);
+ *len = nv_ro08(bios, volt + 1);
+ return volt;
+ case 0x20:
+ *hdr = nv_ro08(bios, volt + 1);
+ *cnt = nv_ro08(bios, volt + 2);
+ *len = nv_ro08(bios, volt + 3);
+ return volt;
+ case 0x30:
+ case 0x40:
+ case 0x50:
+ *hdr = nv_ro08(bios, volt + 1);
+ *cnt = nv_ro08(bios, volt + 3);
+ *len = nv_ro08(bios, volt + 2);
+ return volt;
+ }
+ }
+ }
+
+ return 0x0000;
+}
+
+u16
+nvbios_volt_parse(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
+ struct nvbios_volt *info)
+{
+ u16 volt = nvbios_volt_table(bios, ver, hdr, cnt, len);
+ memset(info, 0x00, sizeof(*info));
+ switch (!!volt * *ver) {
+ case 0x12:
+ info->vidmask = nv_ro08(bios, volt + 0x04);
+ break;
+ case 0x20:
+ info->vidmask = nv_ro08(bios, volt + 0x05);
+ break;
+ case 0x30:
+ info->vidmask = nv_ro08(bios, volt + 0x04);
+ break;
+ case 0x40:
+ info->base = nv_ro32(bios, volt + 0x04);
+ info->step = nv_ro16(bios, volt + 0x08);
+ info->vidmask = nv_ro08(bios, volt + 0x0b);
+ /*XXX*/
+ info->min = 0;
+ info->max = info->base;
+ break;
+ case 0x50:
+ info->vidmask = nv_ro08(bios, volt + 0x06);
+ info->min = nv_ro32(bios, volt + 0x0a);
+ info->max = nv_ro32(bios, volt + 0x0e);
+ info->base = nv_ro32(bios, volt + 0x12) & 0x00ffffff;
+ info->step = nv_ro16(bios, volt + 0x16);
+ break;
+ }
+ return volt;
+}
+
+u16
+nvbios_volt_entry(struct nvkm_bios *bios, int idx, u8 *ver, u8 *len)
+{
+ u8 hdr, cnt;
+ u16 volt = nvbios_volt_table(bios, ver, &hdr, &cnt, len);
+ if (volt && idx < cnt) {
+ volt = volt + hdr + (idx * *len);
+ return volt;
+ }
+ return 0x0000;
+}
+
+u16
+nvbios_volt_entry_parse(struct nvkm_bios *bios, int idx, u8 *ver, u8 *len,
+ struct nvbios_volt_entry *info)
+{
+ u16 volt = nvbios_volt_entry(bios, idx, ver, len);
+ memset(info, 0x00, sizeof(*info));
+ switch (!!volt * *ver) {
+ case 0x12:
+ case 0x20:
+ info->voltage = nv_ro08(bios, volt + 0x00) * 10000;
+ info->vid = nv_ro08(bios, volt + 0x01);
+ break;
+ case 0x30:
+ info->voltage = nv_ro08(bios, volt + 0x00) * 10000;
+ info->vid = nv_ro08(bios, volt + 0x01) >> 2;
+ break;
+ case 0x40:
+ case 0x50:
+ break;
+ }
+ return volt;
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/xpio.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/xpio.c
new file mode 100644
index 000000000000..63a5e1b5cb3c
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/xpio.c
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include <subdev/bios.h>
+#include <subdev/bios/gpio.h>
+#include <subdev/bios/xpio.h>
+
+static u16
+dcb_xpiod_table(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
+{
+ u16 data = dcb_gpio_table(bios, ver, hdr, cnt, len);
+ if (data && *ver >= 0x40 && *hdr >= 0x06) {
+ u16 xpio = nv_ro16(bios, data + 0x04);
+ if (xpio) {
+ *ver = nv_ro08(bios, data + 0x00);
+ *hdr = nv_ro08(bios, data + 0x01);
+ *cnt = nv_ro08(bios, data + 0x02);
+ *len = nv_ro08(bios, data + 0x03);
+ return xpio;
+ }
+ }
+ return 0x0000;
+}
+
+u16
+dcb_xpio_table(struct nvkm_bios *bios, u8 idx,
+ u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
+{
+ u16 data = dcb_xpiod_table(bios, ver, hdr, cnt, len);
+ if (data && idx < *cnt) {
+ u16 xpio = nv_ro16(bios, data + *hdr + (idx * *len));
+ if (xpio) {
+ *ver = nv_ro08(bios, data + 0x00);
+ *hdr = nv_ro08(bios, data + 0x01);
+ *cnt = nv_ro08(bios, data + 0x02);
+ *len = nv_ro08(bios, data + 0x03);
+ return xpio;
+ }
+ }
+ return 0x0000;
+}
+
+u16
+dcb_xpio_parse(struct nvkm_bios *bios, u8 idx,
+ u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_xpio *info)
+{
+ u16 data = dcb_xpio_table(bios, idx, ver, hdr, cnt, len);
+ if (data && *len >= 6) {
+ info->type = nv_ro08(bios, data + 0x04);
+ info->addr = nv_ro08(bios, data + 0x05);
+ info->flags = nv_ro08(bios, data + 0x06);
+ }
+ return 0x0000;
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bus/Kbuild b/drivers/gpu/drm/nouveau/nvkm/subdev/bus/Kbuild
new file mode 100644
index 000000000000..83d80b13f149
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bus/Kbuild
@@ -0,0 +1,6 @@
+nvkm-y += nvkm/subdev/bus/hwsq.o
+nvkm-y += nvkm/subdev/bus/nv04.o
+nvkm-y += nvkm/subdev/bus/nv31.o
+nvkm-y += nvkm/subdev/bus/nv50.o
+nvkm-y += nvkm/subdev/bus/g94.o
+nvkm-y += nvkm/subdev/bus/gf100.o
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bus/g94.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bus/g94.c
new file mode 100644
index 000000000000..cbe699e82593
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bus/g94.c
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2012 Nouveau Community
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Martin Peres <martin.peres@labri.fr>
+ * Ben Skeggs
+ */
+#include "nv04.h"
+
+#include <subdev/timer.h>
+
+static int
+g94_bus_hwsq_exec(struct nvkm_bus *pbus, u32 *data, u32 size)
+{
+ struct nv50_bus_priv *priv = (void *)pbus;
+ int i;
+
+ nv_mask(pbus, 0x001098, 0x00000008, 0x00000000);
+ nv_wr32(pbus, 0x001304, 0x00000000);
+ nv_wr32(pbus, 0x001318, 0x00000000);
+ for (i = 0; i < size; i++)
+ nv_wr32(priv, 0x080000 + (i * 4), data[i]);
+ nv_mask(pbus, 0x001098, 0x00000018, 0x00000018);
+ nv_wr32(pbus, 0x00130c, 0x00000001);
+
+ return nv_wait(pbus, 0x001308, 0x00000100, 0x00000000) ? 0 : -ETIMEDOUT;
+}
+
+struct nvkm_oclass *
+g94_bus_oclass = &(struct nv04_bus_impl) {
+ .base.handle = NV_SUBDEV(BUS, 0x94),
+ .base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv04_bus_ctor,
+ .dtor = _nvkm_bus_dtor,
+ .init = nv50_bus_init,
+ .fini = _nvkm_bus_fini,
+ },
+ .intr = nv50_bus_intr,
+ .hwsq_exec = g94_bus_hwsq_exec,
+ .hwsq_size = 128,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bus/gf100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bus/gf100.c
new file mode 100644
index 000000000000..ebc63ba968d4
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bus/gf100.c
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2012 Nouveau Community
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Martin Peres <martin.peres@labri.fr>
+ * Ben Skeggs
+ */
+#include "nv04.h"
+
+static void
+gf100_bus_intr(struct nvkm_subdev *subdev)
+{
+ struct nvkm_bus *pbus = nvkm_bus(subdev);
+ u32 stat = nv_rd32(pbus, 0x001100) & nv_rd32(pbus, 0x001140);
+
+ if (stat & 0x0000000e) {
+ u32 addr = nv_rd32(pbus, 0x009084);
+ u32 data = nv_rd32(pbus, 0x009088);
+
+ nv_error(pbus, "MMIO %s of 0x%08x FAULT at 0x%06x [ %s%s%s]\n",
+ (addr & 0x00000002) ? "write" : "read", data,
+ (addr & 0x00fffffc),
+ (stat & 0x00000002) ? "!ENGINE " : "",
+ (stat & 0x00000004) ? "IBUS " : "",
+ (stat & 0x00000008) ? "TIMEOUT " : "");
+
+ nv_wr32(pbus, 0x009084, 0x00000000);
+ nv_wr32(pbus, 0x001100, (stat & 0x0000000e));
+ stat &= ~0x0000000e;
+ }
+
+ if (stat) {
+ nv_error(pbus, "unknown intr 0x%08x\n", stat);
+ nv_mask(pbus, 0x001140, stat, 0x00000000);
+ }
+}
+
+static int
+gf100_bus_init(struct nvkm_object *object)
+{
+ struct nv04_bus_priv *priv = (void *)object;
+ int ret;
+
+ ret = nvkm_bus_init(&priv->base);
+ if (ret)
+ return ret;
+
+ nv_wr32(priv, 0x001100, 0xffffffff);
+ nv_wr32(priv, 0x001140, 0x0000000e);
+ return 0;
+}
+
+struct nvkm_oclass *
+gf100_bus_oclass = &(struct nv04_bus_impl) {
+ .base.handle = NV_SUBDEV(BUS, 0xc0),
+ .base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv04_bus_ctor,
+ .dtor = _nvkm_bus_dtor,
+ .init = gf100_bus_init,
+ .fini = _nvkm_bus_fini,
+ },
+ .intr = gf100_bus_intr,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bus/hwsq.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bus/hwsq.c
new file mode 100644
index 000000000000..b8853bf16b23
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bus/hwsq.c
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs <bskeggs@redhat.com>
+ */
+#include <subdev/bus.h>
+
+struct nvkm_hwsq {
+ struct nvkm_bus *pbus;
+ u32 addr;
+ u32 data;
+ struct {
+ u8 data[512];
+ u8 size;
+ } c;
+};
+
+static void
+hwsq_cmd(struct nvkm_hwsq *hwsq, int size, u8 data[])
+{
+ memcpy(&hwsq->c.data[hwsq->c.size], data, size * sizeof(data[0]));
+ hwsq->c.size += size;
+}
+
+int
+nvkm_hwsq_init(struct nvkm_bus *pbus, struct nvkm_hwsq **phwsq)
+{
+ struct nvkm_hwsq *hwsq;
+
+ hwsq = *phwsq = kmalloc(sizeof(*hwsq), GFP_KERNEL);
+ if (hwsq) {
+ hwsq->pbus = pbus;
+ hwsq->addr = ~0;
+ hwsq->data = ~0;
+ memset(hwsq->c.data, 0x7f, sizeof(hwsq->c.data));
+ hwsq->c.size = 0;
+ }
+
+ return hwsq ? 0 : -ENOMEM;
+}
+
+int
+nvkm_hwsq_fini(struct nvkm_hwsq **phwsq, bool exec)
+{
+ struct nvkm_hwsq *hwsq = *phwsq;
+ int ret = 0, i;
+ if (hwsq) {
+ struct nvkm_bus *pbus = hwsq->pbus;
+ hwsq->c.size = (hwsq->c.size + 4) / 4;
+ if (hwsq->c.size <= pbus->hwsq_size) {
+ if (exec)
+ ret = pbus->hwsq_exec(pbus, (u32 *)hwsq->c.data,
+ hwsq->c.size);
+ if (ret)
+ nv_error(pbus, "hwsq exec failed: %d\n", ret);
+ } else {
+ nv_error(pbus, "hwsq ucode too large\n");
+ ret = -ENOSPC;
+ }
+
+ for (i = 0; ret && i < hwsq->c.size; i++)
+ nv_error(pbus, "\t0x%08x\n", ((u32 *)hwsq->c.data)[i]);
+
+ *phwsq = NULL;
+ kfree(hwsq);
+ }
+ return ret;
+}
+
+void
+nvkm_hwsq_wr32(struct nvkm_hwsq *hwsq, u32 addr, u32 data)
+{
+ nv_debug(hwsq->pbus, "R[%06x] = 0x%08x\n", addr, data);
+
+ if (hwsq->data != data) {
+ if ((data & 0xffff0000) != (hwsq->data & 0xffff0000)) {
+ hwsq_cmd(hwsq, 5, (u8[]){ 0xe2, data, data >> 8,
+ data >> 16, data >> 24 });
+ } else {
+ hwsq_cmd(hwsq, 3, (u8[]){ 0x42, data, data >> 8 });
+ }
+ }
+
+ if ((addr & 0xffff0000) != (hwsq->addr & 0xffff0000)) {
+ hwsq_cmd(hwsq, 5, (u8[]){ 0xe0, addr, addr >> 8,
+ addr >> 16, addr >> 24 });
+ } else {
+ hwsq_cmd(hwsq, 3, (u8[]){ 0x40, addr, addr >> 8 });
+ }
+
+ hwsq->addr = addr;
+ hwsq->data = data;
+}
+
+void
+nvkm_hwsq_setf(struct nvkm_hwsq *hwsq, u8 flag, int data)
+{
+ nv_debug(hwsq->pbus, " FLAG[%02x] = %d\n", flag, data);
+ flag += 0x80;
+ if (data >= 0)
+ flag += 0x20;
+ if (data >= 1)
+ flag += 0x20;
+ hwsq_cmd(hwsq, 1, (u8[]){ flag });
+}
+
+void
+nvkm_hwsq_wait(struct nvkm_hwsq *hwsq, u8 flag, u8 data)
+{
+ nv_debug(hwsq->pbus, " WAIT[%02x] = %d\n", flag, data);
+ hwsq_cmd(hwsq, 3, (u8[]){ 0x5f, flag, data });
+}
+
+void
+nvkm_hwsq_nsec(struct nvkm_hwsq *hwsq, u32 nsec)
+{
+ u8 shift = 0, usec = nsec / 1000;
+ while (usec & ~3) {
+ usec >>= 2;
+ shift++;
+ }
+
+ nv_debug(hwsq->pbus, " DELAY = %d ns\n", nsec);
+ hwsq_cmd(hwsq, 1, (u8[]){ 0x00 | (shift << 2) | usec });
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bus/hwsq.h b/drivers/gpu/drm/nouveau/nvkm/subdev/bus/hwsq.h
new file mode 100644
index 000000000000..3394a5ea8a9f
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bus/hwsq.h
@@ -0,0 +1,111 @@
+#ifndef __NVKM_BUS_HWSQ_H__
+#define __NVKM_BUS_HWSQ_H__
+#include <subdev/bus.h>
+
+struct hwsq {
+ struct nvkm_subdev *subdev;
+ struct nvkm_hwsq *hwsq;
+ int sequence;
+};
+
+struct hwsq_reg {
+ int sequence;
+ bool force;
+ u32 addr[2];
+ u32 data;
+};
+
+static inline struct hwsq_reg
+hwsq_reg2(u32 addr1, u32 addr2)
+{
+ return (struct hwsq_reg) {
+ .sequence = 0,
+ .force = 0,
+ .addr = { addr1, addr2 },
+ .data = 0xdeadbeef,
+ };
+}
+
+static inline struct hwsq_reg
+hwsq_reg(u32 addr)
+{
+ return hwsq_reg2(addr, addr);
+}
+
+static inline int
+hwsq_init(struct hwsq *ram, struct nvkm_subdev *subdev)
+{
+ struct nvkm_bus *pbus = nvkm_bus(subdev);
+ int ret;
+
+ ret = nvkm_hwsq_init(pbus, &ram->hwsq);
+ if (ret)
+ return ret;
+
+ ram->sequence++;
+ ram->subdev = subdev;
+ return 0;
+}
+
+static inline int
+hwsq_exec(struct hwsq *ram, bool exec)
+{
+ int ret = 0;
+ if (ram->subdev) {
+ ret = nvkm_hwsq_fini(&ram->hwsq, exec);
+ ram->subdev = NULL;
+ }
+ return ret;
+}
+
+static inline u32
+hwsq_rd32(struct hwsq *ram, struct hwsq_reg *reg)
+{
+ if (reg->sequence != ram->sequence)
+ reg->data = nv_rd32(ram->subdev, reg->addr[0]);
+ return reg->data;
+}
+
+static inline void
+hwsq_wr32(struct hwsq *ram, struct hwsq_reg *reg, u32 data)
+{
+ reg->sequence = ram->sequence;
+ reg->data = data;
+ if (reg->addr[0] != reg->addr[1])
+ nvkm_hwsq_wr32(ram->hwsq, reg->addr[1], reg->data);
+ nvkm_hwsq_wr32(ram->hwsq, reg->addr[0], reg->data);
+}
+
+static inline void
+hwsq_nuke(struct hwsq *ram, struct hwsq_reg *reg)
+{
+ reg->force = true;
+}
+
+static inline u32
+hwsq_mask(struct hwsq *ram, struct hwsq_reg *reg, u32 mask, u32 data)
+{
+ u32 temp = hwsq_rd32(ram, reg);
+ if (temp != ((temp & ~mask) | data) || reg->force)
+ hwsq_wr32(ram, reg, (temp & ~mask) | data);
+ return temp;
+}
+
+static inline void
+hwsq_setf(struct hwsq *ram, u8 flag, int data)
+{
+ nvkm_hwsq_setf(ram->hwsq, flag, data);
+}
+
+static inline void
+hwsq_wait(struct hwsq *ram, u8 flag, u8 data)
+{
+ nvkm_hwsq_wait(ram->hwsq, flag, data);
+}
+
+static inline void
+hwsq_nsec(struct hwsq *ram, u32 nsec)
+{
+ nvkm_hwsq_nsec(ram->hwsq, nsec);
+}
+#endif
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bus/nv04.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bus/nv04.c
new file mode 100644
index 000000000000..19c8e50eeff7
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bus/nv04.c
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2012 Nouveau Community
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Martin Peres <martin.peres@labri.fr>
+ * Ben Skeggs
+ */
+#include "nv04.h"
+
+static void
+nv04_bus_intr(struct nvkm_subdev *subdev)
+{
+ struct nvkm_bus *pbus = nvkm_bus(subdev);
+ u32 stat = nv_rd32(pbus, 0x001100) & nv_rd32(pbus, 0x001140);
+
+ if (stat & 0x00000001) {
+ nv_error(pbus, "BUS ERROR\n");
+ stat &= ~0x00000001;
+ nv_wr32(pbus, 0x001100, 0x00000001);
+ }
+
+ if (stat & 0x00000110) {
+ subdev = nvkm_subdev(subdev, NVDEV_SUBDEV_GPIO);
+ if (subdev && subdev->intr)
+ subdev->intr(subdev);
+ stat &= ~0x00000110;
+ nv_wr32(pbus, 0x001100, 0x00000110);
+ }
+
+ if (stat) {
+ nv_error(pbus, "unknown intr 0x%08x\n", stat);
+ nv_mask(pbus, 0x001140, stat, 0x00000000);
+ }
+}
+
+static int
+nv04_bus_init(struct nvkm_object *object)
+{
+ struct nv04_bus_priv *priv = (void *)object;
+
+ nv_wr32(priv, 0x001100, 0xffffffff);
+ nv_wr32(priv, 0x001140, 0x00000111);
+
+ return nvkm_bus_init(&priv->base);
+}
+
+int
+nv04_bus_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 size,
+ struct nvkm_object **pobject)
+{
+ struct nv04_bus_impl *impl = (void *)oclass;
+ struct nv04_bus_priv *priv;
+ int ret;
+
+ ret = nvkm_bus_create(parent, engine, oclass, &priv);
+ *pobject = nv_object(priv);
+ if (ret)
+ return ret;
+
+ nv_subdev(priv)->intr = impl->intr;
+ priv->base.hwsq_exec = impl->hwsq_exec;
+ priv->base.hwsq_size = impl->hwsq_size;
+ return 0;
+}
+
+struct nvkm_oclass *
+nv04_bus_oclass = &(struct nv04_bus_impl) {
+ .base.handle = NV_SUBDEV(BUS, 0x04),
+ .base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv04_bus_ctor,
+ .dtor = _nvkm_bus_dtor,
+ .init = nv04_bus_init,
+ .fini = _nvkm_bus_fini,
+ },
+ .intr = nv04_bus_intr,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bus/nv04.h b/drivers/gpu/drm/nouveau/nvkm/subdev/bus/nv04.h
new file mode 100644
index 000000000000..3ddc8f91b1e3
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bus/nv04.h
@@ -0,0 +1,21 @@
+#ifndef __NVKM_BUS_NV04_H__
+#define __NVKM_BUS_NV04_H__
+#include <subdev/bus.h>
+
+struct nv04_bus_priv {
+ struct nvkm_bus base;
+};
+
+int nv04_bus_ctor(struct nvkm_object *, struct nvkm_object *,
+ struct nvkm_oclass *, void *, u32,
+ struct nvkm_object **);
+int nv50_bus_init(struct nvkm_object *);
+void nv50_bus_intr(struct nvkm_subdev *);
+
+struct nv04_bus_impl {
+ struct nvkm_oclass base;
+ void (*intr)(struct nvkm_subdev *);
+ int (*hwsq_exec)(struct nvkm_bus *, u32 *, u32);
+ u32 hwsq_size;
+};
+#endif
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bus/nv31.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bus/nv31.c
new file mode 100644
index 000000000000..c5739bce8052
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bus/nv31.c
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2012 Nouveau Community
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Martin Peres <martin.peres@labri.fr>
+ * Ben Skeggs
+ */
+#include "nv04.h"
+
+static void
+nv31_bus_intr(struct nvkm_subdev *subdev)
+{
+ struct nvkm_bus *pbus = nvkm_bus(subdev);
+ u32 stat = nv_rd32(pbus, 0x001100) & nv_rd32(pbus, 0x001140);
+ u32 gpio = nv_rd32(pbus, 0x001104) & nv_rd32(pbus, 0x001144);
+
+ if (gpio) {
+ subdev = nvkm_subdev(pbus, NVDEV_SUBDEV_GPIO);
+ if (subdev && subdev->intr)
+ subdev->intr(subdev);
+ }
+
+ if (stat & 0x00000008) { /* NV41- */
+ u32 addr = nv_rd32(pbus, 0x009084);
+ u32 data = nv_rd32(pbus, 0x009088);
+
+ nv_error(pbus, "MMIO %s of 0x%08x FAULT at 0x%06x\n",
+ (addr & 0x00000002) ? "write" : "read", data,
+ (addr & 0x00fffffc));
+
+ stat &= ~0x00000008;
+ nv_wr32(pbus, 0x001100, 0x00000008);
+ }
+
+ if (stat & 0x00070000) {
+ subdev = nvkm_subdev(pbus, NVDEV_SUBDEV_THERM);
+ if (subdev && subdev->intr)
+ subdev->intr(subdev);
+ stat &= ~0x00070000;
+ nv_wr32(pbus, 0x001100, 0x00070000);
+ }
+
+ if (stat) {
+ nv_error(pbus, "unknown intr 0x%08x\n", stat);
+ nv_mask(pbus, 0x001140, stat, 0x00000000);
+ }
+}
+
+static int
+nv31_bus_init(struct nvkm_object *object)
+{
+ struct nv04_bus_priv *priv = (void *)object;
+ int ret;
+
+ ret = nvkm_bus_init(&priv->base);
+ if (ret)
+ return ret;
+
+ nv_wr32(priv, 0x001100, 0xffffffff);
+ nv_wr32(priv, 0x001140, 0x00070008);
+ return 0;
+}
+
+struct nvkm_oclass *
+nv31_bus_oclass = &(struct nv04_bus_impl) {
+ .base.handle = NV_SUBDEV(BUS, 0x31),
+ .base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv04_bus_ctor,
+ .dtor = _nvkm_bus_dtor,
+ .init = nv31_bus_init,
+ .fini = _nvkm_bus_fini,
+ },
+ .intr = nv31_bus_intr,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bus/nv50.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bus/nv50.c
new file mode 100644
index 000000000000..1987863d71ee
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bus/nv50.c
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2012 Nouveau Community
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Martin Peres <martin.peres@labri.fr>
+ * Ben Skeggs
+ */
+#include "nv04.h"
+
+#include <subdev/timer.h>
+
+static int
+nv50_bus_hwsq_exec(struct nvkm_bus *pbus, u32 *data, u32 size)
+{
+ struct nv50_bus_priv *priv = (void *)pbus;
+ int i;
+
+ nv_mask(pbus, 0x001098, 0x00000008, 0x00000000);
+ nv_wr32(pbus, 0x001304, 0x00000000);
+ for (i = 0; i < size; i++)
+ nv_wr32(priv, 0x001400 + (i * 4), data[i]);
+ nv_mask(pbus, 0x001098, 0x00000018, 0x00000018);
+ nv_wr32(pbus, 0x00130c, 0x00000003);
+
+ return nv_wait(pbus, 0x001308, 0x00000100, 0x00000000) ? 0 : -ETIMEDOUT;
+}
+
+void
+nv50_bus_intr(struct nvkm_subdev *subdev)
+{
+ struct nvkm_bus *pbus = nvkm_bus(subdev);
+ u32 stat = nv_rd32(pbus, 0x001100) & nv_rd32(pbus, 0x001140);
+
+ if (stat & 0x00000008) {
+ u32 addr = nv_rd32(pbus, 0x009084);
+ u32 data = nv_rd32(pbus, 0x009088);
+
+ nv_error(pbus, "MMIO %s of 0x%08x FAULT at 0x%06x\n",
+ (addr & 0x00000002) ? "write" : "read", data,
+ (addr & 0x00fffffc));
+
+ stat &= ~0x00000008;
+ nv_wr32(pbus, 0x001100, 0x00000008);
+ }
+
+ if (stat & 0x00010000) {
+ subdev = nvkm_subdev(pbus, NVDEV_SUBDEV_THERM);
+ if (subdev && subdev->intr)
+ subdev->intr(subdev);
+ stat &= ~0x00010000;
+ nv_wr32(pbus, 0x001100, 0x00010000);
+ }
+
+ if (stat) {
+ nv_error(pbus, "unknown intr 0x%08x\n", stat);
+ nv_mask(pbus, 0x001140, stat, 0);
+ }
+}
+
+int
+nv50_bus_init(struct nvkm_object *object)
+{
+ struct nv04_bus_priv *priv = (void *)object;
+ int ret;
+
+ ret = nvkm_bus_init(&priv->base);
+ if (ret)
+ return ret;
+
+ nv_wr32(priv, 0x001100, 0xffffffff);
+ nv_wr32(priv, 0x001140, 0x00010008);
+ return 0;
+}
+
+struct nvkm_oclass *
+nv50_bus_oclass = &(struct nv04_bus_impl) {
+ .base.handle = NV_SUBDEV(BUS, 0x50),
+ .base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv04_bus_ctor,
+ .dtor = _nvkm_bus_dtor,
+ .init = nv50_bus_init,
+ .fini = _nvkm_bus_fini,
+ },
+ .intr = nv50_bus_intr,
+ .hwsq_exec = nv50_bus_hwsq_exec,
+ .hwsq_size = 64,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/Kbuild b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/Kbuild
new file mode 100644
index 000000000000..9c2f688c9602
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/Kbuild
@@ -0,0 +1,12 @@
+nvkm-y += nvkm/subdev/clk/base.o
+nvkm-y += nvkm/subdev/clk/nv04.o
+nvkm-y += nvkm/subdev/clk/nv40.o
+nvkm-y += nvkm/subdev/clk/nv50.o
+nvkm-y += nvkm/subdev/clk/g84.o
+nvkm-y += nvkm/subdev/clk/gt215.o
+nvkm-y += nvkm/subdev/clk/mcp77.o
+nvkm-y += nvkm/subdev/clk/gf100.o
+nvkm-y += nvkm/subdev/clk/gk104.o
+nvkm-y += nvkm/subdev/clk/gk20a.o
+nvkm-y += nvkm/subdev/clk/pllnv04.o
+nvkm-y += nvkm/subdev/clk/pllgt215.o
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/base.c
new file mode 100644
index 000000000000..b24a9cc04b73
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/base.c
@@ -0,0 +1,591 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include <subdev/clk.h>
+#include <subdev/bios.h>
+#include <subdev/bios/boost.h>
+#include <subdev/bios/cstep.h>
+#include <subdev/bios/perf.h>
+#include <subdev/fb.h>
+#include <subdev/therm.h>
+#include <subdev/volt.h>
+
+#include <core/device.h>
+#include <core/option.h>
+
+/******************************************************************************
+ * misc
+ *****************************************************************************/
+static u32
+nvkm_clk_adjust(struct nvkm_clk *clk, bool adjust,
+ u8 pstate, u8 domain, u32 input)
+{
+ struct nvkm_bios *bios = nvkm_bios(clk);
+ struct nvbios_boostE boostE;
+ u8 ver, hdr, cnt, len;
+ u16 data;
+
+ data = nvbios_boostEm(bios, pstate, &ver, &hdr, &cnt, &len, &boostE);
+ if (data) {
+ struct nvbios_boostS boostS;
+ u8 idx = 0, sver, shdr;
+ u16 subd;
+
+ input = max(boostE.min, input);
+ input = min(boostE.max, input);
+ do {
+ sver = ver;
+ shdr = hdr;
+ subd = nvbios_boostSp(bios, idx++, data, &sver, &shdr,
+ cnt, len, &boostS);
+ if (subd && boostS.domain == domain) {
+ if (adjust)
+ input = input * boostS.percent / 100;
+ input = max(boostS.min, input);
+ input = min(boostS.max, input);
+ break;
+ }
+ } while (subd);
+ }
+
+ return input;
+}
+
+/******************************************************************************
+ * C-States
+ *****************************************************************************/
+static int
+nvkm_cstate_prog(struct nvkm_clk *clk, struct nvkm_pstate *pstate, int cstatei)
+{
+ struct nvkm_therm *ptherm = nvkm_therm(clk);
+ struct nvkm_volt *volt = nvkm_volt(clk);
+ struct nvkm_cstate *cstate;
+ int ret;
+
+ if (!list_empty(&pstate->list)) {
+ cstate = list_entry(pstate->list.prev, typeof(*cstate), head);
+ } else {
+ cstate = &pstate->base;
+ }
+
+ if (ptherm) {
+ ret = nvkm_therm_cstate(ptherm, pstate->fanspeed, +1);
+ if (ret && ret != -ENODEV) {
+ nv_error(clk, "failed to raise fan speed: %d\n", ret);
+ return ret;
+ }
+ }
+
+ if (volt) {
+ ret = volt->set_id(volt, cstate->voltage, +1);
+ if (ret && ret != -ENODEV) {
+ nv_error(clk, "failed to raise voltage: %d\n", ret);
+ return ret;
+ }
+ }
+
+ ret = clk->calc(clk, cstate);
+ if (ret == 0) {
+ ret = clk->prog(clk);
+ clk->tidy(clk);
+ }
+
+ if (volt) {
+ ret = volt->set_id(volt, cstate->voltage, -1);
+ if (ret && ret != -ENODEV)
+ nv_error(clk, "failed to lower voltage: %d\n", ret);
+ }
+
+ if (ptherm) {
+ ret = nvkm_therm_cstate(ptherm, pstate->fanspeed, -1);
+ if (ret && ret != -ENODEV)
+ nv_error(clk, "failed to lower fan speed: %d\n", ret);
+ }
+
+ return 0;
+}
+
+static void
+nvkm_cstate_del(struct nvkm_cstate *cstate)
+{
+ list_del(&cstate->head);
+ kfree(cstate);
+}
+
+static int
+nvkm_cstate_new(struct nvkm_clk *clk, int idx, struct nvkm_pstate *pstate)
+{
+ struct nvkm_bios *bios = nvkm_bios(clk);
+ struct nvkm_domain *domain = clk->domains;
+ struct nvkm_cstate *cstate = NULL;
+ struct nvbios_cstepX cstepX;
+ u8 ver, hdr;
+ u16 data;
+
+ data = nvbios_cstepXp(bios, idx, &ver, &hdr, &cstepX);
+ if (!data)
+ return -ENOENT;
+
+ cstate = kzalloc(sizeof(*cstate), GFP_KERNEL);
+ if (!cstate)
+ return -ENOMEM;
+
+ *cstate = pstate->base;
+ cstate->voltage = cstepX.voltage;
+
+ while (domain && domain->name != nv_clk_src_max) {
+ if (domain->flags & NVKM_CLK_DOM_FLAG_CORE) {
+ u32 freq = nvkm_clk_adjust(clk, true, pstate->pstate,
+ domain->bios, cstepX.freq);
+ cstate->domain[domain->name] = freq;
+ }
+ domain++;
+ }
+
+ list_add(&cstate->head, &pstate->list);
+ return 0;
+}
+
+/******************************************************************************
+ * P-States
+ *****************************************************************************/
+static int
+nvkm_pstate_prog(struct nvkm_clk *clk, int pstatei)
+{
+ struct nvkm_fb *pfb = nvkm_fb(clk);
+ struct nvkm_pstate *pstate;
+ int ret, idx = 0;
+
+ list_for_each_entry(pstate, &clk->states, head) {
+ if (idx++ == pstatei)
+ break;
+ }
+
+ nv_debug(clk, "setting performance state %d\n", pstatei);
+ clk->pstate = pstatei;
+
+ if (pfb->ram->calc) {
+ int khz = pstate->base.domain[nv_clk_src_mem];
+ do {
+ ret = pfb->ram->calc(pfb, khz);
+ if (ret == 0)
+ ret = pfb->ram->prog(pfb);
+ } while (ret > 0);
+ pfb->ram->tidy(pfb);
+ }
+
+ return nvkm_cstate_prog(clk, pstate, 0);
+}
+
+static void
+nvkm_pstate_work(struct work_struct *work)
+{
+ struct nvkm_clk *clk = container_of(work, typeof(*clk), work);
+ int pstate;
+
+ if (!atomic_xchg(&clk->waiting, 0))
+ return;
+ clk->pwrsrc = power_supply_is_system_supplied();
+
+ nv_trace(clk, "P %d PWR %d U(AC) %d U(DC) %d A %d T %d D %d\n",
+ clk->pstate, clk->pwrsrc, clk->ustate_ac, clk->ustate_dc,
+ clk->astate, clk->tstate, clk->dstate);
+
+ pstate = clk->pwrsrc ? clk->ustate_ac : clk->ustate_dc;
+ if (clk->state_nr && pstate != -1) {
+ pstate = (pstate < 0) ? clk->astate : pstate;
+ pstate = min(pstate, clk->state_nr - 1 - clk->tstate);
+ pstate = max(pstate, clk->dstate);
+ } else {
+ pstate = clk->pstate = -1;
+ }
+
+ nv_trace(clk, "-> %d\n", pstate);
+ if (pstate != clk->pstate) {
+ int ret = nvkm_pstate_prog(clk, pstate);
+ if (ret) {
+ nv_error(clk, "error setting pstate %d: %d\n",
+ pstate, ret);
+ }
+ }
+
+ wake_up_all(&clk->wait);
+ nvkm_notify_get(&clk->pwrsrc_ntfy);
+}
+
+static int
+nvkm_pstate_calc(struct nvkm_clk *clk, bool wait)
+{
+ atomic_set(&clk->waiting, 1);
+ schedule_work(&clk->work);
+ if (wait)
+ wait_event(clk->wait, !atomic_read(&clk->waiting));
+ return 0;
+}
+
+static void
+nvkm_pstate_info(struct nvkm_clk *clk, struct nvkm_pstate *pstate)
+{
+ struct nvkm_domain *clock = clk->domains - 1;
+ struct nvkm_cstate *cstate;
+ char info[3][32] = { "", "", "" };
+ char name[4] = "--";
+ int i = -1;
+
+ if (pstate->pstate != 0xff)
+ snprintf(name, sizeof(name), "%02x", pstate->pstate);
+
+ while ((++clock)->name != nv_clk_src_max) {
+ u32 lo = pstate->base.domain[clock->name];
+ u32 hi = lo;
+ if (hi == 0)
+ continue;
+
+ nv_debug(clk, "%02x: %10d KHz\n", clock->name, lo);
+ list_for_each_entry(cstate, &pstate->list, head) {
+ u32 freq = cstate->domain[clock->name];
+ lo = min(lo, freq);
+ hi = max(hi, freq);
+ nv_debug(clk, "%10d KHz\n", freq);
+ }
+
+ if (clock->mname && ++i < ARRAY_SIZE(info)) {
+ lo /= clock->mdiv;
+ hi /= clock->mdiv;
+ if (lo == hi) {
+ snprintf(info[i], sizeof(info[i]), "%s %d MHz",
+ clock->mname, lo);
+ } else {
+ snprintf(info[i], sizeof(info[i]),
+ "%s %d-%d MHz", clock->mname, lo, hi);
+ }
+ }
+ }
+
+ nv_info(clk, "%s: %s %s %s\n", name, info[0], info[1], info[2]);
+}
+
+static void
+nvkm_pstate_del(struct nvkm_pstate *pstate)
+{
+ struct nvkm_cstate *cstate, *temp;
+
+ list_for_each_entry_safe(cstate, temp, &pstate->list, head) {
+ nvkm_cstate_del(cstate);
+ }
+
+ list_del(&pstate->head);
+ kfree(pstate);
+}
+
+static int
+nvkm_pstate_new(struct nvkm_clk *clk, int idx)
+{
+ struct nvkm_bios *bios = nvkm_bios(clk);
+ struct nvkm_domain *domain = clk->domains - 1;
+ struct nvkm_pstate *pstate;
+ struct nvkm_cstate *cstate;
+ struct nvbios_cstepE cstepE;
+ struct nvbios_perfE perfE;
+ u8 ver, hdr, cnt, len;
+ u16 data;
+
+ data = nvbios_perfEp(bios, idx, &ver, &hdr, &cnt, &len, &perfE);
+ if (!data)
+ return -EINVAL;
+ if (perfE.pstate == 0xff)
+ return 0;
+
+ pstate = kzalloc(sizeof(*pstate), GFP_KERNEL);
+ cstate = &pstate->base;
+ if (!pstate)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&pstate->list);
+
+ pstate->pstate = perfE.pstate;
+ pstate->fanspeed = perfE.fanspeed;
+ cstate->voltage = perfE.voltage;
+ cstate->domain[nv_clk_src_core] = perfE.core;
+ cstate->domain[nv_clk_src_shader] = perfE.shader;
+ cstate->domain[nv_clk_src_mem] = perfE.memory;
+ cstate->domain[nv_clk_src_vdec] = perfE.vdec;
+ cstate->domain[nv_clk_src_dom6] = perfE.disp;
+
+ while (ver >= 0x40 && (++domain)->name != nv_clk_src_max) {
+ struct nvbios_perfS perfS;
+ u8 sver = ver, shdr = hdr;
+ u32 perfSe = nvbios_perfSp(bios, data, domain->bios,
+ &sver, &shdr, cnt, len, &perfS);
+ if (perfSe == 0 || sver != 0x40)
+ continue;
+
+ if (domain->flags & NVKM_CLK_DOM_FLAG_CORE) {
+ perfS.v40.freq = nvkm_clk_adjust(clk, false,
+ pstate->pstate,
+ domain->bios,
+ perfS.v40.freq);
+ }
+
+ cstate->domain[domain->name] = perfS.v40.freq;
+ }
+
+ data = nvbios_cstepEm(bios, pstate->pstate, &ver, &hdr, &cstepE);
+ if (data) {
+ int idx = cstepE.index;
+ do {
+ nvkm_cstate_new(clk, idx, pstate);
+ } while(idx--);
+ }
+
+ nvkm_pstate_info(clk, pstate);
+ list_add_tail(&pstate->head, &clk->states);
+ clk->state_nr++;
+ return 0;
+}
+
+/******************************************************************************
+ * Adjustment triggers
+ *****************************************************************************/
+static int
+nvkm_clk_ustate_update(struct nvkm_clk *clk, int req)
+{
+ struct nvkm_pstate *pstate;
+ int i = 0;
+
+ if (!clk->allow_reclock)
+ return -ENOSYS;
+
+ if (req != -1 && req != -2) {
+ list_for_each_entry(pstate, &clk->states, head) {
+ if (pstate->pstate == req)
+ break;
+ i++;
+ }
+
+ if (pstate->pstate != req)
+ return -EINVAL;
+ req = i;
+ }
+
+ return req + 2;
+}
+
+static int
+nvkm_clk_nstate(struct nvkm_clk *clk, const char *mode, int arglen)
+{
+ int ret = 1;
+
+ if (clk->allow_reclock && !strncasecmpz(mode, "auto", arglen))
+ return -2;
+
+ if (strncasecmpz(mode, "disabled", arglen)) {
+ char save = mode[arglen];
+ long v;
+
+ ((char *)mode)[arglen] = '\0';
+ if (!kstrtol(mode, 0, &v)) {
+ ret = nvkm_clk_ustate_update(clk, v);
+ if (ret < 0)
+ ret = 1;
+ }
+ ((char *)mode)[arglen] = save;
+ }
+
+ return ret - 2;
+}
+
+int
+nvkm_clk_ustate(struct nvkm_clk *clk, int req, int pwr)
+{
+ int ret = nvkm_clk_ustate_update(clk, req);
+ if (ret >= 0) {
+ if (ret -= 2, pwr) clk->ustate_ac = ret;
+ else clk->ustate_dc = ret;
+ return nvkm_pstate_calc(clk, true);
+ }
+ return ret;
+}
+
+int
+nvkm_clk_astate(struct nvkm_clk *clk, int req, int rel, bool wait)
+{
+ if (!rel) clk->astate = req;
+ if ( rel) clk->astate += rel;
+ clk->astate = min(clk->astate, clk->state_nr - 1);
+ clk->astate = max(clk->astate, 0);
+ return nvkm_pstate_calc(clk, wait);
+}
+
+int
+nvkm_clk_tstate(struct nvkm_clk *clk, int req, int rel)
+{
+ if (!rel) clk->tstate = req;
+ if ( rel) clk->tstate += rel;
+ clk->tstate = min(clk->tstate, 0);
+ clk->tstate = max(clk->tstate, -(clk->state_nr - 1));
+ return nvkm_pstate_calc(clk, true);
+}
+
+int
+nvkm_clk_dstate(struct nvkm_clk *clk, int req, int rel)
+{
+ if (!rel) clk->dstate = req;
+ if ( rel) clk->dstate += rel;
+ clk->dstate = min(clk->dstate, clk->state_nr - 1);
+ clk->dstate = max(clk->dstate, 0);
+ return nvkm_pstate_calc(clk, true);
+}
+
+static int
+nvkm_clk_pwrsrc(struct nvkm_notify *notify)
+{
+ struct nvkm_clk *clk =
+ container_of(notify, typeof(*clk), pwrsrc_ntfy);
+ nvkm_pstate_calc(clk, false);
+ return NVKM_NOTIFY_DROP;
+}
+
+/******************************************************************************
+ * subdev base class implementation
+ *****************************************************************************/
+
+int
+_nvkm_clk_fini(struct nvkm_object *object, bool suspend)
+{
+ struct nvkm_clk *clk = (void *)object;
+ nvkm_notify_put(&clk->pwrsrc_ntfy);
+ return nvkm_subdev_fini(&clk->base, suspend);
+}
+
+int
+_nvkm_clk_init(struct nvkm_object *object)
+{
+ struct nvkm_clk *clk = (void *)object;
+ struct nvkm_domain *clock = clk->domains;
+ int ret;
+
+ ret = nvkm_subdev_init(&clk->base);
+ if (ret)
+ return ret;
+
+ memset(&clk->bstate, 0x00, sizeof(clk->bstate));
+ INIT_LIST_HEAD(&clk->bstate.list);
+ clk->bstate.pstate = 0xff;
+
+ while (clock->name != nv_clk_src_max) {
+ ret = clk->read(clk, clock->name);
+ if (ret < 0) {
+ nv_error(clk, "%02x freq unknown\n", clock->name);
+ return ret;
+ }
+ clk->bstate.base.domain[clock->name] = ret;
+ clock++;
+ }
+
+ nvkm_pstate_info(clk, &clk->bstate);
+
+ clk->astate = clk->state_nr - 1;
+ clk->tstate = 0;
+ clk->dstate = 0;
+ clk->pstate = -1;
+ nvkm_pstate_calc(clk, true);
+ return 0;
+}
+
+void
+_nvkm_clk_dtor(struct nvkm_object *object)
+{
+ struct nvkm_clk *clk = (void *)object;
+ struct nvkm_pstate *pstate, *temp;
+
+ nvkm_notify_fini(&clk->pwrsrc_ntfy);
+
+ list_for_each_entry_safe(pstate, temp, &clk->states, head) {
+ nvkm_pstate_del(pstate);
+ }
+
+ nvkm_subdev_destroy(&clk->base);
+}
+
+int
+nvkm_clk_create_(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, struct nvkm_domain *clocks,
+ struct nvkm_pstate *pstates, int nb_pstates,
+ bool allow_reclock, int length, void **object)
+{
+ struct nvkm_device *device = nv_device(parent);
+ struct nvkm_clk *clk;
+ int ret, idx, arglen;
+ const char *mode;
+
+ ret = nvkm_subdev_create_(parent, engine, oclass, 0, "CLK",
+ "clock", length, object);
+ clk = *object;
+ if (ret)
+ return ret;
+
+ INIT_LIST_HEAD(&clk->states);
+ clk->domains = clocks;
+ clk->ustate_ac = -1;
+ clk->ustate_dc = -1;
+
+ INIT_WORK(&clk->work, nvkm_pstate_work);
+ init_waitqueue_head(&clk->wait);
+ atomic_set(&clk->waiting, 0);
+
+ /* If no pstates are provided, try and fetch them from the BIOS */
+ if (!pstates) {
+ idx = 0;
+ do {
+ ret = nvkm_pstate_new(clk, idx++);
+ } while (ret == 0);
+ } else {
+ for (idx = 0; idx < nb_pstates; idx++)
+ list_add_tail(&pstates[idx].head, &clk->states);
+ clk->state_nr = nb_pstates;
+ }
+
+ clk->allow_reclock = allow_reclock;
+
+ ret = nvkm_notify_init(NULL, &device->event, nvkm_clk_pwrsrc, true,
+ NULL, 0, 0, &clk->pwrsrc_ntfy);
+ if (ret)
+ return ret;
+
+ mode = nvkm_stropt(device->cfgopt, "NvClkMode", &arglen);
+ if (mode) {
+ clk->ustate_ac = nvkm_clk_nstate(clk, mode, arglen);
+ clk->ustate_dc = nvkm_clk_nstate(clk, mode, arglen);
+ }
+
+ mode = nvkm_stropt(device->cfgopt, "NvClkModeAC", &arglen);
+ if (mode)
+ clk->ustate_ac = nvkm_clk_nstate(clk, mode, arglen);
+
+ mode = nvkm_stropt(device->cfgopt, "NvClkModeDC", &arglen);
+ if (mode)
+ clk->ustate_dc = nvkm_clk_nstate(clk, mode, arglen);
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/g84.c b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/g84.c
new file mode 100644
index 000000000000..4c90b9769d64
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/g84.c
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs <bskeggs@redhat.com>
+ */
+#include "nv50.h"
+
+static struct nvkm_domain
+g84_domains[] = {
+ { nv_clk_src_crystal, 0xff },
+ { nv_clk_src_href , 0xff },
+ { nv_clk_src_core , 0xff, 0, "core", 1000 },
+ { nv_clk_src_shader , 0xff, 0, "shader", 1000 },
+ { nv_clk_src_mem , 0xff, 0, "memory", 1000 },
+ { nv_clk_src_vdec , 0xff },
+ { nv_clk_src_max }
+};
+
+struct nvkm_oclass *
+g84_clk_oclass = &(struct nv50_clk_oclass) {
+ .base.handle = NV_SUBDEV(CLK, 0x84),
+ .base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv50_clk_ctor,
+ .dtor = _nvkm_clk_dtor,
+ .init = _nvkm_clk_init,
+ .fini = _nvkm_clk_fini,
+ },
+ .domains = g84_domains,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gf100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gf100.c
new file mode 100644
index 000000000000..3d7330d54b02
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gf100.c
@@ -0,0 +1,462 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include <subdev/clk.h>
+#include "pll.h"
+
+#include <core/device.h>
+#include <subdev/bios.h>
+#include <subdev/bios/pll.h>
+#include <subdev/timer.h>
+
+struct gf100_clk_info {
+ u32 freq;
+ u32 ssel;
+ u32 mdiv;
+ u32 dsrc;
+ u32 ddiv;
+ u32 coef;
+};
+
+struct gf100_clk_priv {
+ struct nvkm_clk base;
+ struct gf100_clk_info eng[16];
+};
+
+static u32 read_div(struct gf100_clk_priv *, int, u32, u32);
+
+static u32
+read_vco(struct gf100_clk_priv *priv, u32 dsrc)
+{
+ struct nvkm_clk *clk = &priv->base;
+ u32 ssrc = nv_rd32(priv, dsrc);
+ if (!(ssrc & 0x00000100))
+ return clk->read(clk, nv_clk_src_sppll0);
+ return clk->read(clk, nv_clk_src_sppll1);
+}
+
+static u32
+read_pll(struct gf100_clk_priv *priv, u32 pll)
+{
+ struct nvkm_clk *clk = &priv->base;
+ u32 ctrl = nv_rd32(priv, pll + 0x00);
+ u32 coef = nv_rd32(priv, pll + 0x04);
+ u32 P = (coef & 0x003f0000) >> 16;
+ u32 N = (coef & 0x0000ff00) >> 8;
+ u32 M = (coef & 0x000000ff) >> 0;
+ u32 sclk;
+
+ if (!(ctrl & 0x00000001))
+ return 0;
+
+ switch (pll) {
+ case 0x00e800:
+ case 0x00e820:
+ sclk = nv_device(priv)->crystal;
+ P = 1;
+ break;
+ case 0x132000:
+ sclk = clk->read(clk, nv_clk_src_mpllsrc);
+ break;
+ case 0x132020:
+ sclk = clk->read(clk, nv_clk_src_mpllsrcref);
+ break;
+ case 0x137000:
+ case 0x137020:
+ case 0x137040:
+ case 0x1370e0:
+ sclk = read_div(priv, (pll & 0xff) / 0x20, 0x137120, 0x137140);
+ break;
+ default:
+ return 0;
+ }
+
+ return sclk * N / M / P;
+}
+
+static u32
+read_div(struct gf100_clk_priv *priv, int doff, u32 dsrc, u32 dctl)
+{
+ u32 ssrc = nv_rd32(priv, dsrc + (doff * 4));
+ u32 sctl = nv_rd32(priv, dctl + (doff * 4));
+
+ switch (ssrc & 0x00000003) {
+ case 0:
+ if ((ssrc & 0x00030000) != 0x00030000)
+ return nv_device(priv)->crystal;
+ return 108000;
+ case 2:
+ return 100000;
+ case 3:
+ if (sctl & 0x80000000) {
+ u32 sclk = read_vco(priv, dsrc + (doff * 4));
+ u32 sdiv = (sctl & 0x0000003f) + 2;
+ return (sclk * 2) / sdiv;
+ }
+
+ return read_vco(priv, dsrc + (doff * 4));
+ default:
+ return 0;
+ }
+}
+
+static u32
+read_clk(struct gf100_clk_priv *priv, int clk)
+{
+ u32 sctl = nv_rd32(priv, 0x137250 + (clk * 4));
+ u32 ssel = nv_rd32(priv, 0x137100);
+ u32 sclk, sdiv;
+
+ if (ssel & (1 << clk)) {
+ if (clk < 7)
+ sclk = read_pll(priv, 0x137000 + (clk * 0x20));
+ else
+ sclk = read_pll(priv, 0x1370e0);
+ sdiv = ((sctl & 0x00003f00) >> 8) + 2;
+ } else {
+ sclk = read_div(priv, clk, 0x137160, 0x1371d0);
+ sdiv = ((sctl & 0x0000003f) >> 0) + 2;
+ }
+
+ if (sctl & 0x80000000)
+ return (sclk * 2) / sdiv;
+
+ return sclk;
+}
+
+static int
+gf100_clk_read(struct nvkm_clk *clk, enum nv_clk_src src)
+{
+ struct nvkm_device *device = nv_device(clk);
+ struct gf100_clk_priv *priv = (void *)clk;
+
+ switch (src) {
+ case nv_clk_src_crystal:
+ return device->crystal;
+ case nv_clk_src_href:
+ return 100000;
+ case nv_clk_src_sppll0:
+ return read_pll(priv, 0x00e800);
+ case nv_clk_src_sppll1:
+ return read_pll(priv, 0x00e820);
+
+ case nv_clk_src_mpllsrcref:
+ return read_div(priv, 0, 0x137320, 0x137330);
+ case nv_clk_src_mpllsrc:
+ return read_pll(priv, 0x132020);
+ case nv_clk_src_mpll:
+ return read_pll(priv, 0x132000);
+ case nv_clk_src_mdiv:
+ return read_div(priv, 0, 0x137300, 0x137310);
+ case nv_clk_src_mem:
+ if (nv_rd32(priv, 0x1373f0) & 0x00000002)
+ return clk->read(clk, nv_clk_src_mpll);
+ return clk->read(clk, nv_clk_src_mdiv);
+
+ case nv_clk_src_gpc:
+ return read_clk(priv, 0x00);
+ case nv_clk_src_rop:
+ return read_clk(priv, 0x01);
+ case nv_clk_src_hubk07:
+ return read_clk(priv, 0x02);
+ case nv_clk_src_hubk06:
+ return read_clk(priv, 0x07);
+ case nv_clk_src_hubk01:
+ return read_clk(priv, 0x08);
+ case nv_clk_src_copy:
+ return read_clk(priv, 0x09);
+ case nv_clk_src_daemon:
+ return read_clk(priv, 0x0c);
+ case nv_clk_src_vdec:
+ return read_clk(priv, 0x0e);
+ default:
+ nv_error(clk, "invalid clock source %d\n", src);
+ return -EINVAL;
+ }
+}
+
+static u32
+calc_div(struct gf100_clk_priv *priv, int clk, u32 ref, u32 freq, u32 *ddiv)
+{
+ u32 div = min((ref * 2) / freq, (u32)65);
+ if (div < 2)
+ div = 2;
+
+ *ddiv = div - 2;
+ return (ref * 2) / div;
+}
+
+static u32
+calc_src(struct gf100_clk_priv *priv, int clk, u32 freq, u32 *dsrc, u32 *ddiv)
+{
+ u32 sclk;
+
+ /* use one of the fixed frequencies if possible */
+ *ddiv = 0x00000000;
+ switch (freq) {
+ case 27000:
+ case 108000:
+ *dsrc = 0x00000000;
+ if (freq == 108000)
+ *dsrc |= 0x00030000;
+ return freq;
+ case 100000:
+ *dsrc = 0x00000002;
+ return freq;
+ default:
+ *dsrc = 0x00000003;
+ break;
+ }
+
+ /* otherwise, calculate the closest divider */
+ sclk = read_vco(priv, 0x137160 + (clk * 4));
+ if (clk < 7)
+ sclk = calc_div(priv, clk, sclk, freq, ddiv);
+ return sclk;
+}
+
+static u32
+calc_pll(struct gf100_clk_priv *priv, int clk, u32 freq, u32 *coef)
+{
+ struct nvkm_bios *bios = nvkm_bios(priv);
+ struct nvbios_pll limits;
+ int N, M, P, ret;
+
+ ret = nvbios_pll_parse(bios, 0x137000 + (clk * 0x20), &limits);
+ if (ret)
+ return 0;
+
+ limits.refclk = read_div(priv, clk, 0x137120, 0x137140);
+ if (!limits.refclk)
+ return 0;
+
+ ret = gt215_pll_calc(nv_subdev(priv), &limits, freq, &N, NULL, &M, &P);
+ if (ret <= 0)
+ return 0;
+
+ *coef = (P << 16) | (N << 8) | M;
+ return ret;
+}
+
+static int
+calc_clk(struct gf100_clk_priv *priv,
+ struct nvkm_cstate *cstate, int clk, int dom)
+{
+ struct gf100_clk_info *info = &priv->eng[clk];
+ u32 freq = cstate->domain[dom];
+ u32 src0, div0, div1D, div1P = 0;
+ u32 clk0, clk1 = 0;
+
+ /* invalid clock domain */
+ if (!freq)
+ return 0;
+
+ /* first possible path, using only dividers */
+ clk0 = calc_src(priv, clk, freq, &src0, &div0);
+ clk0 = calc_div(priv, clk, clk0, freq, &div1D);
+
+ /* see if we can get any closer using PLLs */
+ if (clk0 != freq && (0x00004387 & (1 << clk))) {
+ if (clk <= 7)
+ clk1 = calc_pll(priv, clk, freq, &info->coef);
+ else
+ clk1 = cstate->domain[nv_clk_src_hubk06];
+ clk1 = calc_div(priv, clk, clk1, freq, &div1P);
+ }
+
+ /* select the method which gets closest to target freq */
+ if (abs((int)freq - clk0) <= abs((int)freq - clk1)) {
+ info->dsrc = src0;
+ if (div0) {
+ info->ddiv |= 0x80000000;
+ info->ddiv |= div0 << 8;
+ info->ddiv |= div0;
+ }
+ if (div1D) {
+ info->mdiv |= 0x80000000;
+ info->mdiv |= div1D;
+ }
+ info->ssel = info->coef = 0;
+ info->freq = clk0;
+ } else {
+ if (div1P) {
+ info->mdiv |= 0x80000000;
+ info->mdiv |= div1P << 8;
+ }
+ info->ssel = (1 << clk);
+ info->freq = clk1;
+ }
+
+ return 0;
+}
+
+static int
+gf100_clk_calc(struct nvkm_clk *clk, struct nvkm_cstate *cstate)
+{
+ struct gf100_clk_priv *priv = (void *)clk;
+ int ret;
+
+ if ((ret = calc_clk(priv, cstate, 0x00, nv_clk_src_gpc)) ||
+ (ret = calc_clk(priv, cstate, 0x01, nv_clk_src_rop)) ||
+ (ret = calc_clk(priv, cstate, 0x02, nv_clk_src_hubk07)) ||
+ (ret = calc_clk(priv, cstate, 0x07, nv_clk_src_hubk06)) ||
+ (ret = calc_clk(priv, cstate, 0x08, nv_clk_src_hubk01)) ||
+ (ret = calc_clk(priv, cstate, 0x09, nv_clk_src_copy)) ||
+ (ret = calc_clk(priv, cstate, 0x0c, nv_clk_src_daemon)) ||
+ (ret = calc_clk(priv, cstate, 0x0e, nv_clk_src_vdec)))
+ return ret;
+
+ return 0;
+}
+
+static void
+gf100_clk_prog_0(struct gf100_clk_priv *priv, int clk)
+{
+ struct gf100_clk_info *info = &priv->eng[clk];
+ if (clk < 7 && !info->ssel) {
+ nv_mask(priv, 0x1371d0 + (clk * 0x04), 0x80003f3f, info->ddiv);
+ nv_wr32(priv, 0x137160 + (clk * 0x04), info->dsrc);
+ }
+}
+
+static void
+gf100_clk_prog_1(struct gf100_clk_priv *priv, int clk)
+{
+ nv_mask(priv, 0x137100, (1 << clk), 0x00000000);
+ nv_wait(priv, 0x137100, (1 << clk), 0x00000000);
+}
+
+static void
+gf100_clk_prog_2(struct gf100_clk_priv *priv, int clk)
+{
+ struct gf100_clk_info *info = &priv->eng[clk];
+ const u32 addr = 0x137000 + (clk * 0x20);
+ if (clk <= 7) {
+ nv_mask(priv, addr + 0x00, 0x00000004, 0x00000000);
+ nv_mask(priv, addr + 0x00, 0x00000001, 0x00000000);
+ if (info->coef) {
+ nv_wr32(priv, addr + 0x04, info->coef);
+ nv_mask(priv, addr + 0x00, 0x00000001, 0x00000001);
+ nv_wait(priv, addr + 0x00, 0x00020000, 0x00020000);
+ nv_mask(priv, addr + 0x00, 0x00020004, 0x00000004);
+ }
+ }
+}
+
+static void
+gf100_clk_prog_3(struct gf100_clk_priv *priv, int clk)
+{
+ struct gf100_clk_info *info = &priv->eng[clk];
+ if (info->ssel) {
+ nv_mask(priv, 0x137100, (1 << clk), info->ssel);
+ nv_wait(priv, 0x137100, (1 << clk), info->ssel);
+ }
+}
+
+static void
+gf100_clk_prog_4(struct gf100_clk_priv *priv, int clk)
+{
+ struct gf100_clk_info *info = &priv->eng[clk];
+ nv_mask(priv, 0x137250 + (clk * 0x04), 0x00003f3f, info->mdiv);
+}
+
+static int
+gf100_clk_prog(struct nvkm_clk *clk)
+{
+ struct gf100_clk_priv *priv = (void *)clk;
+ struct {
+ void (*exec)(struct gf100_clk_priv *, int);
+ } stage[] = {
+ { gf100_clk_prog_0 }, /* div programming */
+ { gf100_clk_prog_1 }, /* select div mode */
+ { gf100_clk_prog_2 }, /* (maybe) program pll */
+ { gf100_clk_prog_3 }, /* (maybe) select pll mode */
+ { gf100_clk_prog_4 }, /* final divider */
+ };
+ int i, j;
+
+ for (i = 0; i < ARRAY_SIZE(stage); i++) {
+ for (j = 0; j < ARRAY_SIZE(priv->eng); j++) {
+ if (!priv->eng[j].freq)
+ continue;
+ stage[i].exec(priv, j);
+ }
+ }
+
+ return 0;
+}
+
+static void
+gf100_clk_tidy(struct nvkm_clk *clk)
+{
+ struct gf100_clk_priv *priv = (void *)clk;
+ memset(priv->eng, 0x00, sizeof(priv->eng));
+}
+
+static struct nvkm_domain
+gf100_domain[] = {
+ { nv_clk_src_crystal, 0xff },
+ { nv_clk_src_href , 0xff },
+ { nv_clk_src_hubk06 , 0x00 },
+ { nv_clk_src_hubk01 , 0x01 },
+ { nv_clk_src_copy , 0x02 },
+ { nv_clk_src_gpc , 0x03, 0, "core", 2000 },
+ { nv_clk_src_rop , 0x04 },
+ { nv_clk_src_mem , 0x05, 0, "memory", 1000 },
+ { nv_clk_src_vdec , 0x06 },
+ { nv_clk_src_daemon , 0x0a },
+ { nv_clk_src_hubk07 , 0x0b },
+ { nv_clk_src_max }
+};
+
+static int
+gf100_clk_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 size,
+ struct nvkm_object **pobject)
+{
+ struct gf100_clk_priv *priv;
+ int ret;
+
+ ret = nvkm_clk_create(parent, engine, oclass, gf100_domain,
+ NULL, 0, false, &priv);
+ *pobject = nv_object(priv);
+ if (ret)
+ return ret;
+
+ priv->base.read = gf100_clk_read;
+ priv->base.calc = gf100_clk_calc;
+ priv->base.prog = gf100_clk_prog;
+ priv->base.tidy = gf100_clk_tidy;
+ return 0;
+}
+
+struct nvkm_oclass
+gf100_clk_oclass = {
+ .handle = NV_SUBDEV(CLK, 0xc0),
+ .ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = gf100_clk_ctor,
+ .dtor = _nvkm_clk_dtor,
+ .init = _nvkm_clk_init,
+ .fini = _nvkm_clk_fini,
+ },
+};
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk104.c b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk104.c
new file mode 100644
index 000000000000..e9b2310bdfbb
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk104.c
@@ -0,0 +1,500 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include <subdev/clk.h>
+#include "pll.h"
+
+#include <core/device.h>
+#include <subdev/timer.h>
+#include <subdev/bios.h>
+#include <subdev/bios/pll.h>
+
+struct gk104_clk_info {
+ u32 freq;
+ u32 ssel;
+ u32 mdiv;
+ u32 dsrc;
+ u32 ddiv;
+ u32 coef;
+};
+
+struct gk104_clk_priv {
+ struct nvkm_clk base;
+ struct gk104_clk_info eng[16];
+};
+
+static u32 read_div(struct gk104_clk_priv *, int, u32, u32);
+static u32 read_pll(struct gk104_clk_priv *, u32);
+
+static u32
+read_vco(struct gk104_clk_priv *priv, u32 dsrc)
+{
+ u32 ssrc = nv_rd32(priv, dsrc);
+ if (!(ssrc & 0x00000100))
+ return read_pll(priv, 0x00e800);
+ return read_pll(priv, 0x00e820);
+}
+
+static u32
+read_pll(struct gk104_clk_priv *priv, u32 pll)
+{
+ u32 ctrl = nv_rd32(priv, pll + 0x00);
+ u32 coef = nv_rd32(priv, pll + 0x04);
+ u32 P = (coef & 0x003f0000) >> 16;
+ u32 N = (coef & 0x0000ff00) >> 8;
+ u32 M = (coef & 0x000000ff) >> 0;
+ u32 sclk;
+ u16 fN = 0xf000;
+
+ if (!(ctrl & 0x00000001))
+ return 0;
+
+ switch (pll) {
+ case 0x00e800:
+ case 0x00e820:
+ sclk = nv_device(priv)->crystal;
+ P = 1;
+ break;
+ case 0x132000:
+ sclk = read_pll(priv, 0x132020);
+ P = (coef & 0x10000000) ? 2 : 1;
+ break;
+ case 0x132020:
+ sclk = read_div(priv, 0, 0x137320, 0x137330);
+ fN = nv_rd32(priv, pll + 0x10) >> 16;
+ break;
+ case 0x137000:
+ case 0x137020:
+ case 0x137040:
+ case 0x1370e0:
+ sclk = read_div(priv, (pll & 0xff) / 0x20, 0x137120, 0x137140);
+ break;
+ default:
+ return 0;
+ }
+
+ if (P == 0)
+ P = 1;
+
+ sclk = (sclk * N) + (((u16)(fN + 4096) * sclk) >> 13);
+ return sclk / (M * P);
+}
+
+static u32
+read_div(struct gk104_clk_priv *priv, int doff, u32 dsrc, u32 dctl)
+{
+ u32 ssrc = nv_rd32(priv, dsrc + (doff * 4));
+ u32 sctl = nv_rd32(priv, dctl + (doff * 4));
+
+ switch (ssrc & 0x00000003) {
+ case 0:
+ if ((ssrc & 0x00030000) != 0x00030000)
+ return nv_device(priv)->crystal;
+ return 108000;
+ case 2:
+ return 100000;
+ case 3:
+ if (sctl & 0x80000000) {
+ u32 sclk = read_vco(priv, dsrc + (doff * 4));
+ u32 sdiv = (sctl & 0x0000003f) + 2;
+ return (sclk * 2) / sdiv;
+ }
+
+ return read_vco(priv, dsrc + (doff * 4));
+ default:
+ return 0;
+ }
+}
+
+static u32
+read_mem(struct gk104_clk_priv *priv)
+{
+ switch (nv_rd32(priv, 0x1373f4) & 0x0000000f) {
+ case 1: return read_pll(priv, 0x132020);
+ case 2: return read_pll(priv, 0x132000);
+ default:
+ return 0;
+ }
+}
+
+static u32
+read_clk(struct gk104_clk_priv *priv, int clk)
+{
+ u32 sctl = nv_rd32(priv, 0x137250 + (clk * 4));
+ u32 sclk, sdiv;
+
+ if (clk < 7) {
+ u32 ssel = nv_rd32(priv, 0x137100);
+ if (ssel & (1 << clk)) {
+ sclk = read_pll(priv, 0x137000 + (clk * 0x20));
+ sdiv = 1;
+ } else {
+ sclk = read_div(priv, clk, 0x137160, 0x1371d0);
+ sdiv = 0;
+ }
+ } else {
+ u32 ssrc = nv_rd32(priv, 0x137160 + (clk * 0x04));
+ if ((ssrc & 0x00000003) == 0x00000003) {
+ sclk = read_div(priv, clk, 0x137160, 0x1371d0);
+ if (ssrc & 0x00000100) {
+ if (ssrc & 0x40000000)
+ sclk = read_pll(priv, 0x1370e0);
+ sdiv = 1;
+ } else {
+ sdiv = 0;
+ }
+ } else {
+ sclk = read_div(priv, clk, 0x137160, 0x1371d0);
+ sdiv = 0;
+ }
+ }
+
+ if (sctl & 0x80000000) {
+ if (sdiv)
+ sdiv = ((sctl & 0x00003f00) >> 8) + 2;
+ else
+ sdiv = ((sctl & 0x0000003f) >> 0) + 2;
+ return (sclk * 2) / sdiv;
+ }
+
+ return sclk;
+}
+
+static int
+gk104_clk_read(struct nvkm_clk *clk, enum nv_clk_src src)
+{
+ struct nvkm_device *device = nv_device(clk);
+ struct gk104_clk_priv *priv = (void *)clk;
+
+ switch (src) {
+ case nv_clk_src_crystal:
+ return device->crystal;
+ case nv_clk_src_href:
+ return 100000;
+ case nv_clk_src_mem:
+ return read_mem(priv);
+ case nv_clk_src_gpc:
+ return read_clk(priv, 0x00);
+ case nv_clk_src_rop:
+ return read_clk(priv, 0x01);
+ case nv_clk_src_hubk07:
+ return read_clk(priv, 0x02);
+ case nv_clk_src_hubk06:
+ return read_clk(priv, 0x07);
+ case nv_clk_src_hubk01:
+ return read_clk(priv, 0x08);
+ case nv_clk_src_daemon:
+ return read_clk(priv, 0x0c);
+ case nv_clk_src_vdec:
+ return read_clk(priv, 0x0e);
+ default:
+ nv_error(clk, "invalid clock source %d\n", src);
+ return -EINVAL;
+ }
+}
+
+static u32
+calc_div(struct gk104_clk_priv *priv, int clk, u32 ref, u32 freq, u32 *ddiv)
+{
+ u32 div = min((ref * 2) / freq, (u32)65);
+ if (div < 2)
+ div = 2;
+
+ *ddiv = div - 2;
+ return (ref * 2) / div;
+}
+
+static u32
+calc_src(struct gk104_clk_priv *priv, int clk, u32 freq, u32 *dsrc, u32 *ddiv)
+{
+ u32 sclk;
+
+ /* use one of the fixed frequencies if possible */
+ *ddiv = 0x00000000;
+ switch (freq) {
+ case 27000:
+ case 108000:
+ *dsrc = 0x00000000;
+ if (freq == 108000)
+ *dsrc |= 0x00030000;
+ return freq;
+ case 100000:
+ *dsrc = 0x00000002;
+ return freq;
+ default:
+ *dsrc = 0x00000003;
+ break;
+ }
+
+ /* otherwise, calculate the closest divider */
+ sclk = read_vco(priv, 0x137160 + (clk * 4));
+ if (clk < 7)
+ sclk = calc_div(priv, clk, sclk, freq, ddiv);
+ return sclk;
+}
+
+static u32
+calc_pll(struct gk104_clk_priv *priv, int clk, u32 freq, u32 *coef)
+{
+ struct nvkm_bios *bios = nvkm_bios(priv);
+ struct nvbios_pll limits;
+ int N, M, P, ret;
+
+ ret = nvbios_pll_parse(bios, 0x137000 + (clk * 0x20), &limits);
+ if (ret)
+ return 0;
+
+ limits.refclk = read_div(priv, clk, 0x137120, 0x137140);
+ if (!limits.refclk)
+ return 0;
+
+ ret = gt215_pll_calc(nv_subdev(priv), &limits, freq, &N, NULL, &M, &P);
+ if (ret <= 0)
+ return 0;
+
+ *coef = (P << 16) | (N << 8) | M;
+ return ret;
+}
+
+static int
+calc_clk(struct gk104_clk_priv *priv,
+ struct nvkm_cstate *cstate, int clk, int dom)
+{
+ struct gk104_clk_info *info = &priv->eng[clk];
+ u32 freq = cstate->domain[dom];
+ u32 src0, div0, div1D, div1P = 0;
+ u32 clk0, clk1 = 0;
+
+ /* invalid clock domain */
+ if (!freq)
+ return 0;
+
+ /* first possible path, using only dividers */
+ clk0 = calc_src(priv, clk, freq, &src0, &div0);
+ clk0 = calc_div(priv, clk, clk0, freq, &div1D);
+
+ /* see if we can get any closer using PLLs */
+ if (clk0 != freq && (0x0000ff87 & (1 << clk))) {
+ if (clk <= 7)
+ clk1 = calc_pll(priv, clk, freq, &info->coef);
+ else
+ clk1 = cstate->domain[nv_clk_src_hubk06];
+ clk1 = calc_div(priv, clk, clk1, freq, &div1P);
+ }
+
+ /* select the method which gets closest to target freq */
+ if (abs((int)freq - clk0) <= abs((int)freq - clk1)) {
+ info->dsrc = src0;
+ if (div0) {
+ info->ddiv |= 0x80000000;
+ info->ddiv |= div0;
+ }
+ if (div1D) {
+ info->mdiv |= 0x80000000;
+ info->mdiv |= div1D;
+ }
+ info->ssel = 0;
+ info->freq = clk0;
+ } else {
+ if (div1P) {
+ info->mdiv |= 0x80000000;
+ info->mdiv |= div1P << 8;
+ }
+ info->ssel = (1 << clk);
+ info->dsrc = 0x40000100;
+ info->freq = clk1;
+ }
+
+ return 0;
+}
+
+static int
+gk104_clk_calc(struct nvkm_clk *clk, struct nvkm_cstate *cstate)
+{
+ struct gk104_clk_priv *priv = (void *)clk;
+ int ret;
+
+ if ((ret = calc_clk(priv, cstate, 0x00, nv_clk_src_gpc)) ||
+ (ret = calc_clk(priv, cstate, 0x01, nv_clk_src_rop)) ||
+ (ret = calc_clk(priv, cstate, 0x02, nv_clk_src_hubk07)) ||
+ (ret = calc_clk(priv, cstate, 0x07, nv_clk_src_hubk06)) ||
+ (ret = calc_clk(priv, cstate, 0x08, nv_clk_src_hubk01)) ||
+ (ret = calc_clk(priv, cstate, 0x0c, nv_clk_src_daemon)) ||
+ (ret = calc_clk(priv, cstate, 0x0e, nv_clk_src_vdec)))
+ return ret;
+
+ return 0;
+}
+
+static void
+gk104_clk_prog_0(struct gk104_clk_priv *priv, int clk)
+{
+ struct gk104_clk_info *info = &priv->eng[clk];
+ if (!info->ssel) {
+ nv_mask(priv, 0x1371d0 + (clk * 0x04), 0x8000003f, info->ddiv);
+ nv_wr32(priv, 0x137160 + (clk * 0x04), info->dsrc);
+ }
+}
+
+static void
+gk104_clk_prog_1_0(struct gk104_clk_priv *priv, int clk)
+{
+ nv_mask(priv, 0x137100, (1 << clk), 0x00000000);
+ nv_wait(priv, 0x137100, (1 << clk), 0x00000000);
+}
+
+static void
+gk104_clk_prog_1_1(struct gk104_clk_priv *priv, int clk)
+{
+ nv_mask(priv, 0x137160 + (clk * 0x04), 0x00000100, 0x00000000);
+}
+
+static void
+gk104_clk_prog_2(struct gk104_clk_priv *priv, int clk)
+{
+ struct gk104_clk_info *info = &priv->eng[clk];
+ const u32 addr = 0x137000 + (clk * 0x20);
+ nv_mask(priv, addr + 0x00, 0x00000004, 0x00000000);
+ nv_mask(priv, addr + 0x00, 0x00000001, 0x00000000);
+ if (info->coef) {
+ nv_wr32(priv, addr + 0x04, info->coef);
+ nv_mask(priv, addr + 0x00, 0x00000001, 0x00000001);
+ nv_wait(priv, addr + 0x00, 0x00020000, 0x00020000);
+ nv_mask(priv, addr + 0x00, 0x00020004, 0x00000004);
+ }
+}
+
+static void
+gk104_clk_prog_3(struct gk104_clk_priv *priv, int clk)
+{
+ struct gk104_clk_info *info = &priv->eng[clk];
+ if (info->ssel)
+ nv_mask(priv, 0x137250 + (clk * 0x04), 0x00003f00, info->mdiv);
+ else
+ nv_mask(priv, 0x137250 + (clk * 0x04), 0x0000003f, info->mdiv);
+}
+
+static void
+gk104_clk_prog_4_0(struct gk104_clk_priv *priv, int clk)
+{
+ struct gk104_clk_info *info = &priv->eng[clk];
+ if (info->ssel) {
+ nv_mask(priv, 0x137100, (1 << clk), info->ssel);
+ nv_wait(priv, 0x137100, (1 << clk), info->ssel);
+ }
+}
+
+static void
+gk104_clk_prog_4_1(struct gk104_clk_priv *priv, int clk)
+{
+ struct gk104_clk_info *info = &priv->eng[clk];
+ if (info->ssel) {
+ nv_mask(priv, 0x137160 + (clk * 0x04), 0x40000000, 0x40000000);
+ nv_mask(priv, 0x137160 + (clk * 0x04), 0x00000100, 0x00000100);
+ }
+}
+
+static int
+gk104_clk_prog(struct nvkm_clk *clk)
+{
+ struct gk104_clk_priv *priv = (void *)clk;
+ struct {
+ u32 mask;
+ void (*exec)(struct gk104_clk_priv *, int);
+ } stage[] = {
+ { 0x007f, gk104_clk_prog_0 }, /* div programming */
+ { 0x007f, gk104_clk_prog_1_0 }, /* select div mode */
+ { 0xff80, gk104_clk_prog_1_1 },
+ { 0x00ff, gk104_clk_prog_2 }, /* (maybe) program pll */
+ { 0xff80, gk104_clk_prog_3 }, /* final divider */
+ { 0x007f, gk104_clk_prog_4_0 }, /* (maybe) select pll mode */
+ { 0xff80, gk104_clk_prog_4_1 },
+ };
+ int i, j;
+
+ for (i = 0; i < ARRAY_SIZE(stage); i++) {
+ for (j = 0; j < ARRAY_SIZE(priv->eng); j++) {
+ if (!(stage[i].mask & (1 << j)))
+ continue;
+ if (!priv->eng[j].freq)
+ continue;
+ stage[i].exec(priv, j);
+ }
+ }
+
+ return 0;
+}
+
+static void
+gk104_clk_tidy(struct nvkm_clk *clk)
+{
+ struct gk104_clk_priv *priv = (void *)clk;
+ memset(priv->eng, 0x00, sizeof(priv->eng));
+}
+
+static struct nvkm_domain
+gk104_domain[] = {
+ { nv_clk_src_crystal, 0xff },
+ { nv_clk_src_href , 0xff },
+ { nv_clk_src_gpc , 0x00, NVKM_CLK_DOM_FLAG_CORE, "core", 2000 },
+ { nv_clk_src_hubk07 , 0x01, NVKM_CLK_DOM_FLAG_CORE },
+ { nv_clk_src_rop , 0x02, NVKM_CLK_DOM_FLAG_CORE },
+ { nv_clk_src_mem , 0x03, 0, "memory", 500 },
+ { nv_clk_src_hubk06 , 0x04, NVKM_CLK_DOM_FLAG_CORE },
+ { nv_clk_src_hubk01 , 0x05 },
+ { nv_clk_src_vdec , 0x06 },
+ { nv_clk_src_daemon , 0x07 },
+ { nv_clk_src_max }
+};
+
+static int
+gk104_clk_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 size,
+ struct nvkm_object **pobject)
+{
+ struct gk104_clk_priv *priv;
+ int ret;
+
+ ret = nvkm_clk_create(parent, engine, oclass, gk104_domain,
+ NULL, 0, true, &priv);
+ *pobject = nv_object(priv);
+ if (ret)
+ return ret;
+
+ priv->base.read = gk104_clk_read;
+ priv->base.calc = gk104_clk_calc;
+ priv->base.prog = gk104_clk_prog;
+ priv->base.tidy = gk104_clk_tidy;
+ return 0;
+}
+
+struct nvkm_oclass
+gk104_clk_oclass = {
+ .handle = NV_SUBDEV(CLK, 0xe0),
+ .ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = gk104_clk_ctor,
+ .dtor = _nvkm_clk_dtor,
+ .init = _nvkm_clk_init,
+ .fini = _nvkm_clk_fini,
+ },
+};
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk20a.c b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk20a.c
new file mode 100644
index 000000000000..65c532742b08
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk20a.c
@@ -0,0 +1,680 @@
+/*
+ * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Shamelessly ripped off from ChromeOS's gk20a/clk_pllg.c
+ *
+ */
+#include <subdev/clk.h>
+#include <subdev/timer.h>
+
+#include <core/device.h>
+
+#ifdef __KERNEL__
+#include <nouveau_platform.h>
+#endif
+
+#define MHZ (1000 * 1000)
+
+#define MASK(w) ((1 << w) - 1)
+
+#define SYS_GPCPLL_CFG_BASE 0x00137000
+#define GPC_BCASE_GPCPLL_CFG_BASE 0x00132800
+
+#define GPCPLL_CFG (SYS_GPCPLL_CFG_BASE + 0)
+#define GPCPLL_CFG_ENABLE BIT(0)
+#define GPCPLL_CFG_IDDQ BIT(1)
+#define GPCPLL_CFG_LOCK_DET_OFF BIT(4)
+#define GPCPLL_CFG_LOCK BIT(17)
+
+#define GPCPLL_COEFF (SYS_GPCPLL_CFG_BASE + 4)
+#define GPCPLL_COEFF_M_SHIFT 0
+#define GPCPLL_COEFF_M_WIDTH 8
+#define GPCPLL_COEFF_N_SHIFT 8
+#define GPCPLL_COEFF_N_WIDTH 8
+#define GPCPLL_COEFF_P_SHIFT 16
+#define GPCPLL_COEFF_P_WIDTH 6
+
+#define GPCPLL_CFG2 (SYS_GPCPLL_CFG_BASE + 0xc)
+#define GPCPLL_CFG2_SETUP2_SHIFT 16
+#define GPCPLL_CFG2_PLL_STEPA_SHIFT 24
+
+#define GPCPLL_CFG3 (SYS_GPCPLL_CFG_BASE + 0x18)
+#define GPCPLL_CFG3_PLL_STEPB_SHIFT 16
+
+#define GPCPLL_NDIV_SLOWDOWN (SYS_GPCPLL_CFG_BASE + 0x1c)
+#define GPCPLL_NDIV_SLOWDOWN_NDIV_LO_SHIFT 0
+#define GPCPLL_NDIV_SLOWDOWN_NDIV_MID_SHIFT 8
+#define GPCPLL_NDIV_SLOWDOWN_STEP_SIZE_LO2MID_SHIFT 16
+#define GPCPLL_NDIV_SLOWDOWN_SLOWDOWN_USING_PLL_SHIFT 22
+#define GPCPLL_NDIV_SLOWDOWN_EN_DYNRAMP_SHIFT 31
+
+#define SEL_VCO (SYS_GPCPLL_CFG_BASE + 0x100)
+#define SEL_VCO_GPC2CLK_OUT_SHIFT 0
+
+#define GPC2CLK_OUT (SYS_GPCPLL_CFG_BASE + 0x250)
+#define GPC2CLK_OUT_SDIV14_INDIV4_WIDTH 1
+#define GPC2CLK_OUT_SDIV14_INDIV4_SHIFT 31
+#define GPC2CLK_OUT_SDIV14_INDIV4_MODE 1
+#define GPC2CLK_OUT_VCODIV_WIDTH 6
+#define GPC2CLK_OUT_VCODIV_SHIFT 8
+#define GPC2CLK_OUT_VCODIV1 0
+#define GPC2CLK_OUT_VCODIV_MASK (MASK(GPC2CLK_OUT_VCODIV_WIDTH) << \
+ GPC2CLK_OUT_VCODIV_SHIFT)
+#define GPC2CLK_OUT_BYPDIV_WIDTH 6
+#define GPC2CLK_OUT_BYPDIV_SHIFT 0
+#define GPC2CLK_OUT_BYPDIV31 0x3c
+#define GPC2CLK_OUT_INIT_MASK ((MASK(GPC2CLK_OUT_SDIV14_INDIV4_WIDTH) << \
+ GPC2CLK_OUT_SDIV14_INDIV4_SHIFT)\
+ | (MASK(GPC2CLK_OUT_VCODIV_WIDTH) << GPC2CLK_OUT_VCODIV_SHIFT)\
+ | (MASK(GPC2CLK_OUT_BYPDIV_WIDTH) << GPC2CLK_OUT_BYPDIV_SHIFT))
+#define GPC2CLK_OUT_INIT_VAL ((GPC2CLK_OUT_SDIV14_INDIV4_MODE << \
+ GPC2CLK_OUT_SDIV14_INDIV4_SHIFT) \
+ | (GPC2CLK_OUT_VCODIV1 << GPC2CLK_OUT_VCODIV_SHIFT) \
+ | (GPC2CLK_OUT_BYPDIV31 << GPC2CLK_OUT_BYPDIV_SHIFT))
+
+#define GPC_BCAST_NDIV_SLOWDOWN_DEBUG (GPC_BCASE_GPCPLL_CFG_BASE + 0xa0)
+#define GPC_BCAST_NDIV_SLOWDOWN_DEBUG_PLL_DYNRAMP_DONE_SYNCED_SHIFT 24
+#define GPC_BCAST_NDIV_SLOWDOWN_DEBUG_PLL_DYNRAMP_DONE_SYNCED_MASK \
+ (0x1 << GPC_BCAST_NDIV_SLOWDOWN_DEBUG_PLL_DYNRAMP_DONE_SYNCED_SHIFT)
+
+static const u8 pl_to_div[] = {
+/* PL: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 */
+/* p: */ 1, 2, 3, 4, 5, 6, 8, 10, 12, 16, 12, 16, 20, 24, 32,
+};
+
+/* All frequencies in Mhz */
+struct gk20a_clk_pllg_params {
+ u32 min_vco, max_vco;
+ u32 min_u, max_u;
+ u32 min_m, max_m;
+ u32 min_n, max_n;
+ u32 min_pl, max_pl;
+};
+
+static const struct gk20a_clk_pllg_params gk20a_pllg_params = {
+ .min_vco = 1000, .max_vco = 2064,
+ .min_u = 12, .max_u = 38,
+ .min_m = 1, .max_m = 255,
+ .min_n = 8, .max_n = 255,
+ .min_pl = 1, .max_pl = 32,
+};
+
+struct gk20a_clk_priv {
+ struct nvkm_clk base;
+ const struct gk20a_clk_pllg_params *params;
+ u32 m, n, pl;
+ u32 parent_rate;
+};
+#define to_gk20a_clk(base) container_of(base, struct gk20a_clk_priv, base)
+
+static void
+gk20a_pllg_read_mnp(struct gk20a_clk_priv *priv)
+{
+ u32 val;
+
+ val = nv_rd32(priv, GPCPLL_COEFF);
+ priv->m = (val >> GPCPLL_COEFF_M_SHIFT) & MASK(GPCPLL_COEFF_M_WIDTH);
+ priv->n = (val >> GPCPLL_COEFF_N_SHIFT) & MASK(GPCPLL_COEFF_N_WIDTH);
+ priv->pl = (val >> GPCPLL_COEFF_P_SHIFT) & MASK(GPCPLL_COEFF_P_WIDTH);
+}
+
+static u32
+gk20a_pllg_calc_rate(struct gk20a_clk_priv *priv)
+{
+ u32 rate;
+ u32 divider;
+
+ rate = priv->parent_rate * priv->n;
+ divider = priv->m * pl_to_div[priv->pl];
+ do_div(rate, divider);
+
+ return rate / 2;
+}
+
+static int
+gk20a_pllg_calc_mnp(struct gk20a_clk_priv *priv, unsigned long rate)
+{
+ u32 target_clk_f, ref_clk_f, target_freq;
+ u32 min_vco_f, max_vco_f;
+ u32 low_pl, high_pl, best_pl;
+ u32 target_vco_f, vco_f;
+ u32 best_m, best_n;
+ u32 u_f;
+ u32 m, n, n2;
+ u32 delta, lwv, best_delta = ~0;
+ u32 pl;
+
+ target_clk_f = rate * 2 / MHZ;
+ ref_clk_f = priv->parent_rate / MHZ;
+
+ max_vco_f = priv->params->max_vco;
+ min_vco_f = priv->params->min_vco;
+ best_m = priv->params->max_m;
+ best_n = priv->params->min_n;
+ best_pl = priv->params->min_pl;
+
+ target_vco_f = target_clk_f + target_clk_f / 50;
+ if (max_vco_f < target_vco_f)
+ max_vco_f = target_vco_f;
+
+ /* min_pl <= high_pl <= max_pl */
+ high_pl = (max_vco_f + target_vco_f - 1) / target_vco_f;
+ high_pl = min(high_pl, priv->params->max_pl);
+ high_pl = max(high_pl, priv->params->min_pl);
+
+ /* min_pl <= low_pl <= max_pl */
+ low_pl = min_vco_f / target_vco_f;
+ low_pl = min(low_pl, priv->params->max_pl);
+ low_pl = max(low_pl, priv->params->min_pl);
+
+ /* Find Indices of high_pl and low_pl */
+ for (pl = 0; pl < ARRAY_SIZE(pl_to_div) - 1; pl++) {
+ if (pl_to_div[pl] >= low_pl) {
+ low_pl = pl;
+ break;
+ }
+ }
+ for (pl = 0; pl < ARRAY_SIZE(pl_to_div) - 1; pl++) {
+ if (pl_to_div[pl] >= high_pl) {
+ high_pl = pl;
+ break;
+ }
+ }
+
+ nv_debug(priv, "low_PL %d(div%d), high_PL %d(div%d)", low_pl,
+ pl_to_div[low_pl], high_pl, pl_to_div[high_pl]);
+
+ /* Select lowest possible VCO */
+ for (pl = low_pl; pl <= high_pl; pl++) {
+ target_vco_f = target_clk_f * pl_to_div[pl];
+ for (m = priv->params->min_m; m <= priv->params->max_m; m++) {
+ u_f = ref_clk_f / m;
+
+ if (u_f < priv->params->min_u)
+ break;
+ if (u_f > priv->params->max_u)
+ continue;
+
+ n = (target_vco_f * m) / ref_clk_f;
+ n2 = ((target_vco_f * m) + (ref_clk_f - 1)) / ref_clk_f;
+
+ if (n > priv->params->max_n)
+ break;
+
+ for (; n <= n2; n++) {
+ if (n < priv->params->min_n)
+ continue;
+ if (n > priv->params->max_n)
+ break;
+
+ vco_f = ref_clk_f * n / m;
+
+ if (vco_f >= min_vco_f && vco_f <= max_vco_f) {
+ lwv = (vco_f + (pl_to_div[pl] / 2))
+ / pl_to_div[pl];
+ delta = abs(lwv - target_clk_f);
+
+ if (delta < best_delta) {
+ best_delta = delta;
+ best_m = m;
+ best_n = n;
+ best_pl = pl;
+
+ if (best_delta == 0)
+ goto found_match;
+ }
+ }
+ }
+ }
+ }
+
+found_match:
+ WARN_ON(best_delta == ~0);
+
+ if (best_delta != 0)
+ nv_debug(priv, "no best match for target @ %dMHz on gpc_pll",
+ target_clk_f);
+
+ priv->m = best_m;
+ priv->n = best_n;
+ priv->pl = best_pl;
+
+ target_freq = gk20a_pllg_calc_rate(priv) / MHZ;
+
+ nv_debug(priv, "actual target freq %d MHz, M %d, N %d, PL %d(div%d)\n",
+ target_freq, priv->m, priv->n, priv->pl, pl_to_div[priv->pl]);
+ return 0;
+}
+
+static int
+gk20a_pllg_slide(struct gk20a_clk_priv *priv, u32 n)
+{
+ u32 val;
+ int ramp_timeout;
+
+ /* get old coefficients */
+ val = nv_rd32(priv, GPCPLL_COEFF);
+ /* do nothing if NDIV is the same */
+ if (n == ((val >> GPCPLL_COEFF_N_SHIFT) & MASK(GPCPLL_COEFF_N_WIDTH)))
+ return 0;
+
+ /* setup */
+ nv_mask(priv, GPCPLL_CFG2, 0xff << GPCPLL_CFG2_PLL_STEPA_SHIFT,
+ 0x2b << GPCPLL_CFG2_PLL_STEPA_SHIFT);
+ nv_mask(priv, GPCPLL_CFG3, 0xff << GPCPLL_CFG3_PLL_STEPB_SHIFT,
+ 0xb << GPCPLL_CFG3_PLL_STEPB_SHIFT);
+
+ /* pll slowdown mode */
+ nv_mask(priv, GPCPLL_NDIV_SLOWDOWN,
+ BIT(GPCPLL_NDIV_SLOWDOWN_SLOWDOWN_USING_PLL_SHIFT),
+ BIT(GPCPLL_NDIV_SLOWDOWN_SLOWDOWN_USING_PLL_SHIFT));
+
+ /* new ndiv ready for ramp */
+ val = nv_rd32(priv, GPCPLL_COEFF);
+ val &= ~(MASK(GPCPLL_COEFF_N_WIDTH) << GPCPLL_COEFF_N_SHIFT);
+ val |= (n & MASK(GPCPLL_COEFF_N_WIDTH)) << GPCPLL_COEFF_N_SHIFT;
+ udelay(1);
+ nv_wr32(priv, GPCPLL_COEFF, val);
+
+ /* dynamic ramp to new ndiv */
+ val = nv_rd32(priv, GPCPLL_NDIV_SLOWDOWN);
+ val |= 0x1 << GPCPLL_NDIV_SLOWDOWN_EN_DYNRAMP_SHIFT;
+ udelay(1);
+ nv_wr32(priv, GPCPLL_NDIV_SLOWDOWN, val);
+
+ for (ramp_timeout = 500; ramp_timeout > 0; ramp_timeout--) {
+ udelay(1);
+ val = nv_rd32(priv, GPC_BCAST_NDIV_SLOWDOWN_DEBUG);
+ if (val & GPC_BCAST_NDIV_SLOWDOWN_DEBUG_PLL_DYNRAMP_DONE_SYNCED_MASK)
+ break;
+ }
+
+ /* exit slowdown mode */
+ nv_mask(priv, GPCPLL_NDIV_SLOWDOWN,
+ BIT(GPCPLL_NDIV_SLOWDOWN_SLOWDOWN_USING_PLL_SHIFT) |
+ BIT(GPCPLL_NDIV_SLOWDOWN_EN_DYNRAMP_SHIFT), 0);
+ nv_rd32(priv, GPCPLL_NDIV_SLOWDOWN);
+
+ if (ramp_timeout <= 0) {
+ nv_error(priv, "gpcpll dynamic ramp timeout\n");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static void
+_gk20a_pllg_enable(struct gk20a_clk_priv *priv)
+{
+ nv_mask(priv, GPCPLL_CFG, GPCPLL_CFG_ENABLE, GPCPLL_CFG_ENABLE);
+ nv_rd32(priv, GPCPLL_CFG);
+}
+
+static void
+_gk20a_pllg_disable(struct gk20a_clk_priv *priv)
+{
+ nv_mask(priv, GPCPLL_CFG, GPCPLL_CFG_ENABLE, 0);
+ nv_rd32(priv, GPCPLL_CFG);
+}
+
+static int
+_gk20a_pllg_program_mnp(struct gk20a_clk_priv *priv, bool allow_slide)
+{
+ u32 val, cfg;
+ u32 m_old, pl_old, n_lo;
+
+ /* get old coefficients */
+ val = nv_rd32(priv, GPCPLL_COEFF);
+ m_old = (val >> GPCPLL_COEFF_M_SHIFT) & MASK(GPCPLL_COEFF_M_WIDTH);
+ pl_old = (val >> GPCPLL_COEFF_P_SHIFT) & MASK(GPCPLL_COEFF_P_WIDTH);
+
+ /* do NDIV slide if there is no change in M and PL */
+ cfg = nv_rd32(priv, GPCPLL_CFG);
+ if (allow_slide && priv->m == m_old && priv->pl == pl_old &&
+ (cfg & GPCPLL_CFG_ENABLE)) {
+ return gk20a_pllg_slide(priv, priv->n);
+ }
+
+ /* slide down to NDIV_LO */
+ n_lo = DIV_ROUND_UP(m_old * priv->params->min_vco,
+ priv->parent_rate / MHZ);
+ if (allow_slide && (cfg & GPCPLL_CFG_ENABLE)) {
+ int ret = gk20a_pllg_slide(priv, n_lo);
+
+ if (ret)
+ return ret;
+ }
+
+ /* split FO-to-bypass jump in halfs by setting out divider 1:2 */
+ nv_mask(priv, GPC2CLK_OUT, GPC2CLK_OUT_VCODIV_MASK,
+ 0x2 << GPC2CLK_OUT_VCODIV_SHIFT);
+
+ /* put PLL in bypass before programming it */
+ val = nv_rd32(priv, SEL_VCO);
+ val &= ~(BIT(SEL_VCO_GPC2CLK_OUT_SHIFT));
+ udelay(2);
+ nv_wr32(priv, SEL_VCO, val);
+
+ /* get out from IDDQ */
+ val = nv_rd32(priv, GPCPLL_CFG);
+ if (val & GPCPLL_CFG_IDDQ) {
+ val &= ~GPCPLL_CFG_IDDQ;
+ nv_wr32(priv, GPCPLL_CFG, val);
+ nv_rd32(priv, GPCPLL_CFG);
+ udelay(2);
+ }
+
+ _gk20a_pllg_disable(priv);
+
+ nv_debug(priv, "%s: m=%d n=%d pl=%d\n", __func__, priv->m, priv->n,
+ priv->pl);
+
+ n_lo = DIV_ROUND_UP(priv->m * priv->params->min_vco,
+ priv->parent_rate / MHZ);
+ val = priv->m << GPCPLL_COEFF_M_SHIFT;
+ val |= (allow_slide ? n_lo : priv->n) << GPCPLL_COEFF_N_SHIFT;
+ val |= priv->pl << GPCPLL_COEFF_P_SHIFT;
+ nv_wr32(priv, GPCPLL_COEFF, val);
+
+ _gk20a_pllg_enable(priv);
+
+ val = nv_rd32(priv, GPCPLL_CFG);
+ if (val & GPCPLL_CFG_LOCK_DET_OFF) {
+ val &= ~GPCPLL_CFG_LOCK_DET_OFF;
+ nv_wr32(priv, GPCPLL_CFG, val);
+ }
+
+ if (!nvkm_timer_wait_eq(priv, 300000, GPCPLL_CFG, GPCPLL_CFG_LOCK,
+ GPCPLL_CFG_LOCK)) {
+ nv_error(priv, "%s: timeout waiting for pllg lock\n", __func__);
+ return -ETIMEDOUT;
+ }
+
+ /* switch to VCO mode */
+ nv_mask(priv, SEL_VCO, 0, BIT(SEL_VCO_GPC2CLK_OUT_SHIFT));
+
+ /* restore out divider 1:1 */
+ val = nv_rd32(priv, GPC2CLK_OUT);
+ val &= ~GPC2CLK_OUT_VCODIV_MASK;
+ udelay(2);
+ nv_wr32(priv, GPC2CLK_OUT, val);
+
+ /* slide up to new NDIV */
+ return allow_slide ? gk20a_pllg_slide(priv, priv->n) : 0;
+}
+
+static int
+gk20a_pllg_program_mnp(struct gk20a_clk_priv *priv)
+{
+ int err;
+
+ err = _gk20a_pllg_program_mnp(priv, true);
+ if (err)
+ err = _gk20a_pllg_program_mnp(priv, false);
+
+ return err;
+}
+
+static void
+gk20a_pllg_disable(struct gk20a_clk_priv *priv)
+{
+ u32 val;
+
+ /* slide to VCO min */
+ val = nv_rd32(priv, GPCPLL_CFG);
+ if (val & GPCPLL_CFG_ENABLE) {
+ u32 coeff, m, n_lo;
+
+ coeff = nv_rd32(priv, GPCPLL_COEFF);
+ m = (coeff >> GPCPLL_COEFF_M_SHIFT) & MASK(GPCPLL_COEFF_M_WIDTH);
+ n_lo = DIV_ROUND_UP(m * priv->params->min_vco,
+ priv->parent_rate / MHZ);
+ gk20a_pllg_slide(priv, n_lo);
+ }
+
+ /* put PLL in bypass before disabling it */
+ nv_mask(priv, SEL_VCO, BIT(SEL_VCO_GPC2CLK_OUT_SHIFT), 0);
+
+ _gk20a_pllg_disable(priv);
+}
+
+#define GK20A_CLK_GPC_MDIV 1000
+
+static struct nvkm_domain
+gk20a_domains[] = {
+ { nv_clk_src_crystal, 0xff },
+ { nv_clk_src_gpc, 0xff, 0, "core", GK20A_CLK_GPC_MDIV },
+ { nv_clk_src_max }
+};
+
+static struct nvkm_pstate
+gk20a_pstates[] = {
+ {
+ .base = {
+ .domain[nv_clk_src_gpc] = 72000,
+ .voltage = 0,
+ },
+ },
+ {
+ .base = {
+ .domain[nv_clk_src_gpc] = 108000,
+ .voltage = 1,
+ },
+ },
+ {
+ .base = {
+ .domain[nv_clk_src_gpc] = 180000,
+ .voltage = 2,
+ },
+ },
+ {
+ .base = {
+ .domain[nv_clk_src_gpc] = 252000,
+ .voltage = 3,
+ },
+ },
+ {
+ .base = {
+ .domain[nv_clk_src_gpc] = 324000,
+ .voltage = 4,
+ },
+ },
+ {
+ .base = {
+ .domain[nv_clk_src_gpc] = 396000,
+ .voltage = 5,
+ },
+ },
+ {
+ .base = {
+ .domain[nv_clk_src_gpc] = 468000,
+ .voltage = 6,
+ },
+ },
+ {
+ .base = {
+ .domain[nv_clk_src_gpc] = 540000,
+ .voltage = 7,
+ },
+ },
+ {
+ .base = {
+ .domain[nv_clk_src_gpc] = 612000,
+ .voltage = 8,
+ },
+ },
+ {
+ .base = {
+ .domain[nv_clk_src_gpc] = 648000,
+ .voltage = 9,
+ },
+ },
+ {
+ .base = {
+ .domain[nv_clk_src_gpc] = 684000,
+ .voltage = 10,
+ },
+ },
+ {
+ .base = {
+ .domain[nv_clk_src_gpc] = 708000,
+ .voltage = 11,
+ },
+ },
+ {
+ .base = {
+ .domain[nv_clk_src_gpc] = 756000,
+ .voltage = 12,
+ },
+ },
+ {
+ .base = {
+ .domain[nv_clk_src_gpc] = 804000,
+ .voltage = 13,
+ },
+ },
+ {
+ .base = {
+ .domain[nv_clk_src_gpc] = 852000,
+ .voltage = 14,
+ },
+ },
+};
+
+static int
+gk20a_clk_read(struct nvkm_clk *clk, enum nv_clk_src src)
+{
+ struct gk20a_clk_priv *priv = (void *)clk;
+
+ switch (src) {
+ case nv_clk_src_crystal:
+ return nv_device(clk)->crystal;
+ case nv_clk_src_gpc:
+ gk20a_pllg_read_mnp(priv);
+ return gk20a_pllg_calc_rate(priv) / GK20A_CLK_GPC_MDIV;
+ default:
+ nv_error(clk, "invalid clock source %d\n", src);
+ return -EINVAL;
+ }
+}
+
+static int
+gk20a_clk_calc(struct nvkm_clk *clk, struct nvkm_cstate *cstate)
+{
+ struct gk20a_clk_priv *priv = (void *)clk;
+
+ return gk20a_pllg_calc_mnp(priv, cstate->domain[nv_clk_src_gpc] *
+ GK20A_CLK_GPC_MDIV);
+}
+
+static int
+gk20a_clk_prog(struct nvkm_clk *clk)
+{
+ struct gk20a_clk_priv *priv = (void *)clk;
+
+ return gk20a_pllg_program_mnp(priv);
+}
+
+static void
+gk20a_clk_tidy(struct nvkm_clk *clk)
+{
+}
+
+static int
+gk20a_clk_fini(struct nvkm_object *object, bool suspend)
+{
+ struct gk20a_clk_priv *priv = (void *)object;
+ int ret;
+
+ ret = nvkm_clk_fini(&priv->base, false);
+
+ gk20a_pllg_disable(priv);
+
+ return ret;
+}
+
+static int
+gk20a_clk_init(struct nvkm_object *object)
+{
+ struct gk20a_clk_priv *priv = (void *)object;
+ int ret;
+
+ nv_mask(priv, GPC2CLK_OUT, GPC2CLK_OUT_INIT_MASK, GPC2CLK_OUT_INIT_VAL);
+
+ ret = nvkm_clk_init(&priv->base);
+ if (ret)
+ return ret;
+
+ ret = gk20a_clk_prog(&priv->base);
+ if (ret) {
+ nv_error(priv, "cannot initialize clock\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int
+gk20a_clk_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 size,
+ struct nvkm_object **pobject)
+{
+ struct gk20a_clk_priv *priv;
+ struct nouveau_platform_device *plat;
+ int ret;
+ int i;
+
+ /* Finish initializing the pstates */
+ for (i = 0; i < ARRAY_SIZE(gk20a_pstates); i++) {
+ INIT_LIST_HEAD(&gk20a_pstates[i].list);
+ gk20a_pstates[i].pstate = i + 1;
+ }
+
+ ret = nvkm_clk_create(parent, engine, oclass, gk20a_domains,
+ gk20a_pstates, ARRAY_SIZE(gk20a_pstates),
+ true, &priv);
+ *pobject = nv_object(priv);
+ if (ret)
+ return ret;
+
+ priv->params = &gk20a_pllg_params;
+
+ plat = nv_device_to_platform(nv_device(parent));
+ priv->parent_rate = clk_get_rate(plat->gpu->clk);
+ nv_info(priv, "parent clock rate: %d Mhz\n", priv->parent_rate / MHZ);
+
+ priv->base.read = gk20a_clk_read;
+ priv->base.calc = gk20a_clk_calc;
+ priv->base.prog = gk20a_clk_prog;
+ priv->base.tidy = gk20a_clk_tidy;
+ return 0;
+}
+
+struct nvkm_oclass
+gk20a_clk_oclass = {
+ .handle = NV_SUBDEV(CLK, 0xea),
+ .ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = gk20a_clk_ctor,
+ .dtor = _nvkm_subdev_dtor,
+ .init = gk20a_clk_init,
+ .fini = gk20a_clk_fini,
+ },
+};
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gt215.c b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gt215.c
new file mode 100644
index 000000000000..822d32a28d6e
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gt215.c
@@ -0,0 +1,533 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ * Roy Spliet
+ */
+#include "gt215.h"
+#include "pll.h"
+
+#include <core/device.h>
+#include <engine/fifo.h>
+#include <subdev/bios.h>
+#include <subdev/bios/pll.h>
+#include <subdev/timer.h>
+
+struct gt215_clk_priv {
+ struct nvkm_clk base;
+ struct gt215_clk_info eng[nv_clk_src_max];
+};
+
+static u32 read_clk(struct gt215_clk_priv *, int, bool);
+static u32 read_pll(struct gt215_clk_priv *, int, u32);
+
+static u32
+read_vco(struct gt215_clk_priv *priv, int clk)
+{
+ u32 sctl = nv_rd32(priv, 0x4120 + (clk * 4));
+
+ switch (sctl & 0x00000030) {
+ case 0x00000000:
+ return nv_device(priv)->crystal;
+ case 0x00000020:
+ return read_pll(priv, 0x41, 0x00e820);
+ case 0x00000030:
+ return read_pll(priv, 0x42, 0x00e8a0);
+ default:
+ return 0;
+ }
+}
+
+static u32
+read_clk(struct gt215_clk_priv *priv, int clk, bool ignore_en)
+{
+ u32 sctl, sdiv, sclk;
+
+ /* refclk for the 0xe8xx plls is a fixed frequency */
+ if (clk >= 0x40) {
+ if (nv_device(priv)->chipset == 0xaf) {
+ /* no joke.. seriously.. sigh.. */
+ return nv_rd32(priv, 0x00471c) * 1000;
+ }
+
+ return nv_device(priv)->crystal;
+ }
+
+ sctl = nv_rd32(priv, 0x4120 + (clk * 4));
+ if (!ignore_en && !(sctl & 0x00000100))
+ return 0;
+
+ /* out_alt */
+ if (sctl & 0x00000400)
+ return 108000;
+
+ /* vco_out */
+ switch (sctl & 0x00003000) {
+ case 0x00000000:
+ if (!(sctl & 0x00000200))
+ return nv_device(priv)->crystal;
+ return 0;
+ case 0x00002000:
+ if (sctl & 0x00000040)
+ return 108000;
+ return 100000;
+ case 0x00003000:
+ /* vco_enable */
+ if (!(sctl & 0x00000001))
+ return 0;
+
+ sclk = read_vco(priv, clk);
+ sdiv = ((sctl & 0x003f0000) >> 16) + 2;
+ return (sclk * 2) / sdiv;
+ default:
+ return 0;
+ }
+}
+
+static u32
+read_pll(struct gt215_clk_priv *priv, int clk, u32 pll)
+{
+ u32 ctrl = nv_rd32(priv, pll + 0);
+ u32 sclk = 0, P = 1, N = 1, M = 1;
+
+ if (!(ctrl & 0x00000008)) {
+ if (ctrl & 0x00000001) {
+ u32 coef = nv_rd32(priv, pll + 4);
+ M = (coef & 0x000000ff) >> 0;
+ N = (coef & 0x0000ff00) >> 8;
+ P = (coef & 0x003f0000) >> 16;
+
+ /* no post-divider on these..
+ * XXX: it looks more like two post-"dividers" that
+ * cross each other out in the default RPLL config */
+ if ((pll & 0x00ff00) == 0x00e800)
+ P = 1;
+
+ sclk = read_clk(priv, 0x00 + clk, false);
+ }
+ } else {
+ sclk = read_clk(priv, 0x10 + clk, false);
+ }
+
+ if (M * P)
+ return sclk * N / (M * P);
+
+ return 0;
+}
+
+static int
+gt215_clk_read(struct nvkm_clk *clk, enum nv_clk_src src)
+{
+ struct gt215_clk_priv *priv = (void *)clk;
+ u32 hsrc;
+
+ switch (src) {
+ case nv_clk_src_crystal:
+ return nv_device(priv)->crystal;
+ case nv_clk_src_core:
+ case nv_clk_src_core_intm:
+ return read_pll(priv, 0x00, 0x4200);
+ case nv_clk_src_shader:
+ return read_pll(priv, 0x01, 0x4220);
+ case nv_clk_src_mem:
+ return read_pll(priv, 0x02, 0x4000);
+ case nv_clk_src_disp:
+ return read_clk(priv, 0x20, false);
+ case nv_clk_src_vdec:
+ return read_clk(priv, 0x21, false);
+ case nv_clk_src_daemon:
+ return read_clk(priv, 0x25, false);
+ case nv_clk_src_host:
+ hsrc = (nv_rd32(priv, 0xc040) & 0x30000000) >> 28;
+ switch (hsrc) {
+ case 0:
+ return read_clk(priv, 0x1d, false);
+ case 2:
+ case 3:
+ return 277000;
+ default:
+ nv_error(clk, "unknown HOST clock source %d\n", hsrc);
+ return -EINVAL;
+ }
+ default:
+ nv_error(clk, "invalid clock source %d\n", src);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int
+gt215_clk_info(struct nvkm_clk *clock, int clk, u32 khz,
+ struct gt215_clk_info *info)
+{
+ struct gt215_clk_priv *priv = (void *)clock;
+ u32 oclk, sclk, sdiv, diff;
+
+ info->clk = 0;
+
+ switch (khz) {
+ case 27000:
+ info->clk = 0x00000100;
+ return khz;
+ case 100000:
+ info->clk = 0x00002100;
+ return khz;
+ case 108000:
+ info->clk = 0x00002140;
+ return khz;
+ default:
+ sclk = read_vco(priv, clk);
+ sdiv = min((sclk * 2) / khz, (u32)65);
+ oclk = (sclk * 2) / sdiv;
+ diff = ((khz + 3000) - oclk);
+
+ /* When imprecise, play it safe and aim for a clock lower than
+ * desired rather than higher */
+ if (diff < 0) {
+ sdiv++;
+ oclk = (sclk * 2) / sdiv;
+ }
+
+ /* divider can go as low as 2, limited here because NVIDIA
+ * and the VBIOS on my NVA8 seem to prefer using the PLL
+ * for 810MHz - is there a good reason?
+ * XXX: PLLs with refclk 810MHz? */
+ if (sdiv > 4) {
+ info->clk = (((sdiv - 2) << 16) | 0x00003100);
+ return oclk;
+ }
+
+ break;
+ }
+
+ return -ERANGE;
+}
+
+int
+gt215_pll_info(struct nvkm_clk *clock, int clk, u32 pll, u32 khz,
+ struct gt215_clk_info *info)
+{
+ struct nvkm_bios *bios = nvkm_bios(clock);
+ struct gt215_clk_priv *priv = (void *)clock;
+ struct nvbios_pll limits;
+ int P, N, M, diff;
+ int ret;
+
+ info->pll = 0;
+
+ /* If we can get a within [-2, 3) MHz of a divider, we'll disable the
+ * PLL and use the divider instead. */
+ ret = gt215_clk_info(clock, clk, khz, info);
+ diff = khz - ret;
+ if (!pll || (diff >= -2000 && diff < 3000)) {
+ goto out;
+ }
+
+ /* Try with PLL */
+ ret = nvbios_pll_parse(bios, pll, &limits);
+ if (ret)
+ return ret;
+
+ ret = gt215_clk_info(clock, clk - 0x10, limits.refclk, info);
+ if (ret != limits.refclk)
+ return -EINVAL;
+
+ ret = gt215_pll_calc(nv_subdev(priv), &limits, khz, &N, NULL, &M, &P);
+ if (ret >= 0) {
+ info->pll = (P << 16) | (N << 8) | M;
+ }
+
+out:
+ info->fb_delay = max(((khz + 7566) / 15133), (u32) 18);
+ return ret ? ret : -ERANGE;
+}
+
+static int
+calc_clk(struct gt215_clk_priv *priv, struct nvkm_cstate *cstate,
+ int clk, u32 pll, int idx)
+{
+ int ret = gt215_pll_info(&priv->base, clk, pll, cstate->domain[idx],
+ &priv->eng[idx]);
+ if (ret >= 0)
+ return 0;
+ return ret;
+}
+
+static int
+calc_host(struct gt215_clk_priv *priv, struct nvkm_cstate *cstate)
+{
+ int ret = 0;
+ u32 kHz = cstate->domain[nv_clk_src_host];
+ struct gt215_clk_info *info = &priv->eng[nv_clk_src_host];
+
+ if (kHz == 277000) {
+ info->clk = 0;
+ info->host_out = NVA3_HOST_277;
+ return 0;
+ }
+
+ info->host_out = NVA3_HOST_CLK;
+
+ ret = gt215_clk_info(&priv->base, 0x1d, kHz, info);
+ if (ret >= 0)
+ return 0;
+
+ return ret;
+}
+
+int
+gt215_clk_pre(struct nvkm_clk *clk, unsigned long *flags)
+{
+ struct nvkm_fifo *pfifo = nvkm_fifo(clk);
+
+ /* halt and idle execution engines */
+ nv_mask(clk, 0x020060, 0x00070000, 0x00000000);
+ nv_mask(clk, 0x002504, 0x00000001, 0x00000001);
+ /* Wait until the interrupt handler is finished */
+ if (!nv_wait(clk, 0x000100, 0xffffffff, 0x00000000))
+ return -EBUSY;
+
+ if (pfifo)
+ pfifo->pause(pfifo, flags);
+
+ if (!nv_wait(clk, 0x002504, 0x00000010, 0x00000010))
+ return -EIO;
+ if (!nv_wait(clk, 0x00251c, 0x0000003f, 0x0000003f))
+ return -EIO;
+
+ return 0;
+}
+
+void
+gt215_clk_post(struct nvkm_clk *clk, unsigned long *flags)
+{
+ struct nvkm_fifo *pfifo = nvkm_fifo(clk);
+
+ if (pfifo && flags)
+ pfifo->start(pfifo, flags);
+
+ nv_mask(clk, 0x002504, 0x00000001, 0x00000000);
+ nv_mask(clk, 0x020060, 0x00070000, 0x00040000);
+}
+
+static void
+disable_clk_src(struct gt215_clk_priv *priv, u32 src)
+{
+ nv_mask(priv, src, 0x00000100, 0x00000000);
+ nv_mask(priv, src, 0x00000001, 0x00000000);
+}
+
+static void
+prog_pll(struct gt215_clk_priv *priv, int clk, u32 pll, int idx)
+{
+ struct gt215_clk_info *info = &priv->eng[idx];
+ const u32 src0 = 0x004120 + (clk * 4);
+ const u32 src1 = 0x004160 + (clk * 4);
+ const u32 ctrl = pll + 0;
+ const u32 coef = pll + 4;
+ u32 bypass;
+
+ if (info->pll) {
+ /* Always start from a non-PLL clock */
+ bypass = nv_rd32(priv, ctrl) & 0x00000008;
+ if (!bypass) {
+ nv_mask(priv, src1, 0x00000101, 0x00000101);
+ nv_mask(priv, ctrl, 0x00000008, 0x00000008);
+ udelay(20);
+ }
+
+ nv_mask(priv, src0, 0x003f3141, 0x00000101 | info->clk);
+ nv_wr32(priv, coef, info->pll);
+ nv_mask(priv, ctrl, 0x00000015, 0x00000015);
+ nv_mask(priv, ctrl, 0x00000010, 0x00000000);
+ if (!nv_wait(priv, ctrl, 0x00020000, 0x00020000)) {
+ nv_mask(priv, ctrl, 0x00000010, 0x00000010);
+ nv_mask(priv, src0, 0x00000101, 0x00000000);
+ return;
+ }
+ nv_mask(priv, ctrl, 0x00000010, 0x00000010);
+ nv_mask(priv, ctrl, 0x00000008, 0x00000000);
+ disable_clk_src(priv, src1);
+ } else {
+ nv_mask(priv, src1, 0x003f3141, 0x00000101 | info->clk);
+ nv_mask(priv, ctrl, 0x00000018, 0x00000018);
+ udelay(20);
+ nv_mask(priv, ctrl, 0x00000001, 0x00000000);
+ disable_clk_src(priv, src0);
+ }
+}
+
+static void
+prog_clk(struct gt215_clk_priv *priv, int clk, int idx)
+{
+ struct gt215_clk_info *info = &priv->eng[idx];
+ nv_mask(priv, 0x004120 + (clk * 4), 0x003f3141, 0x00000101 | info->clk);
+}
+
+static void
+prog_host(struct gt215_clk_priv *priv)
+{
+ struct gt215_clk_info *info = &priv->eng[nv_clk_src_host];
+ u32 hsrc = (nv_rd32(priv, 0xc040));
+
+ switch (info->host_out) {
+ case NVA3_HOST_277:
+ if ((hsrc & 0x30000000) == 0) {
+ nv_wr32(priv, 0xc040, hsrc | 0x20000000);
+ disable_clk_src(priv, 0x4194);
+ }
+ break;
+ case NVA3_HOST_CLK:
+ prog_clk(priv, 0x1d, nv_clk_src_host);
+ if ((hsrc & 0x30000000) >= 0x20000000) {
+ nv_wr32(priv, 0xc040, hsrc & ~0x30000000);
+ }
+ break;
+ default:
+ break;
+ }
+
+ /* This seems to be a clock gating factor on idle, always set to 64 */
+ nv_wr32(priv, 0xc044, 0x3e);
+}
+
+static void
+prog_core(struct gt215_clk_priv *priv, int idx)
+{
+ struct gt215_clk_info *info = &priv->eng[idx];
+ u32 fb_delay = nv_rd32(priv, 0x10002c);
+
+ if (fb_delay < info->fb_delay)
+ nv_wr32(priv, 0x10002c, info->fb_delay);
+
+ prog_pll(priv, 0x00, 0x004200, idx);
+
+ if (fb_delay > info->fb_delay)
+ nv_wr32(priv, 0x10002c, info->fb_delay);
+}
+
+static int
+gt215_clk_calc(struct nvkm_clk *clk, struct nvkm_cstate *cstate)
+{
+ struct gt215_clk_priv *priv = (void *)clk;
+ struct gt215_clk_info *core = &priv->eng[nv_clk_src_core];
+ int ret;
+
+ if ((ret = calc_clk(priv, cstate, 0x10, 0x4200, nv_clk_src_core)) ||
+ (ret = calc_clk(priv, cstate, 0x11, 0x4220, nv_clk_src_shader)) ||
+ (ret = calc_clk(priv, cstate, 0x20, 0x0000, nv_clk_src_disp)) ||
+ (ret = calc_clk(priv, cstate, 0x21, 0x0000, nv_clk_src_vdec)) ||
+ (ret = calc_host(priv, cstate)))
+ return ret;
+
+ /* XXX: Should be reading the highest bit in the VBIOS clock to decide
+ * whether to use a PLL or not... but using a PLL defeats the purpose */
+ if (core->pll) {
+ ret = gt215_clk_info(clk, 0x10,
+ cstate->domain[nv_clk_src_core_intm],
+ &priv->eng[nv_clk_src_core_intm]);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int
+gt215_clk_prog(struct nvkm_clk *clk)
+{
+ struct gt215_clk_priv *priv = (void *)clk;
+ struct gt215_clk_info *core = &priv->eng[nv_clk_src_core];
+ int ret = 0;
+ unsigned long flags;
+ unsigned long *f = &flags;
+
+ ret = gt215_clk_pre(clk, f);
+ if (ret)
+ goto out;
+
+ if (core->pll)
+ prog_core(priv, nv_clk_src_core_intm);
+
+ prog_core(priv, nv_clk_src_core);
+ prog_pll(priv, 0x01, 0x004220, nv_clk_src_shader);
+ prog_clk(priv, 0x20, nv_clk_src_disp);
+ prog_clk(priv, 0x21, nv_clk_src_vdec);
+ prog_host(priv);
+
+out:
+ if (ret == -EBUSY)
+ f = NULL;
+
+ gt215_clk_post(clk, f);
+ return ret;
+}
+
+static void
+gt215_clk_tidy(struct nvkm_clk *clk)
+{
+}
+
+static struct nvkm_domain
+gt215_domain[] = {
+ { nv_clk_src_crystal , 0xff },
+ { nv_clk_src_core , 0x00, 0, "core", 1000 },
+ { nv_clk_src_shader , 0x01, 0, "shader", 1000 },
+ { nv_clk_src_mem , 0x02, 0, "memory", 1000 },
+ { nv_clk_src_vdec , 0x03 },
+ { nv_clk_src_disp , 0x04 },
+ { nv_clk_src_host , 0x05 },
+ { nv_clk_src_core_intm, 0x06 },
+ { nv_clk_src_max }
+};
+
+static int
+gt215_clk_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 size,
+ struct nvkm_object **pobject)
+{
+ struct gt215_clk_priv *priv;
+ int ret;
+
+ ret = nvkm_clk_create(parent, engine, oclass, gt215_domain,
+ NULL, 0, true, &priv);
+ *pobject = nv_object(priv);
+ if (ret)
+ return ret;
+
+ priv->base.read = gt215_clk_read;
+ priv->base.calc = gt215_clk_calc;
+ priv->base.prog = gt215_clk_prog;
+ priv->base.tidy = gt215_clk_tidy;
+ return 0;
+}
+
+struct nvkm_oclass
+gt215_clk_oclass = {
+ .handle = NV_SUBDEV(CLK, 0xa3),
+ .ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = gt215_clk_ctor,
+ .dtor = _nvkm_clk_dtor,
+ .init = _nvkm_clk_init,
+ .fini = _nvkm_clk_fini,
+ },
+};
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gt215.h b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gt215.h
new file mode 100644
index 000000000000..b447d9cd4d37
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gt215.h
@@ -0,0 +1,18 @@
+#ifndef __NVKM_CLK_NVA3_H__
+#define __NVKM_CLK_NVA3_H__
+#include <subdev/clk.h>
+
+struct gt215_clk_info {
+ u32 clk;
+ u32 pll;
+ enum {
+ NVA3_HOST_277,
+ NVA3_HOST_CLK,
+ } host_out;
+ u32 fb_delay;
+};
+
+int gt215_pll_info(struct nvkm_clk *, int, u32, u32, struct gt215_clk_info *);
+int gt215_clk_pre(struct nvkm_clk *clk, unsigned long *flags);
+void gt215_clk_post(struct nvkm_clk *clk, unsigned long *flags);
+#endif
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/mcp77.c b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/mcp77.c
new file mode 100644
index 000000000000..c54417b146c7
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/mcp77.c
@@ -0,0 +1,429 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "gt215.h"
+#include "pll.h"
+
+#include <core/device.h>
+#include <subdev/bios.h>
+#include <subdev/bios/pll.h>
+#include <subdev/timer.h>
+
+struct mcp77_clk_priv {
+ struct nvkm_clk base;
+ enum nv_clk_src csrc, ssrc, vsrc;
+ u32 cctrl, sctrl;
+ u32 ccoef, scoef;
+ u32 cpost, spost;
+ u32 vdiv;
+};
+
+static u32
+read_div(struct nvkm_clk *clk)
+{
+ return nv_rd32(clk, 0x004600);
+}
+
+static u32
+read_pll(struct nvkm_clk *clk, u32 base)
+{
+ u32 ctrl = nv_rd32(clk, base + 0);
+ u32 coef = nv_rd32(clk, base + 4);
+ u32 ref = clk->read(clk, nv_clk_src_href);
+ u32 post_div = 0;
+ u32 clock = 0;
+ int N1, M1;
+
+ switch (base){
+ case 0x4020:
+ post_div = 1 << ((nv_rd32(clk, 0x4070) & 0x000f0000) >> 16);
+ break;
+ case 0x4028:
+ post_div = (nv_rd32(clk, 0x4040) & 0x000f0000) >> 16;
+ break;
+ default:
+ break;
+ }
+
+ N1 = (coef & 0x0000ff00) >> 8;
+ M1 = (coef & 0x000000ff);
+ if ((ctrl & 0x80000000) && M1) {
+ clock = ref * N1 / M1;
+ clock = clock / post_div;
+ }
+
+ return clock;
+}
+
+static int
+mcp77_clk_read(struct nvkm_clk *clk, enum nv_clk_src src)
+{
+ struct mcp77_clk_priv *priv = (void *)clk;
+ u32 mast = nv_rd32(clk, 0x00c054);
+ u32 P = 0;
+
+ switch (src) {
+ case nv_clk_src_crystal:
+ return nv_device(priv)->crystal;
+ case nv_clk_src_href:
+ return 100000; /* PCIE reference clock */
+ case nv_clk_src_hclkm4:
+ return clk->read(clk, nv_clk_src_href) * 4;
+ case nv_clk_src_hclkm2d3:
+ return clk->read(clk, nv_clk_src_href) * 2 / 3;
+ case nv_clk_src_host:
+ switch (mast & 0x000c0000) {
+ case 0x00000000: return clk->read(clk, nv_clk_src_hclkm2d3);
+ case 0x00040000: break;
+ case 0x00080000: return clk->read(clk, nv_clk_src_hclkm4);
+ case 0x000c0000: return clk->read(clk, nv_clk_src_cclk);
+ }
+ break;
+ case nv_clk_src_core:
+ P = (nv_rd32(clk, 0x004028) & 0x00070000) >> 16;
+
+ switch (mast & 0x00000003) {
+ case 0x00000000: return clk->read(clk, nv_clk_src_crystal) >> P;
+ case 0x00000001: return 0;
+ case 0x00000002: return clk->read(clk, nv_clk_src_hclkm4) >> P;
+ case 0x00000003: return read_pll(clk, 0x004028) >> P;
+ }
+ break;
+ case nv_clk_src_cclk:
+ if ((mast & 0x03000000) != 0x03000000)
+ return clk->read(clk, nv_clk_src_core);
+
+ if ((mast & 0x00000200) == 0x00000000)
+ return clk->read(clk, nv_clk_src_core);
+
+ switch (mast & 0x00000c00) {
+ case 0x00000000: return clk->read(clk, nv_clk_src_href);
+ case 0x00000400: return clk->read(clk, nv_clk_src_hclkm4);
+ case 0x00000800: return clk->read(clk, nv_clk_src_hclkm2d3);
+ default: return 0;
+ }
+ case nv_clk_src_shader:
+ P = (nv_rd32(clk, 0x004020) & 0x00070000) >> 16;
+ switch (mast & 0x00000030) {
+ case 0x00000000:
+ if (mast & 0x00000040)
+ return clk->read(clk, nv_clk_src_href) >> P;
+ return clk->read(clk, nv_clk_src_crystal) >> P;
+ case 0x00000010: break;
+ case 0x00000020: return read_pll(clk, 0x004028) >> P;
+ case 0x00000030: return read_pll(clk, 0x004020) >> P;
+ }
+ break;
+ case nv_clk_src_mem:
+ return 0;
+ break;
+ case nv_clk_src_vdec:
+ P = (read_div(clk) & 0x00000700) >> 8;
+
+ switch (mast & 0x00400000) {
+ case 0x00400000:
+ return clk->read(clk, nv_clk_src_core) >> P;
+ break;
+ default:
+ return 500000 >> P;
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ nv_debug(priv, "unknown clock source %d 0x%08x\n", src, mast);
+ return 0;
+}
+
+static u32
+calc_pll(struct mcp77_clk_priv *priv, u32 reg,
+ u32 clock, int *N, int *M, int *P)
+{
+ struct nvkm_bios *bios = nvkm_bios(priv);
+ struct nvbios_pll pll;
+ struct nvkm_clk *clk = &priv->base;
+ int ret;
+
+ ret = nvbios_pll_parse(bios, reg, &pll);
+ if (ret)
+ return 0;
+
+ pll.vco2.max_freq = 0;
+ pll.refclk = clk->read(clk, nv_clk_src_href);
+ if (!pll.refclk)
+ return 0;
+
+ return nv04_pll_calc(nv_subdev(priv), &pll, clock, N, M, NULL, NULL, P);
+}
+
+static inline u32
+calc_P(u32 src, u32 target, int *div)
+{
+ u32 clk0 = src, clk1 = src;
+ for (*div = 0; *div <= 7; (*div)++) {
+ if (clk0 <= target) {
+ clk1 = clk0 << (*div ? 1 : 0);
+ break;
+ }
+ clk0 >>= 1;
+ }
+
+ if (target - clk0 <= clk1 - target)
+ return clk0;
+ (*div)--;
+ return clk1;
+}
+
+static int
+mcp77_clk_calc(struct nvkm_clk *clk, struct nvkm_cstate *cstate)
+{
+ struct mcp77_clk_priv *priv = (void *)clk;
+ const int shader = cstate->domain[nv_clk_src_shader];
+ const int core = cstate->domain[nv_clk_src_core];
+ const int vdec = cstate->domain[nv_clk_src_vdec];
+ u32 out = 0, clock = 0;
+ int N, M, P1, P2 = 0;
+ int divs = 0;
+
+ /* cclk: find suitable source, disable PLL if we can */
+ if (core < clk->read(clk, nv_clk_src_hclkm4))
+ out = calc_P(clk->read(clk, nv_clk_src_hclkm4), core, &divs);
+
+ /* Calculate clock * 2, so shader clock can use it too */
+ clock = calc_pll(priv, 0x4028, (core << 1), &N, &M, &P1);
+
+ if (abs(core - out) <= abs(core - (clock >> 1))) {
+ priv->csrc = nv_clk_src_hclkm4;
+ priv->cctrl = divs << 16;
+ } else {
+ /* NVCTRL is actually used _after_ NVPOST, and after what we
+ * call NVPLL. To make matters worse, NVPOST is an integer
+ * divider instead of a right-shift number. */
+ if(P1 > 2) {
+ P2 = P1 - 2;
+ P1 = 2;
+ }
+
+ priv->csrc = nv_clk_src_core;
+ priv->ccoef = (N << 8) | M;
+
+ priv->cctrl = (P2 + 1) << 16;
+ priv->cpost = (1 << P1) << 16;
+ }
+
+ /* sclk: nvpll + divisor, href or spll */
+ out = 0;
+ if (shader == clk->read(clk, nv_clk_src_href)) {
+ priv->ssrc = nv_clk_src_href;
+ } else {
+ clock = calc_pll(priv, 0x4020, shader, &N, &M, &P1);
+ if (priv->csrc == nv_clk_src_core)
+ out = calc_P((core << 1), shader, &divs);
+
+ if (abs(shader - out) <=
+ abs(shader - clock) &&
+ (divs + P2) <= 7) {
+ priv->ssrc = nv_clk_src_core;
+ priv->sctrl = (divs + P2) << 16;
+ } else {
+ priv->ssrc = nv_clk_src_shader;
+ priv->scoef = (N << 8) | M;
+ priv->sctrl = P1 << 16;
+ }
+ }
+
+ /* vclk */
+ out = calc_P(core, vdec, &divs);
+ clock = calc_P(500000, vdec, &P1);
+ if(abs(vdec - out) <= abs(vdec - clock)) {
+ priv->vsrc = nv_clk_src_cclk;
+ priv->vdiv = divs << 16;
+ } else {
+ priv->vsrc = nv_clk_src_vdec;
+ priv->vdiv = P1 << 16;
+ }
+
+ /* Print strategy! */
+ nv_debug(priv, "nvpll: %08x %08x %08x\n",
+ priv->ccoef, priv->cpost, priv->cctrl);
+ nv_debug(priv, " spll: %08x %08x %08x\n",
+ priv->scoef, priv->spost, priv->sctrl);
+ nv_debug(priv, " vdiv: %08x\n", priv->vdiv);
+ if (priv->csrc == nv_clk_src_hclkm4)
+ nv_debug(priv, "core: hrefm4\n");
+ else
+ nv_debug(priv, "core: nvpll\n");
+
+ if (priv->ssrc == nv_clk_src_hclkm4)
+ nv_debug(priv, "shader: hrefm4\n");
+ else if (priv->ssrc == nv_clk_src_core)
+ nv_debug(priv, "shader: nvpll\n");
+ else
+ nv_debug(priv, "shader: spll\n");
+
+ if (priv->vsrc == nv_clk_src_hclkm4)
+ nv_debug(priv, "vdec: 500MHz\n");
+ else
+ nv_debug(priv, "vdec: core\n");
+
+ return 0;
+}
+
+static int
+mcp77_clk_prog(struct nvkm_clk *clk)
+{
+ struct mcp77_clk_priv *priv = (void *)clk;
+ u32 pllmask = 0, mast;
+ unsigned long flags;
+ unsigned long *f = &flags;
+ int ret = 0;
+
+ ret = gt215_clk_pre(clk, f);
+ if (ret)
+ goto out;
+
+ /* First switch to safe clocks: href */
+ mast = nv_mask(clk, 0xc054, 0x03400e70, 0x03400640);
+ mast &= ~0x00400e73;
+ mast |= 0x03000000;
+
+ switch (priv->csrc) {
+ case nv_clk_src_hclkm4:
+ nv_mask(clk, 0x4028, 0x00070000, priv->cctrl);
+ mast |= 0x00000002;
+ break;
+ case nv_clk_src_core:
+ nv_wr32(clk, 0x402c, priv->ccoef);
+ nv_wr32(clk, 0x4028, 0x80000000 | priv->cctrl);
+ nv_wr32(clk, 0x4040, priv->cpost);
+ pllmask |= (0x3 << 8);
+ mast |= 0x00000003;
+ break;
+ default:
+ nv_warn(priv,"Reclocking failed: unknown core clock\n");
+ goto resume;
+ }
+
+ switch (priv->ssrc) {
+ case nv_clk_src_href:
+ nv_mask(clk, 0x4020, 0x00070000, 0x00000000);
+ /* mast |= 0x00000000; */
+ break;
+ case nv_clk_src_core:
+ nv_mask(clk, 0x4020, 0x00070000, priv->sctrl);
+ mast |= 0x00000020;
+ break;
+ case nv_clk_src_shader:
+ nv_wr32(clk, 0x4024, priv->scoef);
+ nv_wr32(clk, 0x4020, 0x80000000 | priv->sctrl);
+ nv_wr32(clk, 0x4070, priv->spost);
+ pllmask |= (0x3 << 12);
+ mast |= 0x00000030;
+ break;
+ default:
+ nv_warn(priv,"Reclocking failed: unknown sclk clock\n");
+ goto resume;
+ }
+
+ if (!nv_wait(clk, 0x004080, pllmask, pllmask)) {
+ nv_warn(priv,"Reclocking failed: unstable PLLs\n");
+ goto resume;
+ }
+
+ switch (priv->vsrc) {
+ case nv_clk_src_cclk:
+ mast |= 0x00400000;
+ default:
+ nv_wr32(clk, 0x4600, priv->vdiv);
+ }
+
+ nv_wr32(clk, 0xc054, mast);
+
+resume:
+ /* Disable some PLLs and dividers when unused */
+ if (priv->csrc != nv_clk_src_core) {
+ nv_wr32(clk, 0x4040, 0x00000000);
+ nv_mask(clk, 0x4028, 0x80000000, 0x00000000);
+ }
+
+ if (priv->ssrc != nv_clk_src_shader) {
+ nv_wr32(clk, 0x4070, 0x00000000);
+ nv_mask(clk, 0x4020, 0x80000000, 0x00000000);
+ }
+
+out:
+ if (ret == -EBUSY)
+ f = NULL;
+
+ gt215_clk_post(clk, f);
+ return ret;
+}
+
+static void
+mcp77_clk_tidy(struct nvkm_clk *clk)
+{
+}
+
+static struct nvkm_domain
+mcp77_domains[] = {
+ { nv_clk_src_crystal, 0xff },
+ { nv_clk_src_href , 0xff },
+ { nv_clk_src_core , 0xff, 0, "core", 1000 },
+ { nv_clk_src_shader , 0xff, 0, "shader", 1000 },
+ { nv_clk_src_vdec , 0xff, 0, "vdec", 1000 },
+ { nv_clk_src_max }
+};
+
+static int
+mcp77_clk_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 size,
+ struct nvkm_object **pobject)
+{
+ struct mcp77_clk_priv *priv;
+ int ret;
+
+ ret = nvkm_clk_create(parent, engine, oclass, mcp77_domains,
+ NULL, 0, true, &priv);
+ *pobject = nv_object(priv);
+ if (ret)
+ return ret;
+
+ priv->base.read = mcp77_clk_read;
+ priv->base.calc = mcp77_clk_calc;
+ priv->base.prog = mcp77_clk_prog;
+ priv->base.tidy = mcp77_clk_tidy;
+ return 0;
+}
+
+struct nvkm_oclass *
+mcp77_clk_oclass = &(struct nvkm_oclass) {
+ .handle = NV_SUBDEV(CLK, 0xaa),
+ .ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = mcp77_clk_ctor,
+ .dtor = _nvkm_clk_dtor,
+ .init = _nvkm_clk_init,
+ .fini = _nvkm_clk_fini,
+ },
+};
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/nv04.c b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/nv04.c
new file mode 100644
index 000000000000..63dbbb575228
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/nv04.c
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include <subdev/clk.h>
+#include "pll.h"
+
+#include <subdev/bios.h>
+#include <subdev/bios/pll.h>
+#include <subdev/devinit/nv04.h>
+
+struct nv04_clk_priv {
+ struct nvkm_clk base;
+};
+
+int
+nv04_clk_pll_calc(struct nvkm_clk *clock, struct nvbios_pll *info,
+ int clk, struct nvkm_pll_vals *pv)
+{
+ int N1, M1, N2, M2, P;
+ int ret = nv04_pll_calc(nv_subdev(clock), info, clk, &N1, &M1, &N2, &M2, &P);
+ if (ret) {
+ pv->refclk = info->refclk;
+ pv->N1 = N1;
+ pv->M1 = M1;
+ pv->N2 = N2;
+ pv->M2 = M2;
+ pv->log2P = P;
+ }
+ return ret;
+}
+
+int
+nv04_clk_pll_prog(struct nvkm_clk *clk, u32 reg1, struct nvkm_pll_vals *pv)
+{
+ struct nvkm_devinit *devinit = nvkm_devinit(clk);
+ int cv = nvkm_bios(clk)->version.chip;
+
+ if (cv == 0x30 || cv == 0x31 || cv == 0x35 || cv == 0x36 ||
+ cv >= 0x40) {
+ if (reg1 > 0x405c)
+ setPLL_double_highregs(devinit, reg1, pv);
+ else
+ setPLL_double_lowregs(devinit, reg1, pv);
+ } else
+ setPLL_single(devinit, reg1, pv);
+
+ return 0;
+}
+
+static struct nvkm_domain
+nv04_domain[] = {
+ { nv_clk_src_max }
+};
+
+static int
+nv04_clk_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 size,
+ struct nvkm_object **pobject)
+{
+ struct nv04_clk_priv *priv;
+ int ret;
+
+ ret = nvkm_clk_create(parent, engine, oclass, nv04_domain,
+ NULL, 0, false, &priv);
+ *pobject = nv_object(priv);
+ if (ret)
+ return ret;
+
+ priv->base.pll_calc = nv04_clk_pll_calc;
+ priv->base.pll_prog = nv04_clk_pll_prog;
+ return 0;
+}
+
+struct nvkm_oclass
+nv04_clk_oclass = {
+ .handle = NV_SUBDEV(CLK, 0x04),
+ .ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv04_clk_ctor,
+ .dtor = _nvkm_clk_dtor,
+ .init = _nvkm_clk_init,
+ .fini = _nvkm_clk_fini,
+ },
+};
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/nv40.c b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/nv40.c
new file mode 100644
index 000000000000..ed838130c89d
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/nv40.c
@@ -0,0 +1,241 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include <subdev/clk.h>
+#include "pll.h"
+
+#include <core/device.h>
+#include <subdev/bios.h>
+#include <subdev/bios/pll.h>
+
+struct nv40_clk_priv {
+ struct nvkm_clk base;
+ u32 ctrl;
+ u32 npll_ctrl;
+ u32 npll_coef;
+ u32 spll;
+};
+
+static struct nvkm_domain
+nv40_domain[] = {
+ { nv_clk_src_crystal, 0xff },
+ { nv_clk_src_href , 0xff },
+ { nv_clk_src_core , 0xff, 0, "core", 1000 },
+ { nv_clk_src_shader , 0xff, 0, "shader", 1000 },
+ { nv_clk_src_mem , 0xff, 0, "memory", 1000 },
+ { nv_clk_src_max }
+};
+
+static u32
+read_pll_1(struct nv40_clk_priv *priv, u32 reg)
+{
+ u32 ctrl = nv_rd32(priv, reg + 0x00);
+ int P = (ctrl & 0x00070000) >> 16;
+ int N = (ctrl & 0x0000ff00) >> 8;
+ int M = (ctrl & 0x000000ff) >> 0;
+ u32 ref = 27000, clk = 0;
+
+ if (ctrl & 0x80000000)
+ clk = ref * N / M;
+
+ return clk >> P;
+}
+
+static u32
+read_pll_2(struct nv40_clk_priv *priv, u32 reg)
+{
+ u32 ctrl = nv_rd32(priv, reg + 0x00);
+ u32 coef = nv_rd32(priv, reg + 0x04);
+ int N2 = (coef & 0xff000000) >> 24;
+ int M2 = (coef & 0x00ff0000) >> 16;
+ int N1 = (coef & 0x0000ff00) >> 8;
+ int M1 = (coef & 0x000000ff) >> 0;
+ int P = (ctrl & 0x00070000) >> 16;
+ u32 ref = 27000, clk = 0;
+
+ if ((ctrl & 0x80000000) && M1) {
+ clk = ref * N1 / M1;
+ if ((ctrl & 0x40000100) == 0x40000000) {
+ if (M2)
+ clk = clk * N2 / M2;
+ else
+ clk = 0;
+ }
+ }
+
+ return clk >> P;
+}
+
+static u32
+read_clk(struct nv40_clk_priv *priv, u32 src)
+{
+ switch (src) {
+ case 3:
+ return read_pll_2(priv, 0x004000);
+ case 2:
+ return read_pll_1(priv, 0x004008);
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int
+nv40_clk_read(struct nvkm_clk *clk, enum nv_clk_src src)
+{
+ struct nv40_clk_priv *priv = (void *)clk;
+ u32 mast = nv_rd32(priv, 0x00c040);
+
+ switch (src) {
+ case nv_clk_src_crystal:
+ return nv_device(priv)->crystal;
+ case nv_clk_src_href:
+ return 100000; /*XXX: PCIE/AGP differ*/
+ case nv_clk_src_core:
+ return read_clk(priv, (mast & 0x00000003) >> 0);
+ case nv_clk_src_shader:
+ return read_clk(priv, (mast & 0x00000030) >> 4);
+ case nv_clk_src_mem:
+ return read_pll_2(priv, 0x4020);
+ default:
+ break;
+ }
+
+ nv_debug(priv, "unknown clock source %d 0x%08x\n", src, mast);
+ return -EINVAL;
+}
+
+static int
+nv40_clk_calc_pll(struct nv40_clk_priv *priv, u32 reg, u32 clk,
+ int *N1, int *M1, int *N2, int *M2, int *log2P)
+{
+ struct nvkm_bios *bios = nvkm_bios(priv);
+ struct nvbios_pll pll;
+ int ret;
+
+ ret = nvbios_pll_parse(bios, reg, &pll);
+ if (ret)
+ return ret;
+
+ if (clk < pll.vco1.max_freq)
+ pll.vco2.max_freq = 0;
+
+ ret = nv04_pll_calc(nv_subdev(priv), &pll, clk, N1, M1, N2, M2, log2P);
+ if (ret == 0)
+ return -ERANGE;
+
+ return ret;
+}
+
+static int
+nv40_clk_calc(struct nvkm_clk *clk, struct nvkm_cstate *cstate)
+{
+ struct nv40_clk_priv *priv = (void *)clk;
+ int gclk = cstate->domain[nv_clk_src_core];
+ int sclk = cstate->domain[nv_clk_src_shader];
+ int N1, M1, N2, M2, log2P;
+ int ret;
+
+ /* core/geometric clock */
+ ret = nv40_clk_calc_pll(priv, 0x004000, gclk,
+ &N1, &M1, &N2, &M2, &log2P);
+ if (ret < 0)
+ return ret;
+
+ if (N2 == M2) {
+ priv->npll_ctrl = 0x80000100 | (log2P << 16);
+ priv->npll_coef = (N1 << 8) | M1;
+ } else {
+ priv->npll_ctrl = 0xc0000000 | (log2P << 16);
+ priv->npll_coef = (N2 << 24) | (M2 << 16) | (N1 << 8) | M1;
+ }
+
+ /* use the second pll for shader/rop clock, if it differs from core */
+ if (sclk && sclk != gclk) {
+ ret = nv40_clk_calc_pll(priv, 0x004008, sclk,
+ &N1, &M1, NULL, NULL, &log2P);
+ if (ret < 0)
+ return ret;
+
+ priv->spll = 0xc0000000 | (log2P << 16) | (N1 << 8) | M1;
+ priv->ctrl = 0x00000223;
+ } else {
+ priv->spll = 0x00000000;
+ priv->ctrl = 0x00000333;
+ }
+
+ return 0;
+}
+
+static int
+nv40_clk_prog(struct nvkm_clk *clk)
+{
+ struct nv40_clk_priv *priv = (void *)clk;
+ nv_mask(priv, 0x00c040, 0x00000333, 0x00000000);
+ nv_wr32(priv, 0x004004, priv->npll_coef);
+ nv_mask(priv, 0x004000, 0xc0070100, priv->npll_ctrl);
+ nv_mask(priv, 0x004008, 0xc007ffff, priv->spll);
+ mdelay(5);
+ nv_mask(priv, 0x00c040, 0x00000333, priv->ctrl);
+ return 0;
+}
+
+static void
+nv40_clk_tidy(struct nvkm_clk *clk)
+{
+}
+
+static int
+nv40_clk_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 size,
+ struct nvkm_object **pobject)
+{
+ struct nv40_clk_priv *priv;
+ int ret;
+
+ ret = nvkm_clk_create(parent, engine, oclass, nv40_domain,
+ NULL, 0, true, &priv);
+ *pobject = nv_object(priv);
+ if (ret)
+ return ret;
+
+ priv->base.pll_calc = nv04_clk_pll_calc;
+ priv->base.pll_prog = nv04_clk_pll_prog;
+ priv->base.read = nv40_clk_read;
+ priv->base.calc = nv40_clk_calc;
+ priv->base.prog = nv40_clk_prog;
+ priv->base.tidy = nv40_clk_tidy;
+ return 0;
+}
+
+struct nvkm_oclass
+nv40_clk_oclass = {
+ .handle = NV_SUBDEV(CLK, 0x40),
+ .ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv40_clk_ctor,
+ .dtor = _nvkm_clk_dtor,
+ .init = _nvkm_clk_init,
+ .fini = _nvkm_clk_fini,
+ },
+};
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/nv50.c b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/nv50.c
new file mode 100644
index 000000000000..9b4ffd6347ce
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/nv50.c
@@ -0,0 +1,561 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "nv50.h"
+#include "pll.h"
+#include "seq.h"
+
+#include <core/device.h>
+#include <subdev/bios.h>
+#include <subdev/bios/pll.h>
+
+static u32
+read_div(struct nv50_clk_priv *priv)
+{
+ switch (nv_device(priv)->chipset) {
+ case 0x50: /* it exists, but only has bit 31, not the dividers.. */
+ case 0x84:
+ case 0x86:
+ case 0x98:
+ case 0xa0:
+ return nv_rd32(priv, 0x004700);
+ case 0x92:
+ case 0x94:
+ case 0x96:
+ return nv_rd32(priv, 0x004800);
+ default:
+ return 0x00000000;
+ }
+}
+
+static u32
+read_pll_src(struct nv50_clk_priv *priv, u32 base)
+{
+ struct nvkm_clk *clk = &priv->base;
+ u32 coef, ref = clk->read(clk, nv_clk_src_crystal);
+ u32 rsel = nv_rd32(priv, 0x00e18c);
+ int P, N, M, id;
+
+ switch (nv_device(priv)->chipset) {
+ case 0x50:
+ case 0xa0:
+ switch (base) {
+ case 0x4020:
+ case 0x4028: id = !!(rsel & 0x00000004); break;
+ case 0x4008: id = !!(rsel & 0x00000008); break;
+ case 0x4030: id = 0; break;
+ default:
+ nv_error(priv, "ref: bad pll 0x%06x\n", base);
+ return 0;
+ }
+
+ coef = nv_rd32(priv, 0x00e81c + (id * 0x0c));
+ ref *= (coef & 0x01000000) ? 2 : 4;
+ P = (coef & 0x00070000) >> 16;
+ N = ((coef & 0x0000ff00) >> 8) + 1;
+ M = ((coef & 0x000000ff) >> 0) + 1;
+ break;
+ case 0x84:
+ case 0x86:
+ case 0x92:
+ coef = nv_rd32(priv, 0x00e81c);
+ P = (coef & 0x00070000) >> 16;
+ N = (coef & 0x0000ff00) >> 8;
+ M = (coef & 0x000000ff) >> 0;
+ break;
+ case 0x94:
+ case 0x96:
+ case 0x98:
+ rsel = nv_rd32(priv, 0x00c050);
+ switch (base) {
+ case 0x4020: rsel = (rsel & 0x00000003) >> 0; break;
+ case 0x4008: rsel = (rsel & 0x0000000c) >> 2; break;
+ case 0x4028: rsel = (rsel & 0x00001800) >> 11; break;
+ case 0x4030: rsel = 3; break;
+ default:
+ nv_error(priv, "ref: bad pll 0x%06x\n", base);
+ return 0;
+ }
+
+ switch (rsel) {
+ case 0: id = 1; break;
+ case 1: return clk->read(clk, nv_clk_src_crystal);
+ case 2: return clk->read(clk, nv_clk_src_href);
+ case 3: id = 0; break;
+ }
+
+ coef = nv_rd32(priv, 0x00e81c + (id * 0x28));
+ P = (nv_rd32(priv, 0x00e824 + (id * 0x28)) >> 16) & 7;
+ P += (coef & 0x00070000) >> 16;
+ N = (coef & 0x0000ff00) >> 8;
+ M = (coef & 0x000000ff) >> 0;
+ break;
+ default:
+ BUG_ON(1);
+ }
+
+ if (M)
+ return (ref * N / M) >> P;
+
+ return 0;
+}
+
+static u32
+read_pll_ref(struct nv50_clk_priv *priv, u32 base)
+{
+ struct nvkm_clk *clk = &priv->base;
+ u32 src, mast = nv_rd32(priv, 0x00c040);
+
+ switch (base) {
+ case 0x004028:
+ src = !!(mast & 0x00200000);
+ break;
+ case 0x004020:
+ src = !!(mast & 0x00400000);
+ break;
+ case 0x004008:
+ src = !!(mast & 0x00010000);
+ break;
+ case 0x004030:
+ src = !!(mast & 0x02000000);
+ break;
+ case 0x00e810:
+ return clk->read(clk, nv_clk_src_crystal);
+ default:
+ nv_error(priv, "bad pll 0x%06x\n", base);
+ return 0;
+ }
+
+ if (src)
+ return clk->read(clk, nv_clk_src_href);
+
+ return read_pll_src(priv, base);
+}
+
+static u32
+read_pll(struct nv50_clk_priv *priv, u32 base)
+{
+ struct nvkm_clk *clk = &priv->base;
+ u32 mast = nv_rd32(priv, 0x00c040);
+ u32 ctrl = nv_rd32(priv, base + 0);
+ u32 coef = nv_rd32(priv, base + 4);
+ u32 ref = read_pll_ref(priv, base);
+ u32 freq = 0;
+ int N1, N2, M1, M2;
+
+ if (base == 0x004028 && (mast & 0x00100000)) {
+ /* wtf, appears to only disable post-divider on gt200 */
+ if (nv_device(priv)->chipset != 0xa0)
+ return clk->read(clk, nv_clk_src_dom6);
+ }
+
+ N2 = (coef & 0xff000000) >> 24;
+ M2 = (coef & 0x00ff0000) >> 16;
+ N1 = (coef & 0x0000ff00) >> 8;
+ M1 = (coef & 0x000000ff);
+ if ((ctrl & 0x80000000) && M1) {
+ freq = ref * N1 / M1;
+ if ((ctrl & 0x40000100) == 0x40000000) {
+ if (M2)
+ freq = freq * N2 / M2;
+ else
+ freq = 0;
+ }
+ }
+
+ return freq;
+}
+
+static int
+nv50_clk_read(struct nvkm_clk *clk, enum nv_clk_src src)
+{
+ struct nv50_clk_priv *priv = (void *)clk;
+ u32 mast = nv_rd32(priv, 0x00c040);
+ u32 P = 0;
+
+ switch (src) {
+ case nv_clk_src_crystal:
+ return nv_device(priv)->crystal;
+ case nv_clk_src_href:
+ return 100000; /* PCIE reference clock */
+ case nv_clk_src_hclk:
+ return div_u64((u64)clk->read(clk, nv_clk_src_href) * 27778, 10000);
+ case nv_clk_src_hclkm3:
+ return clk->read(clk, nv_clk_src_hclk) * 3;
+ case nv_clk_src_hclkm3d2:
+ return clk->read(clk, nv_clk_src_hclk) * 3 / 2;
+ case nv_clk_src_host:
+ switch (mast & 0x30000000) {
+ case 0x00000000: return clk->read(clk, nv_clk_src_href);
+ case 0x10000000: break;
+ case 0x20000000: /* !0x50 */
+ case 0x30000000: return clk->read(clk, nv_clk_src_hclk);
+ }
+ break;
+ case nv_clk_src_core:
+ if (!(mast & 0x00100000))
+ P = (nv_rd32(priv, 0x004028) & 0x00070000) >> 16;
+ switch (mast & 0x00000003) {
+ case 0x00000000: return clk->read(clk, nv_clk_src_crystal) >> P;
+ case 0x00000001: return clk->read(clk, nv_clk_src_dom6);
+ case 0x00000002: return read_pll(priv, 0x004020) >> P;
+ case 0x00000003: return read_pll(priv, 0x004028) >> P;
+ }
+ break;
+ case nv_clk_src_shader:
+ P = (nv_rd32(priv, 0x004020) & 0x00070000) >> 16;
+ switch (mast & 0x00000030) {
+ case 0x00000000:
+ if (mast & 0x00000080)
+ return clk->read(clk, nv_clk_src_host) >> P;
+ return clk->read(clk, nv_clk_src_crystal) >> P;
+ case 0x00000010: break;
+ case 0x00000020: return read_pll(priv, 0x004028) >> P;
+ case 0x00000030: return read_pll(priv, 0x004020) >> P;
+ }
+ break;
+ case nv_clk_src_mem:
+ P = (nv_rd32(priv, 0x004008) & 0x00070000) >> 16;
+ if (nv_rd32(priv, 0x004008) & 0x00000200) {
+ switch (mast & 0x0000c000) {
+ case 0x00000000:
+ return clk->read(clk, nv_clk_src_crystal) >> P;
+ case 0x00008000:
+ case 0x0000c000:
+ return clk->read(clk, nv_clk_src_href) >> P;
+ }
+ } else {
+ return read_pll(priv, 0x004008) >> P;
+ }
+ break;
+ case nv_clk_src_vdec:
+ P = (read_div(priv) & 0x00000700) >> 8;
+ switch (nv_device(priv)->chipset) {
+ case 0x84:
+ case 0x86:
+ case 0x92:
+ case 0x94:
+ case 0x96:
+ case 0xa0:
+ switch (mast & 0x00000c00) {
+ case 0x00000000:
+ if (nv_device(priv)->chipset == 0xa0) /* wtf?? */
+ return clk->read(clk, nv_clk_src_core) >> P;
+ return clk->read(clk, nv_clk_src_crystal) >> P;
+ case 0x00000400:
+ return 0;
+ case 0x00000800:
+ if (mast & 0x01000000)
+ return read_pll(priv, 0x004028) >> P;
+ return read_pll(priv, 0x004030) >> P;
+ case 0x00000c00:
+ return clk->read(clk, nv_clk_src_core) >> P;
+ }
+ break;
+ case 0x98:
+ switch (mast & 0x00000c00) {
+ case 0x00000000:
+ return clk->read(clk, nv_clk_src_core) >> P;
+ case 0x00000400:
+ return 0;
+ case 0x00000800:
+ return clk->read(clk, nv_clk_src_hclkm3d2) >> P;
+ case 0x00000c00:
+ return clk->read(clk, nv_clk_src_mem) >> P;
+ }
+ break;
+ }
+ break;
+ case nv_clk_src_dom6:
+ switch (nv_device(priv)->chipset) {
+ case 0x50:
+ case 0xa0:
+ return read_pll(priv, 0x00e810) >> 2;
+ case 0x84:
+ case 0x86:
+ case 0x92:
+ case 0x94:
+ case 0x96:
+ case 0x98:
+ P = (read_div(priv) & 0x00000007) >> 0;
+ switch (mast & 0x0c000000) {
+ case 0x00000000: return clk->read(clk, nv_clk_src_href);
+ case 0x04000000: break;
+ case 0x08000000: return clk->read(clk, nv_clk_src_hclk);
+ case 0x0c000000:
+ return clk->read(clk, nv_clk_src_hclkm3) >> P;
+ }
+ break;
+ default:
+ break;
+ }
+ default:
+ break;
+ }
+
+ nv_debug(priv, "unknown clock source %d 0x%08x\n", src, mast);
+ return -EINVAL;
+}
+
+static u32
+calc_pll(struct nv50_clk_priv *priv, u32 reg, u32 clk, int *N, int *M, int *P)
+{
+ struct nvkm_bios *bios = nvkm_bios(priv);
+ struct nvbios_pll pll;
+ int ret;
+
+ ret = nvbios_pll_parse(bios, reg, &pll);
+ if (ret)
+ return 0;
+
+ pll.vco2.max_freq = 0;
+ pll.refclk = read_pll_ref(priv, reg);
+ if (!pll.refclk)
+ return 0;
+
+ return nv04_pll_calc(nv_subdev(priv), &pll, clk, N, M, NULL, NULL, P);
+}
+
+static inline u32
+calc_div(u32 src, u32 target, int *div)
+{
+ u32 clk0 = src, clk1 = src;
+ for (*div = 0; *div <= 7; (*div)++) {
+ if (clk0 <= target) {
+ clk1 = clk0 << (*div ? 1 : 0);
+ break;
+ }
+ clk0 >>= 1;
+ }
+
+ if (target - clk0 <= clk1 - target)
+ return clk0;
+ (*div)--;
+ return clk1;
+}
+
+static inline u32
+clk_same(u32 a, u32 b)
+{
+ return ((a / 1000) == (b / 1000));
+}
+
+static int
+nv50_clk_calc(struct nvkm_clk *clk, struct nvkm_cstate *cstate)
+{
+ struct nv50_clk_priv *priv = (void *)clk;
+ struct nv50_clk_hwsq *hwsq = &priv->hwsq;
+ const int shader = cstate->domain[nv_clk_src_shader];
+ const int core = cstate->domain[nv_clk_src_core];
+ const int vdec = cstate->domain[nv_clk_src_vdec];
+ const int dom6 = cstate->domain[nv_clk_src_dom6];
+ u32 mastm = 0, mastv = 0;
+ u32 divsm = 0, divsv = 0;
+ int N, M, P1, P2;
+ int freq, out;
+
+ /* prepare a hwsq script from which we'll perform the reclock */
+ out = clk_init(hwsq, nv_subdev(clk));
+ if (out)
+ return out;
+
+ clk_wr32(hwsq, fifo, 0x00000001); /* block fifo */
+ clk_nsec(hwsq, 8000);
+ clk_setf(hwsq, 0x10, 0x00); /* disable fb */
+ clk_wait(hwsq, 0x00, 0x01); /* wait for fb disabled */
+
+ /* vdec: avoid modifying xpll until we know exactly how the other
+ * clock domains work, i suspect at least some of them can also be
+ * tied to xpll...
+ */
+ if (vdec) {
+ /* see how close we can get using nvclk as a source */
+ freq = calc_div(core, vdec, &P1);
+
+ /* see how close we can get using xpll/hclk as a source */
+ if (nv_device(priv)->chipset != 0x98)
+ out = read_pll(priv, 0x004030);
+ else
+ out = clk->read(clk, nv_clk_src_hclkm3d2);
+ out = calc_div(out, vdec, &P2);
+
+ /* select whichever gets us closest */
+ if (abs(vdec - freq) <= abs(vdec - out)) {
+ if (nv_device(priv)->chipset != 0x98)
+ mastv |= 0x00000c00;
+ divsv |= P1 << 8;
+ } else {
+ mastv |= 0x00000800;
+ divsv |= P2 << 8;
+ }
+
+ mastm |= 0x00000c00;
+ divsm |= 0x00000700;
+ }
+
+ /* dom6: nfi what this is, but we're limited to various combinations
+ * of the host clock frequency
+ */
+ if (dom6) {
+ if (clk_same(dom6, clk->read(clk, nv_clk_src_href))) {
+ mastv |= 0x00000000;
+ } else
+ if (clk_same(dom6, clk->read(clk, nv_clk_src_hclk))) {
+ mastv |= 0x08000000;
+ } else {
+ freq = clk->read(clk, nv_clk_src_hclk) * 3;
+ freq = calc_div(freq, dom6, &P1);
+
+ mastv |= 0x0c000000;
+ divsv |= P1;
+ }
+
+ mastm |= 0x0c000000;
+ divsm |= 0x00000007;
+ }
+
+ /* vdec/dom6: switch to "safe" clocks temporarily, update dividers
+ * and then switch to target clocks
+ */
+ clk_mask(hwsq, mast, mastm, 0x00000000);
+ clk_mask(hwsq, divs, divsm, divsv);
+ clk_mask(hwsq, mast, mastm, mastv);
+
+ /* core/shader: disconnect nvclk/sclk from their PLLs (nvclk to dom6,
+ * sclk to hclk) before reprogramming
+ */
+ if (nv_device(priv)->chipset < 0x92)
+ clk_mask(hwsq, mast, 0x001000b0, 0x00100080);
+ else
+ clk_mask(hwsq, mast, 0x000000b3, 0x00000081);
+
+ /* core: for the moment at least, always use nvpll */
+ freq = calc_pll(priv, 0x4028, core, &N, &M, &P1);
+ if (freq == 0)
+ return -ERANGE;
+
+ clk_mask(hwsq, nvpll[0], 0xc03f0100,
+ 0x80000000 | (P1 << 19) | (P1 << 16));
+ clk_mask(hwsq, nvpll[1], 0x0000ffff, (N << 8) | M);
+
+ /* shader: tie to nvclk if possible, otherwise use spll. have to be
+ * very careful that the shader clock is at least twice the core, or
+ * some chipsets will be very unhappy. i expect most or all of these
+ * cases will be handled by tying to nvclk, but it's possible there's
+ * corners
+ */
+ if (P1-- && shader == (core << 1)) {
+ clk_mask(hwsq, spll[0], 0xc03f0100, (P1 << 19) | (P1 << 16));
+ clk_mask(hwsq, mast, 0x00100033, 0x00000023);
+ } else {
+ freq = calc_pll(priv, 0x4020, shader, &N, &M, &P1);
+ if (freq == 0)
+ return -ERANGE;
+
+ clk_mask(hwsq, spll[0], 0xc03f0100,
+ 0x80000000 | (P1 << 19) | (P1 << 16));
+ clk_mask(hwsq, spll[1], 0x0000ffff, (N << 8) | M);
+ clk_mask(hwsq, mast, 0x00100033, 0x00000033);
+ }
+
+ /* restore normal operation */
+ clk_setf(hwsq, 0x10, 0x01); /* enable fb */
+ clk_wait(hwsq, 0x00, 0x00); /* wait for fb enabled */
+ clk_wr32(hwsq, fifo, 0x00000000); /* un-block fifo */
+ return 0;
+}
+
+static int
+nv50_clk_prog(struct nvkm_clk *clk)
+{
+ struct nv50_clk_priv *priv = (void *)clk;
+ return clk_exec(&priv->hwsq, true);
+}
+
+static void
+nv50_clk_tidy(struct nvkm_clk *clk)
+{
+ struct nv50_clk_priv *priv = (void *)clk;
+ clk_exec(&priv->hwsq, false);
+}
+
+int
+nv50_clk_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 size,
+ struct nvkm_object **pobject)
+{
+ struct nv50_clk_oclass *pclass = (void *)oclass;
+ struct nv50_clk_priv *priv;
+ int ret;
+
+ ret = nvkm_clk_create(parent, engine, oclass, pclass->domains,
+ NULL, 0, false, &priv);
+ *pobject = nv_object(priv);
+ if (ret)
+ return ret;
+
+ priv->hwsq.r_fifo = hwsq_reg(0x002504);
+ priv->hwsq.r_spll[0] = hwsq_reg(0x004020);
+ priv->hwsq.r_spll[1] = hwsq_reg(0x004024);
+ priv->hwsq.r_nvpll[0] = hwsq_reg(0x004028);
+ priv->hwsq.r_nvpll[1] = hwsq_reg(0x00402c);
+ switch (nv_device(priv)->chipset) {
+ case 0x92:
+ case 0x94:
+ case 0x96:
+ priv->hwsq.r_divs = hwsq_reg(0x004800);
+ break;
+ default:
+ priv->hwsq.r_divs = hwsq_reg(0x004700);
+ break;
+ }
+ priv->hwsq.r_mast = hwsq_reg(0x00c040);
+
+ priv->base.read = nv50_clk_read;
+ priv->base.calc = nv50_clk_calc;
+ priv->base.prog = nv50_clk_prog;
+ priv->base.tidy = nv50_clk_tidy;
+ return 0;
+}
+
+static struct nvkm_domain
+nv50_domains[] = {
+ { nv_clk_src_crystal, 0xff },
+ { nv_clk_src_href , 0xff },
+ { nv_clk_src_core , 0xff, 0, "core", 1000 },
+ { nv_clk_src_shader , 0xff, 0, "shader", 1000 },
+ { nv_clk_src_mem , 0xff, 0, "memory", 1000 },
+ { nv_clk_src_max }
+};
+
+struct nvkm_oclass *
+nv50_clk_oclass = &(struct nv50_clk_oclass) {
+ .base.handle = NV_SUBDEV(CLK, 0x50),
+ .base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv50_clk_ctor,
+ .dtor = _nvkm_clk_dtor,
+ .init = _nvkm_clk_init,
+ .fini = _nvkm_clk_fini,
+ },
+ .domains = nv50_domains,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/nv50.h b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/nv50.h
new file mode 100644
index 000000000000..0ead76a32f10
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/nv50.h
@@ -0,0 +1,28 @@
+#ifndef __NVKM_CLK_NV50_H__
+#define __NVKM_CLK_NV50_H__
+#include <subdev/bus/hwsq.h>
+#include <subdev/clk.h>
+
+struct nv50_clk_hwsq {
+ struct hwsq base;
+ struct hwsq_reg r_fifo;
+ struct hwsq_reg r_spll[2];
+ struct hwsq_reg r_nvpll[2];
+ struct hwsq_reg r_divs;
+ struct hwsq_reg r_mast;
+};
+
+struct nv50_clk_priv {
+ struct nvkm_clk base;
+ struct nv50_clk_hwsq hwsq;
+};
+
+int nv50_clk_ctor(struct nvkm_object *, struct nvkm_object *,
+ struct nvkm_oclass *, void *, u32,
+ struct nvkm_object **);
+
+struct nv50_clk_oclass {
+ struct nvkm_oclass base;
+ struct nvkm_domain *domains;
+};
+#endif
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/pll.h b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/pll.h
new file mode 100644
index 000000000000..44020a30dee8
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/pll.h
@@ -0,0 +1,11 @@
+#ifndef __NVKM_PLL_H__
+#define __NVKM_PLL_H__
+#include <core/os.h>
+struct nvkm_subdev;
+struct nvbios_pll;
+
+int nv04_pll_calc(struct nvkm_subdev *, struct nvbios_pll *, u32 freq,
+ int *N1, int *M1, int *N2, int *M2, int *P);
+int gt215_pll_calc(struct nvkm_subdev *, struct nvbios_pll *, u32 freq,
+ int *N, int *fN, int *M, int *P);
+#endif
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/pllgt215.c b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/pllgt215.c
new file mode 100644
index 000000000000..783a3e78d632
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/pllgt215.c
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2010 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "pll.h"
+
+#include <subdev/bios.h>
+#include <subdev/bios/pll.h>
+
+int
+gt215_pll_calc(struct nvkm_subdev *subdev, struct nvbios_pll *info,
+ u32 freq, int *pN, int *pfN, int *pM, int *P)
+{
+ u32 best_err = ~0, err;
+ int M, lM, hM, N, fN;
+
+ *P = info->vco1.max_freq / freq;
+ if (*P > info->max_p)
+ *P = info->max_p;
+ if (*P < info->min_p)
+ *P = info->min_p;
+
+ lM = (info->refclk + info->vco1.max_inputfreq) / info->vco1.max_inputfreq;
+ lM = max(lM, (int)info->vco1.min_m);
+ hM = (info->refclk + info->vco1.min_inputfreq) / info->vco1.min_inputfreq;
+ hM = min(hM, (int)info->vco1.max_m);
+ lM = min(lM, hM);
+
+ for (M = lM; M <= hM; M++) {
+ u32 tmp = freq * *P * M;
+ N = tmp / info->refclk;
+ fN = tmp % info->refclk;
+
+ if (!pfN) {
+ if (fN >= info->refclk / 2)
+ N++;
+ } else {
+ if (fN < info->refclk / 2)
+ N--;
+ fN = tmp - (N * info->refclk);
+ }
+
+ if (N < info->vco1.min_n)
+ continue;
+ if (N > info->vco1.max_n)
+ break;
+
+ err = abs(freq - (info->refclk * N / M / *P));
+ if (err < best_err) {
+ best_err = err;
+ *pN = N;
+ *pM = M;
+ }
+
+ if (pfN) {
+ *pfN = ((fN << 13) + info->refclk / 2) / info->refclk;
+ *pfN = (*pfN - 4096) & 0xffff;
+ return freq;
+ }
+ }
+
+ if (unlikely(best_err == ~0)) {
+ nv_error(subdev, "unable to find matching pll values\n");
+ return -EINVAL;
+ }
+
+ return info->refclk * *pN / *pM / *P;
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/pllnv04.c b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/pllnv04.c
new file mode 100644
index 000000000000..f2292895a1a8
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/pllnv04.c
@@ -0,0 +1,245 @@
+/*
+ * Copyright 1993-2003 NVIDIA, Corporation
+ * Copyright 2007-2009 Stuart Bennett
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
+ * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include "pll.h"
+
+#include <subdev/bios.h>
+#include <subdev/bios/pll.h>
+
+static int
+getMNP_single(struct nvkm_subdev *subdev, struct nvbios_pll *info, int clk,
+ int *pN, int *pM, int *pP)
+{
+ /* Find M, N and P for a single stage PLL
+ *
+ * Note that some bioses (NV3x) have lookup tables of precomputed MNP
+ * values, but we're too lazy to use those atm
+ *
+ * "clk" parameter in kHz
+ * returns calculated clock
+ */
+ struct nvkm_bios *bios = nvkm_bios(subdev);
+ int minvco = info->vco1.min_freq, maxvco = info->vco1.max_freq;
+ int minM = info->vco1.min_m, maxM = info->vco1.max_m;
+ int minN = info->vco1.min_n, maxN = info->vco1.max_n;
+ int minU = info->vco1.min_inputfreq;
+ int maxU = info->vco1.max_inputfreq;
+ int minP = info->min_p;
+ int maxP = info->max_p_usable;
+ int crystal = info->refclk;
+ int M, N, thisP, P;
+ int clkP, calcclk;
+ int delta, bestdelta = INT_MAX;
+ int bestclk = 0;
+
+ /* this division verified for nv20, nv18, nv28 (Haiku), and nv34 */
+ /* possibly correlated with introduction of 27MHz crystal */
+ if (bios->version.major < 0x60) {
+ int cv = bios->version.chip;
+ if (cv < 0x17 || cv == 0x1a || cv == 0x20) {
+ if (clk > 250000)
+ maxM = 6;
+ if (clk > 340000)
+ maxM = 2;
+ } else if (cv < 0x40) {
+ if (clk > 150000)
+ maxM = 6;
+ if (clk > 200000)
+ maxM = 4;
+ if (clk > 340000)
+ maxM = 2;
+ }
+ }
+
+ P = 1 << maxP;
+ if ((clk * P) < minvco) {
+ minvco = clk * maxP;
+ maxvco = minvco * 2;
+ }
+
+ if (clk + clk/200 > maxvco) /* +0.5% */
+ maxvco = clk + clk/200;
+
+ /* NV34 goes maxlog2P->0, NV20 goes 0->maxlog2P */
+ for (thisP = minP; thisP <= maxP; thisP++) {
+ P = 1 << thisP;
+ clkP = clk * P;
+
+ if (clkP < minvco)
+ continue;
+ if (clkP > maxvco)
+ return bestclk;
+
+ for (M = minM; M <= maxM; M++) {
+ if (crystal/M < minU)
+ return bestclk;
+ if (crystal/M > maxU)
+ continue;
+
+ /* add crystal/2 to round better */
+ N = (clkP * M + crystal/2) / crystal;
+
+ if (N < minN)
+ continue;
+ if (N > maxN)
+ break;
+
+ /* more rounding additions */
+ calcclk = ((N * crystal + P/2) / P + M/2) / M;
+ delta = abs(calcclk - clk);
+ /* we do an exhaustive search rather than terminating
+ * on an optimality condition...
+ */
+ if (delta < bestdelta) {
+ bestdelta = delta;
+ bestclk = calcclk;
+ *pN = N;
+ *pM = M;
+ *pP = thisP;
+ if (delta == 0) /* except this one */
+ return bestclk;
+ }
+ }
+ }
+
+ return bestclk;
+}
+
+static int
+getMNP_double(struct nvkm_subdev *subdev, struct nvbios_pll *info, int clk,
+ int *pN1, int *pM1, int *pN2, int *pM2, int *pP)
+{
+ /* Find M, N and P for a two stage PLL
+ *
+ * Note that some bioses (NV30+) have lookup tables of precomputed MNP
+ * values, but we're too lazy to use those atm
+ *
+ * "clk" parameter in kHz
+ * returns calculated clock
+ */
+ int chip_version = nvkm_bios(subdev)->version.chip;
+ int minvco1 = info->vco1.min_freq, maxvco1 = info->vco1.max_freq;
+ int minvco2 = info->vco2.min_freq, maxvco2 = info->vco2.max_freq;
+ int minU1 = info->vco1.min_inputfreq, minU2 = info->vco2.min_inputfreq;
+ int maxU1 = info->vco1.max_inputfreq, maxU2 = info->vco2.max_inputfreq;
+ int minM1 = info->vco1.min_m, maxM1 = info->vco1.max_m;
+ int minN1 = info->vco1.min_n, maxN1 = info->vco1.max_n;
+ int minM2 = info->vco2.min_m, maxM2 = info->vco2.max_m;
+ int minN2 = info->vco2.min_n, maxN2 = info->vco2.max_n;
+ int maxlog2P = info->max_p_usable;
+ int crystal = info->refclk;
+ bool fixedgain2 = (minM2 == maxM2 && minN2 == maxN2);
+ int M1, N1, M2, N2, log2P;
+ int clkP, calcclk1, calcclk2, calcclkout;
+ int delta, bestdelta = INT_MAX;
+ int bestclk = 0;
+
+ int vco2 = (maxvco2 - maxvco2/200) / 2;
+ for (log2P = 0; clk && log2P < maxlog2P && clk <= (vco2 >> log2P); log2P++)
+ ;
+ clkP = clk << log2P;
+
+ if (maxvco2 < clk + clk/200) /* +0.5% */
+ maxvco2 = clk + clk/200;
+
+ for (M1 = minM1; M1 <= maxM1; M1++) {
+ if (crystal/M1 < minU1)
+ return bestclk;
+ if (crystal/M1 > maxU1)
+ continue;
+
+ for (N1 = minN1; N1 <= maxN1; N1++) {
+ calcclk1 = crystal * N1 / M1;
+ if (calcclk1 < minvco1)
+ continue;
+ if (calcclk1 > maxvco1)
+ break;
+
+ for (M2 = minM2; M2 <= maxM2; M2++) {
+ if (calcclk1/M2 < minU2)
+ break;
+ if (calcclk1/M2 > maxU2)
+ continue;
+
+ /* add calcclk1/2 to round better */
+ N2 = (clkP * M2 + calcclk1/2) / calcclk1;
+ if (N2 < minN2)
+ continue;
+ if (N2 > maxN2)
+ break;
+
+ if (!fixedgain2) {
+ if (chip_version < 0x60)
+ if (N2/M2 < 4 || N2/M2 > 10)
+ continue;
+
+ calcclk2 = calcclk1 * N2 / M2;
+ if (calcclk2 < minvco2)
+ break;
+ if (calcclk2 > maxvco2)
+ continue;
+ } else
+ calcclk2 = calcclk1;
+
+ calcclkout = calcclk2 >> log2P;
+ delta = abs(calcclkout - clk);
+ /* we do an exhaustive search rather than terminating
+ * on an optimality condition...
+ */
+ if (delta < bestdelta) {
+ bestdelta = delta;
+ bestclk = calcclkout;
+ *pN1 = N1;
+ *pM1 = M1;
+ *pN2 = N2;
+ *pM2 = M2;
+ *pP = log2P;
+ if (delta == 0) /* except this one */
+ return bestclk;
+ }
+ }
+ }
+ }
+
+ return bestclk;
+}
+
+int
+nv04_pll_calc(struct nvkm_subdev *subdev, struct nvbios_pll *info, u32 freq,
+ int *N1, int *M1, int *N2, int *M2, int *P)
+{
+ int ret;
+
+ if (!info->vco2.max_freq || !N2) {
+ ret = getMNP_single(subdev, info, freq, N1, M1, P);
+ if (N2) {
+ *N2 = 1;
+ *M2 = 1;
+ }
+ } else {
+ ret = getMNP_double(subdev, info, freq, N1, M1, N2, M2, P);
+ }
+
+ if (!ret)
+ nv_error(subdev, "unable to compute acceptable pll values\n");
+ return ret;
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/seq.h b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/seq.h
new file mode 100644
index 000000000000..d717e8b8f679
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/seq.h
@@ -0,0 +1,14 @@
+#ifndef __NVKM_CLK_SEQ_H__
+#define __NVKM_CLK_SEQ_H__
+#include <subdev/bus/hwsq.h>
+
+#define clk_init(s,p) hwsq_init(&(s)->base, (p))
+#define clk_exec(s,e) hwsq_exec(&(s)->base, (e))
+#define clk_have(s,r) ((s)->r_##r.addr != 0x000000)
+#define clk_rd32(s,r) hwsq_rd32(&(s)->base, &(s)->r_##r)
+#define clk_wr32(s,r,d) hwsq_wr32(&(s)->base, &(s)->r_##r, (d))
+#define clk_mask(s,r,m,d) hwsq_mask(&(s)->base, &(s)->r_##r, (m), (d))
+#define clk_setf(s,f,d) hwsq_setf(&(s)->base, (f), (d))
+#define clk_wait(s,f,d) hwsq_wait(&(s)->base, (f), (d))
+#define clk_nsec(s,n) hwsq_nsec(&(s)->base, (n))
+#endif
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/Kbuild b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/Kbuild
new file mode 100644
index 000000000000..793e73d16dac
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/Kbuild
@@ -0,0 +1,14 @@
+nvkm-y += nvkm/subdev/devinit/base.o
+nvkm-y += nvkm/subdev/devinit/nv04.o
+nvkm-y += nvkm/subdev/devinit/nv05.o
+nvkm-y += nvkm/subdev/devinit/nv10.o
+nvkm-y += nvkm/subdev/devinit/nv1a.o
+nvkm-y += nvkm/subdev/devinit/nv20.o
+nvkm-y += nvkm/subdev/devinit/nv50.o
+nvkm-y += nvkm/subdev/devinit/g84.o
+nvkm-y += nvkm/subdev/devinit/g98.o
+nvkm-y += nvkm/subdev/devinit/gt215.o
+nvkm-y += nvkm/subdev/devinit/mcp89.o
+nvkm-y += nvkm/subdev/devinit/gf100.o
+nvkm-y += nvkm/subdev/devinit/gm107.o
+nvkm-y += nvkm/subdev/devinit/gm204.o
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/base.c
new file mode 100644
index 000000000000..b0d7c5f40db1
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/base.c
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "priv.h"
+
+#include <core/device.h>
+#include <core/option.h>
+#include <subdev/vga.h>
+
+int
+_nvkm_devinit_fini(struct nvkm_object *object, bool suspend)
+{
+ struct nvkm_devinit *devinit = (void *)object;
+
+ /* force full reinit on resume */
+ if (suspend)
+ devinit->post = true;
+
+ /* unlock the extended vga crtc regs */
+ nv_lockvgac(devinit, false);
+
+ return nvkm_subdev_fini(&devinit->base, suspend);
+}
+
+int
+_nvkm_devinit_init(struct nvkm_object *object)
+{
+ struct nvkm_devinit_impl *impl = (void *)object->oclass;
+ struct nvkm_devinit *devinit = (void *)object;
+ int ret;
+
+ ret = nvkm_subdev_init(&devinit->base);
+ if (ret)
+ return ret;
+
+ ret = impl->post(&devinit->base, devinit->post);
+ if (ret)
+ return ret;
+
+ if (impl->disable)
+ nv_device(devinit)->disable_mask |= impl->disable(devinit);
+ return 0;
+}
+
+void
+_nvkm_devinit_dtor(struct nvkm_object *object)
+{
+ struct nvkm_devinit *devinit = (void *)object;
+
+ /* lock crtc regs */
+ nv_lockvgac(devinit, true);
+
+ nvkm_subdev_destroy(&devinit->base);
+}
+
+int
+nvkm_devinit_create_(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, int size, void **pobject)
+{
+ struct nvkm_devinit_impl *impl = (void *)oclass;
+ struct nvkm_device *device = nv_device(parent);
+ struct nvkm_devinit *devinit;
+ int ret;
+
+ ret = nvkm_subdev_create_(parent, engine, oclass, 0, "DEVINIT",
+ "init", size, pobject);
+ devinit = *pobject;
+ if (ret)
+ return ret;
+
+ devinit->post = nvkm_boolopt(device->cfgopt, "NvForcePost", false);
+ devinit->meminit = impl->meminit;
+ devinit->pll_set = impl->pll_set;
+ devinit->mmio = impl->mmio;
+ return 0;
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/fbmem.h b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/fbmem.h
new file mode 100644
index 000000000000..36684c3f9e9c
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/fbmem.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2010 Francisco Jerez.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#include <core/device.h>
+#include <subdev/fb/regsnv04.h>
+
+#define NV04_PFB_DEBUG_0 0x00100080
+# define NV04_PFB_DEBUG_0_PAGE_MODE 0x00000001
+# define NV04_PFB_DEBUG_0_REFRESH_OFF 0x00000010
+# define NV04_PFB_DEBUG_0_REFRESH_COUNTX64 0x00003f00
+# define NV04_PFB_DEBUG_0_REFRESH_SLOW_CLK 0x00004000
+# define NV04_PFB_DEBUG_0_SAFE_MODE 0x00008000
+# define NV04_PFB_DEBUG_0_ALOM_ENABLE 0x00010000
+# define NV04_PFB_DEBUG_0_CASOE 0x00100000
+# define NV04_PFB_DEBUG_0_CKE_INVERT 0x10000000
+# define NV04_PFB_DEBUG_0_REFINC 0x20000000
+# define NV04_PFB_DEBUG_0_SAVE_POWER_OFF 0x40000000
+#define NV04_PFB_CFG0 0x00100200
+# define NV04_PFB_CFG0_SCRAMBLE 0x20000000
+#define NV04_PFB_CFG1 0x00100204
+#define NV04_PFB_SCRAMBLE(i) (0x00100400 + 4 * (i))
+
+#define NV10_PFB_REFCTRL 0x00100210
+# define NV10_PFB_REFCTRL_VALID_1 (1 << 31)
+
+static inline struct io_mapping *
+fbmem_init(struct nvkm_device *dev)
+{
+ return io_mapping_create_wc(nv_device_resource_start(dev, 1),
+ nv_device_resource_len(dev, 1));
+}
+
+static inline void
+fbmem_fini(struct io_mapping *fb)
+{
+ io_mapping_free(fb);
+}
+
+static inline u32
+fbmem_peek(struct io_mapping *fb, u32 off)
+{
+ u8 __iomem *p = io_mapping_map_atomic_wc(fb, off & PAGE_MASK);
+ u32 val = ioread32(p + (off & ~PAGE_MASK));
+ io_mapping_unmap_atomic(p);
+ return val;
+}
+
+static inline void
+fbmem_poke(struct io_mapping *fb, u32 off, u32 val)
+{
+ u8 __iomem *p = io_mapping_map_atomic_wc(fb, off & PAGE_MASK);
+ iowrite32(val, p + (off & ~PAGE_MASK));
+ wmb();
+ io_mapping_unmap_atomic(p);
+}
+
+static inline bool
+fbmem_readback(struct io_mapping *fb, u32 off, u32 val)
+{
+ fbmem_poke(fb, off, val);
+ return val == fbmem_peek(fb, off);
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/g84.c b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/g84.c
new file mode 100644
index 000000000000..ca776ce75f4f
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/g84.c
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "nv50.h"
+
+#include <subdev/bios.h>
+#include <subdev/bios/init.h>
+
+static u64
+g84_devinit_disable(struct nvkm_devinit *devinit)
+{
+ struct nv50_devinit_priv *priv = (void *)devinit;
+ u32 r001540 = nv_rd32(priv, 0x001540);
+ u32 r00154c = nv_rd32(priv, 0x00154c);
+ u64 disable = 0ULL;
+
+ if (!(r001540 & 0x40000000)) {
+ disable |= (1ULL << NVDEV_ENGINE_MPEG);
+ disable |= (1ULL << NVDEV_ENGINE_VP);
+ disable |= (1ULL << NVDEV_ENGINE_BSP);
+ disable |= (1ULL << NVDEV_ENGINE_CIPHER);
+ }
+
+ if (!(r00154c & 0x00000004))
+ disable |= (1ULL << NVDEV_ENGINE_DISP);
+ if (!(r00154c & 0x00000020))
+ disable |= (1ULL << NVDEV_ENGINE_BSP);
+ if (!(r00154c & 0x00000040))
+ disable |= (1ULL << NVDEV_ENGINE_CIPHER);
+
+ return disable;
+}
+
+struct nvkm_oclass *
+g84_devinit_oclass = &(struct nvkm_devinit_impl) {
+ .base.handle = NV_SUBDEV(DEVINIT, 0x84),
+ .base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv50_devinit_ctor,
+ .dtor = _nvkm_devinit_dtor,
+ .init = nv50_devinit_init,
+ .fini = _nvkm_devinit_fini,
+ },
+ .pll_set = nv50_devinit_pll_set,
+ .disable = g84_devinit_disable,
+ .post = nvbios_init,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/g98.c b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/g98.c
new file mode 100644
index 000000000000..d29bacee65ee
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/g98.c
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "nv50.h"
+
+#include <subdev/bios.h>
+#include <subdev/bios/init.h>
+
+static u64
+g98_devinit_disable(struct nvkm_devinit *devinit)
+{
+ struct nv50_devinit_priv *priv = (void *)devinit;
+ u32 r001540 = nv_rd32(priv, 0x001540);
+ u32 r00154c = nv_rd32(priv, 0x00154c);
+ u64 disable = 0ULL;
+
+ if (!(r001540 & 0x40000000)) {
+ disable |= (1ULL << NVDEV_ENGINE_MSPDEC);
+ disable |= (1ULL << NVDEV_ENGINE_MSVLD);
+ disable |= (1ULL << NVDEV_ENGINE_MSPPP);
+ }
+
+ if (!(r00154c & 0x00000004))
+ disable |= (1ULL << NVDEV_ENGINE_DISP);
+ if (!(r00154c & 0x00000020))
+ disable |= (1ULL << NVDEV_ENGINE_MSVLD);
+ if (!(r00154c & 0x00000040))
+ disable |= (1ULL << NVDEV_ENGINE_SEC);
+
+ return disable;
+}
+
+struct nvkm_oclass *
+g98_devinit_oclass = &(struct nvkm_devinit_impl) {
+ .base.handle = NV_SUBDEV(DEVINIT, 0x98),
+ .base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv50_devinit_ctor,
+ .dtor = _nvkm_devinit_dtor,
+ .init = nv50_devinit_init,
+ .fini = _nvkm_devinit_fini,
+ },
+ .pll_set = nv50_devinit_pll_set,
+ .disable = g98_devinit_disable,
+ .post = nvbios_init,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gf100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gf100.c
new file mode 100644
index 000000000000..e8778c67578e
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gf100.c
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "nv50.h"
+
+#include <subdev/bios.h>
+#include <subdev/bios/init.h>
+#include <subdev/bios/pll.h>
+#include <subdev/clk/pll.h>
+
+int
+gf100_devinit_pll_set(struct nvkm_devinit *devinit, u32 type, u32 freq)
+{
+ struct nv50_devinit_priv *priv = (void *)devinit;
+ struct nvkm_bios *bios = nvkm_bios(priv);
+ struct nvbios_pll info;
+ int N, fN, M, P;
+ int ret;
+
+ ret = nvbios_pll_parse(bios, type, &info);
+ if (ret)
+ return ret;
+
+ ret = gt215_pll_calc(nv_subdev(devinit), &info, freq, &N, &fN, &M, &P);
+ if (ret < 0)
+ return ret;
+
+ switch (info.type) {
+ case PLL_VPLL0:
+ case PLL_VPLL1:
+ case PLL_VPLL2:
+ case PLL_VPLL3:
+ nv_mask(priv, info.reg + 0x0c, 0x00000000, 0x00000100);
+ nv_wr32(priv, info.reg + 0x04, (P << 16) | (N << 8) | M);
+ nv_wr32(priv, info.reg + 0x10, fN << 16);
+ break;
+ default:
+ nv_warn(priv, "0x%08x/%dKhz unimplemented\n", type, freq);
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static u64
+gf100_devinit_disable(struct nvkm_devinit *devinit)
+{
+ struct nv50_devinit_priv *priv = (void *)devinit;
+ u32 r022500 = nv_rd32(priv, 0x022500);
+ u64 disable = 0ULL;
+
+ if (r022500 & 0x00000001)
+ disable |= (1ULL << NVDEV_ENGINE_DISP);
+
+ if (r022500 & 0x00000002) {
+ disable |= (1ULL << NVDEV_ENGINE_MSPDEC);
+ disable |= (1ULL << NVDEV_ENGINE_MSPPP);
+ }
+
+ if (r022500 & 0x00000004)
+ disable |= (1ULL << NVDEV_ENGINE_MSVLD);
+ if (r022500 & 0x00000008)
+ disable |= (1ULL << NVDEV_ENGINE_MSENC);
+ if (r022500 & 0x00000100)
+ disable |= (1ULL << NVDEV_ENGINE_CE0);
+ if (r022500 & 0x00000200)
+ disable |= (1ULL << NVDEV_ENGINE_CE1);
+
+ return disable;
+}
+
+static int
+gf100_devinit_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 size,
+ struct nvkm_object **pobject)
+{
+ struct nv50_devinit_priv *priv;
+ int ret;
+
+ ret = nvkm_devinit_create(parent, engine, oclass, &priv);
+ *pobject = nv_object(priv);
+ if (ret)
+ return ret;
+
+ if (nv_rd32(priv, 0x022500) & 0x00000001)
+ priv->base.post = true;
+
+ return 0;
+}
+
+struct nvkm_oclass *
+gf100_devinit_oclass = &(struct nvkm_devinit_impl) {
+ .base.handle = NV_SUBDEV(DEVINIT, 0xc0),
+ .base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = gf100_devinit_ctor,
+ .dtor = _nvkm_devinit_dtor,
+ .init = nv50_devinit_init,
+ .fini = _nvkm_devinit_fini,
+ },
+ .pll_set = gf100_devinit_pll_set,
+ .disable = gf100_devinit_disable,
+ .post = nvbios_init,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gm107.c b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gm107.c
new file mode 100644
index 000000000000..b345a53e881d
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gm107.c
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "nv50.h"
+
+#include <subdev/bios.h>
+#include <subdev/bios/init.h>
+
+u64
+gm107_devinit_disable(struct nvkm_devinit *devinit)
+{
+ struct nv50_devinit_priv *priv = (void *)devinit;
+ u32 r021c00 = nv_rd32(priv, 0x021c00);
+ u32 r021c04 = nv_rd32(priv, 0x021c04);
+ u64 disable = 0ULL;
+
+ if (r021c00 & 0x00000001)
+ disable |= (1ULL << NVDEV_ENGINE_CE0);
+ if (r021c00 & 0x00000004)
+ disable |= (1ULL << NVDEV_ENGINE_CE2);
+ if (r021c04 & 0x00000001)
+ disable |= (1ULL << NVDEV_ENGINE_DISP);
+
+ return disable;
+}
+
+struct nvkm_oclass *
+gm107_devinit_oclass = &(struct nvkm_devinit_impl) {
+ .base.handle = NV_SUBDEV(DEVINIT, 0x07),
+ .base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv50_devinit_ctor,
+ .dtor = _nvkm_devinit_dtor,
+ .init = nv50_devinit_init,
+ .fini = _nvkm_devinit_fini,
+ },
+ .pll_set = gf100_devinit_pll_set,
+ .disable = gm107_devinit_disable,
+ .post = nvbios_init,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gm204.c b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gm204.c
new file mode 100644
index 000000000000..535172c5f1ad
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gm204.c
@@ -0,0 +1,172 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "nv50.h"
+
+#include <subdev/bios.h>
+#include <subdev/bios/bit.h>
+#include <subdev/bios/pmu.h>
+
+static void
+pmu_code(struct nv50_devinit_priv *priv, u32 pmu, u32 img, u32 len, bool sec)
+{
+ struct nvkm_bios *bios = nvkm_bios(priv);
+ int i;
+
+ nv_wr32(priv, 0x10a180, 0x01000000 | (sec ? 0x10000000 : 0) | pmu);
+ for (i = 0; i < len; i += 4) {
+ if ((i & 0xff) == 0)
+ nv_wr32(priv, 0x10a188, (pmu + i) >> 8);
+ nv_wr32(priv, 0x10a184, nv_ro32(bios, img + i));
+ }
+
+ while (i & 0xff) {
+ nv_wr32(priv, 0x10a184, 0x00000000);
+ i += 4;
+ }
+}
+
+static void
+pmu_data(struct nv50_devinit_priv *priv, u32 pmu, u32 img, u32 len)
+{
+ struct nvkm_bios *bios = nvkm_bios(priv);
+ int i;
+
+ nv_wr32(priv, 0x10a1c0, 0x01000000 | pmu);
+ for (i = 0; i < len; i += 4)
+ nv_wr32(priv, 0x10a1c4, nv_ro32(bios, img + i));
+}
+
+static u32
+pmu_args(struct nv50_devinit_priv *priv, u32 argp, u32 argi)
+{
+ nv_wr32(priv, 0x10a1c0, argp);
+ nv_wr32(priv, 0x10a1c0, nv_rd32(priv, 0x10a1c4) + argi);
+ return nv_rd32(priv, 0x10a1c4);
+}
+
+static void
+pmu_exec(struct nv50_devinit_priv *priv, u32 init_addr)
+{
+ nv_wr32(priv, 0x10a104, init_addr);
+ nv_wr32(priv, 0x10a10c, 0x00000000);
+ nv_wr32(priv, 0x10a100, 0x00000002);
+}
+
+static int
+pmu_load(struct nv50_devinit_priv *priv, u8 type, bool post,
+ u32 *init_addr_pmu, u32 *args_addr_pmu)
+{
+ struct nvkm_bios *bios = nvkm_bios(priv);
+ struct nvbios_pmuR pmu;
+
+ if (!nvbios_pmuRm(bios, type, &pmu)) {
+ nv_error(priv, "VBIOS PMU fuc %02x not found\n", type);
+ return -EINVAL;
+ }
+
+ if (!post)
+ return 0;
+
+ pmu_code(priv, pmu.boot_addr_pmu, pmu.boot_addr, pmu.boot_size, false);
+ pmu_code(priv, pmu.code_addr_pmu, pmu.code_addr, pmu.code_size, true);
+ pmu_data(priv, pmu.data_addr_pmu, pmu.data_addr, pmu.data_size);
+
+ if (init_addr_pmu) {
+ *init_addr_pmu = pmu.init_addr_pmu;
+ *args_addr_pmu = pmu.args_addr_pmu;
+ return 0;
+ }
+
+ return pmu_exec(priv, pmu.init_addr_pmu), 0;
+}
+
+static int
+gm204_devinit_post(struct nvkm_subdev *subdev, bool post)
+{
+ struct nv50_devinit_priv *priv = (void *)nvkm_devinit(subdev);
+ struct nvkm_bios *bios = nvkm_bios(priv);
+ struct bit_entry bit_I;
+ u32 init, args;
+ int ret;
+
+ if (bit_entry(bios, 'I', &bit_I) || bit_I.version != 1 ||
+ bit_I.length < 0x1c) {
+ nv_error(priv, "VBIOS PMU init data not found\n");
+ return -EINVAL;
+ }
+
+ /* reset PMU and load init table parser ucode */
+ if (post) {
+ nv_mask(priv, 0x000200, 0x00002000, 0x00000000);
+ nv_mask(priv, 0x000200, 0x00002000, 0x00002000);
+ nv_rd32(priv, 0x000200);
+ while (nv_rd32(priv, 0x10a10c) & 0x00000006) {
+ }
+ }
+
+ ret = pmu_load(priv, 0x04, post, &init, &args);
+ if (ret)
+ return ret;
+
+ /* upload first chunk of init data */
+ if (post) {
+ u32 pmu = pmu_args(priv, args + 0x08, 0x08);
+ u32 img = nv_ro16(bios, bit_I.offset + 0x14);
+ u32 len = nv_ro16(bios, bit_I.offset + 0x16);
+ pmu_data(priv, pmu, img, len);
+ }
+
+ /* upload second chunk of init data */
+ if (post) {
+ u32 pmu = pmu_args(priv, args + 0x08, 0x10);
+ u32 img = nv_ro16(bios, bit_I.offset + 0x18);
+ u32 len = nv_ro16(bios, bit_I.offset + 0x1a);
+ pmu_data(priv, pmu, img, len);
+ }
+
+ /* execute init tables */
+ if (post) {
+ nv_wr32(priv, 0x10a040, 0x00005000);
+ pmu_exec(priv, init);
+ while (!(nv_rd32(priv, 0x10a040) & 0x00002000)) {
+ }
+ }
+
+ /* load and execute some other ucode image (bios therm?) */
+ return pmu_load(priv, 0x01, post, NULL, NULL);
+}
+
+struct nvkm_oclass *
+gm204_devinit_oclass = &(struct nvkm_devinit_impl) {
+ .base.handle = NV_SUBDEV(DEVINIT, 0x07),
+ .base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv50_devinit_ctor,
+ .dtor = _nvkm_devinit_dtor,
+ .init = nv50_devinit_init,
+ .fini = _nvkm_devinit_fini,
+ },
+ .pll_set = gf100_devinit_pll_set,
+ .disable = gm107_devinit_disable,
+ .post = gm204_devinit_post,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gt215.c b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gt215.c
new file mode 100644
index 000000000000..6a3e8d4efed7
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gt215.c
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "nv50.h"
+
+#include <subdev/bios.h>
+#include <subdev/bios/init.h>
+#include <subdev/bios/pll.h>
+#include <subdev/clk/pll.h>
+
+int
+gt215_devinit_pll_set(struct nvkm_devinit *devinit, u32 type, u32 freq)
+{
+ struct nv50_devinit_priv *priv = (void *)devinit;
+ struct nvkm_bios *bios = nvkm_bios(priv);
+ struct nvbios_pll info;
+ int N, fN, M, P;
+ int ret;
+
+ ret = nvbios_pll_parse(bios, type, &info);
+ if (ret)
+ return ret;
+
+ ret = gt215_pll_calc(nv_subdev(devinit), &info, freq, &N, &fN, &M, &P);
+ if (ret < 0)
+ return ret;
+
+ switch (info.type) {
+ case PLL_VPLL0:
+ case PLL_VPLL1:
+ nv_wr32(priv, info.reg + 0, 0x50000610);
+ nv_mask(priv, info.reg + 4, 0x003fffff,
+ (P << 16) | (M << 8) | N);
+ nv_wr32(priv, info.reg + 8, fN);
+ break;
+ default:
+ nv_warn(priv, "0x%08x/%dKhz unimplemented\n", type, freq);
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static u64
+gt215_devinit_disable(struct nvkm_devinit *devinit)
+{
+ struct nv50_devinit_priv *priv = (void *)devinit;
+ u32 r001540 = nv_rd32(priv, 0x001540);
+ u32 r00154c = nv_rd32(priv, 0x00154c);
+ u64 disable = 0ULL;
+
+ if (!(r001540 & 0x40000000)) {
+ disable |= (1ULL << NVDEV_ENGINE_MSPDEC);
+ disable |= (1ULL << NVDEV_ENGINE_MSPPP);
+ }
+
+ if (!(r00154c & 0x00000004))
+ disable |= (1ULL << NVDEV_ENGINE_DISP);
+ if (!(r00154c & 0x00000020))
+ disable |= (1ULL << NVDEV_ENGINE_MSVLD);
+ if (!(r00154c & 0x00000200))
+ disable |= (1ULL << NVDEV_ENGINE_CE0);
+
+ return disable;
+}
+
+static u32
+gt215_devinit_mmio_part[] = {
+ 0x100720, 0x1008bc, 4,
+ 0x100a20, 0x100adc, 4,
+ 0x100d80, 0x100ddc, 4,
+ 0x110000, 0x110f9c, 4,
+ 0x111000, 0x11103c, 8,
+ 0x111080, 0x1110fc, 4,
+ 0x111120, 0x1111fc, 4,
+ 0x111300, 0x1114bc, 4,
+ 0,
+};
+
+static u32
+gt215_devinit_mmio(struct nvkm_devinit *devinit, u32 addr)
+{
+ struct nv50_devinit_priv *priv = (void *)devinit;
+ u32 *mmio = gt215_devinit_mmio_part;
+
+ /* the init tables on some boards have INIT_RAM_RESTRICT_ZM_REG_GROUP
+ * instructions which touch registers that may not even exist on
+ * some configurations (Quadro 400), which causes the register
+ * interface to screw up for some amount of time after attempting to
+ * write to one of these, and results in all sorts of things going
+ * horribly wrong.
+ *
+ * the binary driver avoids touching these registers at all, however,
+ * the video bios doesn't care and does what the scripts say. it's
+ * presumed that the io-port access to priv registers isn't effected
+ * by the screw-up bug mentioned above.
+ *
+ * really, a new opcode should've been invented to handle these
+ * requirements, but whatever, it's too late for that now.
+ */
+ while (mmio[0]) {
+ if (addr >= mmio[0] && addr <= mmio[1]) {
+ u32 part = (addr / mmio[2]) & 7;
+ if (!priv->r001540)
+ priv->r001540 = nv_rd32(priv, 0x001540);
+ if (part >= hweight8((priv->r001540 >> 16) & 0xff))
+ return ~0;
+ return addr;
+ }
+ mmio += 3;
+ }
+
+ return addr;
+}
+
+struct nvkm_oclass *
+gt215_devinit_oclass = &(struct nvkm_devinit_impl) {
+ .base.handle = NV_SUBDEV(DEVINIT, 0xa3),
+ .base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv50_devinit_ctor,
+ .dtor = _nvkm_devinit_dtor,
+ .init = nv50_devinit_init,
+ .fini = _nvkm_devinit_fini,
+ },
+ .pll_set = gt215_devinit_pll_set,
+ .disable = gt215_devinit_disable,
+ .mmio = gt215_devinit_mmio,
+ .post = nvbios_init,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/mcp89.c b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/mcp89.c
new file mode 100644
index 000000000000..55cf48bbca1c
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/mcp89.c
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "nv50.h"
+
+#include <subdev/bios.h>
+#include <subdev/bios/init.h>
+
+static u64
+mcp89_devinit_disable(struct nvkm_devinit *devinit)
+{
+ struct nv50_devinit_priv *priv = (void *)devinit;
+ u32 r001540 = nv_rd32(priv, 0x001540);
+ u32 r00154c = nv_rd32(priv, 0x00154c);
+ u64 disable = 0;
+
+ if (!(r001540 & 0x40000000)) {
+ disable |= (1ULL << NVDEV_ENGINE_MSPDEC);
+ disable |= (1ULL << NVDEV_ENGINE_MSPPP);
+ }
+
+ if (!(r00154c & 0x00000004))
+ disable |= (1ULL << NVDEV_ENGINE_DISP);
+ if (!(r00154c & 0x00000020))
+ disable |= (1ULL << NVDEV_ENGINE_MSVLD);
+ if (!(r00154c & 0x00000040))
+ disable |= (1ULL << NVDEV_ENGINE_VIC);
+ if (!(r00154c & 0x00000200))
+ disable |= (1ULL << NVDEV_ENGINE_CE0);
+
+ return disable;
+}
+
+struct nvkm_oclass *
+mcp89_devinit_oclass = &(struct nvkm_devinit_impl) {
+ .base.handle = NV_SUBDEV(DEVINIT, 0xaf),
+ .base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv50_devinit_ctor,
+ .dtor = _nvkm_devinit_dtor,
+ .init = nv50_devinit_init,
+ .fini = _nvkm_devinit_fini,
+ },
+ .pll_set = gt215_devinit_pll_set,
+ .disable = mcp89_devinit_disable,
+ .post = nvbios_init,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv04.c b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv04.c
new file mode 100644
index 000000000000..03a0da834244
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv04.c
@@ -0,0 +1,470 @@
+/*
+ * Copyright (C) 2010 Francisco Jerez.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#include "nv04.h"
+#include "fbmem.h"
+
+#include <subdev/bios.h>
+#include <subdev/bios/init.h>
+#include <subdev/bios/pll.h>
+#include <subdev/clk/pll.h>
+#include <subdev/vga.h>
+
+static void
+nv04_devinit_meminit(struct nvkm_devinit *devinit)
+{
+ struct nv04_devinit_priv *priv = (void *)devinit;
+ u32 patt = 0xdeadbeef;
+ struct io_mapping *fb;
+ int i;
+
+ /* Map the framebuffer aperture */
+ fb = fbmem_init(nv_device(priv));
+ if (!fb) {
+ nv_error(priv, "failed to map fb\n");
+ return;
+ }
+
+ /* Sequencer and refresh off */
+ nv_wrvgas(priv, 0, 1, nv_rdvgas(priv, 0, 1) | 0x20);
+ nv_mask(priv, NV04_PFB_DEBUG_0, 0, NV04_PFB_DEBUG_0_REFRESH_OFF);
+
+ nv_mask(priv, NV04_PFB_BOOT_0, ~0,
+ NV04_PFB_BOOT_0_RAM_AMOUNT_16MB |
+ NV04_PFB_BOOT_0_RAM_WIDTH_128 |
+ NV04_PFB_BOOT_0_RAM_TYPE_SGRAM_16MBIT);
+
+ for (i = 0; i < 4; i++)
+ fbmem_poke(fb, 4 * i, patt);
+
+ fbmem_poke(fb, 0x400000, patt + 1);
+
+ if (fbmem_peek(fb, 0) == patt + 1) {
+ nv_mask(priv, NV04_PFB_BOOT_0,
+ NV04_PFB_BOOT_0_RAM_TYPE,
+ NV04_PFB_BOOT_0_RAM_TYPE_SDRAM_16MBIT);
+ nv_mask(priv, NV04_PFB_DEBUG_0,
+ NV04_PFB_DEBUG_0_REFRESH_OFF, 0);
+
+ for (i = 0; i < 4; i++)
+ fbmem_poke(fb, 4 * i, patt);
+
+ if ((fbmem_peek(fb, 0xc) & 0xffff) != (patt & 0xffff))
+ nv_mask(priv, NV04_PFB_BOOT_0,
+ NV04_PFB_BOOT_0_RAM_WIDTH_128 |
+ NV04_PFB_BOOT_0_RAM_AMOUNT,
+ NV04_PFB_BOOT_0_RAM_AMOUNT_8MB);
+ } else
+ if ((fbmem_peek(fb, 0xc) & 0xffff0000) != (patt & 0xffff0000)) {
+ nv_mask(priv, NV04_PFB_BOOT_0,
+ NV04_PFB_BOOT_0_RAM_WIDTH_128 |
+ NV04_PFB_BOOT_0_RAM_AMOUNT,
+ NV04_PFB_BOOT_0_RAM_AMOUNT_4MB);
+ } else
+ if (fbmem_peek(fb, 0) != patt) {
+ if (fbmem_readback(fb, 0x800000, patt))
+ nv_mask(priv, NV04_PFB_BOOT_0,
+ NV04_PFB_BOOT_0_RAM_AMOUNT,
+ NV04_PFB_BOOT_0_RAM_AMOUNT_8MB);
+ else
+ nv_mask(priv, NV04_PFB_BOOT_0,
+ NV04_PFB_BOOT_0_RAM_AMOUNT,
+ NV04_PFB_BOOT_0_RAM_AMOUNT_4MB);
+
+ nv_mask(priv, NV04_PFB_BOOT_0, NV04_PFB_BOOT_0_RAM_TYPE,
+ NV04_PFB_BOOT_0_RAM_TYPE_SGRAM_8MBIT);
+ } else
+ if (!fbmem_readback(fb, 0x800000, patt)) {
+ nv_mask(priv, NV04_PFB_BOOT_0, NV04_PFB_BOOT_0_RAM_AMOUNT,
+ NV04_PFB_BOOT_0_RAM_AMOUNT_8MB);
+
+ }
+
+ /* Refresh on, sequencer on */
+ nv_mask(priv, NV04_PFB_DEBUG_0, NV04_PFB_DEBUG_0_REFRESH_OFF, 0);
+ nv_wrvgas(priv, 0, 1, nv_rdvgas(priv, 0, 1) & ~0x20);
+ fbmem_fini(fb);
+}
+
+static int
+powerctrl_1_shift(int chip_version, int reg)
+{
+ int shift = -4;
+
+ if (chip_version < 0x17 || chip_version == 0x1a || chip_version == 0x20)
+ return shift;
+
+ switch (reg) {
+ case 0x680520:
+ shift += 4;
+ case 0x680508:
+ shift += 4;
+ case 0x680504:
+ shift += 4;
+ case 0x680500:
+ shift += 4;
+ }
+
+ /*
+ * the shift for vpll regs is only used for nv3x chips with a single
+ * stage pll
+ */
+ if (shift > 4 && (chip_version < 0x32 || chip_version == 0x35 ||
+ chip_version == 0x36 || chip_version >= 0x40))
+ shift = -4;
+
+ return shift;
+}
+
+void
+setPLL_single(struct nvkm_devinit *devinit, u32 reg,
+ struct nvkm_pll_vals *pv)
+{
+ int chip_version = nvkm_bios(devinit)->version.chip;
+ uint32_t oldpll = nv_rd32(devinit, reg);
+ int oldN = (oldpll >> 8) & 0xff, oldM = oldpll & 0xff;
+ uint32_t pll = (oldpll & 0xfff80000) | pv->log2P << 16 | pv->NM1;
+ uint32_t saved_powerctrl_1 = 0;
+ int shift_powerctrl_1 = powerctrl_1_shift(chip_version, reg);
+
+ if (oldpll == pll)
+ return; /* already set */
+
+ if (shift_powerctrl_1 >= 0) {
+ saved_powerctrl_1 = nv_rd32(devinit, 0x001584);
+ nv_wr32(devinit, 0x001584,
+ (saved_powerctrl_1 & ~(0xf << shift_powerctrl_1)) |
+ 1 << shift_powerctrl_1);
+ }
+
+ if (oldM && pv->M1 && (oldN / oldM < pv->N1 / pv->M1))
+ /* upclock -- write new post divider first */
+ nv_wr32(devinit, reg, pv->log2P << 16 | (oldpll & 0xffff));
+ else
+ /* downclock -- write new NM first */
+ nv_wr32(devinit, reg, (oldpll & 0xffff0000) | pv->NM1);
+
+ if ((chip_version < 0x17 || chip_version == 0x1a) &&
+ chip_version != 0x11)
+ /* wait a bit on older chips */
+ msleep(64);
+ nv_rd32(devinit, reg);
+
+ /* then write the other half as well */
+ nv_wr32(devinit, reg, pll);
+
+ if (shift_powerctrl_1 >= 0)
+ nv_wr32(devinit, 0x001584, saved_powerctrl_1);
+}
+
+static uint32_t
+new_ramdac580(uint32_t reg1, bool ss, uint32_t ramdac580)
+{
+ bool head_a = (reg1 == 0x680508);
+
+ if (ss) /* single stage pll mode */
+ ramdac580 |= head_a ? 0x00000100 : 0x10000000;
+ else
+ ramdac580 &= head_a ? 0xfffffeff : 0xefffffff;
+
+ return ramdac580;
+}
+
+void
+setPLL_double_highregs(struct nvkm_devinit *devinit, u32 reg1,
+ struct nvkm_pll_vals *pv)
+{
+ int chip_version = nvkm_bios(devinit)->version.chip;
+ bool nv3035 = chip_version == 0x30 || chip_version == 0x35;
+ uint32_t reg2 = reg1 + ((reg1 == 0x680520) ? 0x5c : 0x70);
+ uint32_t oldpll1 = nv_rd32(devinit, reg1);
+ uint32_t oldpll2 = !nv3035 ? nv_rd32(devinit, reg2) : 0;
+ uint32_t pll1 = (oldpll1 & 0xfff80000) | pv->log2P << 16 | pv->NM1;
+ uint32_t pll2 = (oldpll2 & 0x7fff0000) | 1 << 31 | pv->NM2;
+ uint32_t oldramdac580 = 0, ramdac580 = 0;
+ bool single_stage = !pv->NM2 || pv->N2 == pv->M2; /* nv41+ only */
+ uint32_t saved_powerctrl_1 = 0, savedc040 = 0;
+ int shift_powerctrl_1 = powerctrl_1_shift(chip_version, reg1);
+
+ /* model specific additions to generic pll1 and pll2 set up above */
+ if (nv3035) {
+ pll1 = (pll1 & 0xfcc7ffff) | (pv->N2 & 0x18) << 21 |
+ (pv->N2 & 0x7) << 19 | 8 << 4 | (pv->M2 & 7) << 4;
+ pll2 = 0;
+ }
+ if (chip_version > 0x40 && reg1 >= 0x680508) { /* !nv40 */
+ oldramdac580 = nv_rd32(devinit, 0x680580);
+ ramdac580 = new_ramdac580(reg1, single_stage, oldramdac580);
+ if (oldramdac580 != ramdac580)
+ oldpll1 = ~0; /* force mismatch */
+ if (single_stage)
+ /* magic value used by nvidia in single stage mode */
+ pll2 |= 0x011f;
+ }
+ if (chip_version > 0x70)
+ /* magic bits set by the blob (but not the bios) on g71-73 */
+ pll1 = (pll1 & 0x7fffffff) | (single_stage ? 0x4 : 0xc) << 28;
+
+ if (oldpll1 == pll1 && oldpll2 == pll2)
+ return; /* already set */
+
+ if (shift_powerctrl_1 >= 0) {
+ saved_powerctrl_1 = nv_rd32(devinit, 0x001584);
+ nv_wr32(devinit, 0x001584,
+ (saved_powerctrl_1 & ~(0xf << shift_powerctrl_1)) |
+ 1 << shift_powerctrl_1);
+ }
+
+ if (chip_version >= 0x40) {
+ int shift_c040 = 14;
+
+ switch (reg1) {
+ case 0x680504:
+ shift_c040 += 2;
+ case 0x680500:
+ shift_c040 += 2;
+ case 0x680520:
+ shift_c040 += 2;
+ case 0x680508:
+ shift_c040 += 2;
+ }
+
+ savedc040 = nv_rd32(devinit, 0xc040);
+ if (shift_c040 != 14)
+ nv_wr32(devinit, 0xc040, savedc040 & ~(3 << shift_c040));
+ }
+
+ if (oldramdac580 != ramdac580)
+ nv_wr32(devinit, 0x680580, ramdac580);
+
+ if (!nv3035)
+ nv_wr32(devinit, reg2, pll2);
+ nv_wr32(devinit, reg1, pll1);
+
+ if (shift_powerctrl_1 >= 0)
+ nv_wr32(devinit, 0x001584, saved_powerctrl_1);
+ if (chip_version >= 0x40)
+ nv_wr32(devinit, 0xc040, savedc040);
+}
+
+void
+setPLL_double_lowregs(struct nvkm_devinit *devinit, u32 NMNMreg,
+ struct nvkm_pll_vals *pv)
+{
+ /* When setting PLLs, there is a merry game of disabling and enabling
+ * various bits of hardware during the process. This function is a
+ * synthesis of six nv4x traces, nearly each card doing a subtly
+ * different thing. With luck all the necessary bits for each card are
+ * combined herein. Without luck it deviates from each card's formula
+ * so as to not work on any :)
+ */
+
+ uint32_t Preg = NMNMreg - 4;
+ bool mpll = Preg == 0x4020;
+ uint32_t oldPval = nv_rd32(devinit, Preg);
+ uint32_t NMNM = pv->NM2 << 16 | pv->NM1;
+ uint32_t Pval = (oldPval & (mpll ? ~(0x77 << 16) : ~(7 << 16))) |
+ 0xc << 28 | pv->log2P << 16;
+ uint32_t saved4600 = 0;
+ /* some cards have different maskc040s */
+ uint32_t maskc040 = ~(3 << 14), savedc040;
+ bool single_stage = !pv->NM2 || pv->N2 == pv->M2;
+
+ if (nv_rd32(devinit, NMNMreg) == NMNM && (oldPval & 0xc0070000) == Pval)
+ return;
+
+ if (Preg == 0x4000)
+ maskc040 = ~0x333;
+ if (Preg == 0x4058)
+ maskc040 = ~(0xc << 24);
+
+ if (mpll) {
+ struct nvbios_pll info;
+ uint8_t Pval2;
+
+ if (nvbios_pll_parse(nvkm_bios(devinit), Preg, &info))
+ return;
+
+ Pval2 = pv->log2P + info.bias_p;
+ if (Pval2 > info.max_p)
+ Pval2 = info.max_p;
+ Pval |= 1 << 28 | Pval2 << 20;
+
+ saved4600 = nv_rd32(devinit, 0x4600);
+ nv_wr32(devinit, 0x4600, saved4600 | 8 << 28);
+ }
+ if (single_stage)
+ Pval |= mpll ? 1 << 12 : 1 << 8;
+
+ nv_wr32(devinit, Preg, oldPval | 1 << 28);
+ nv_wr32(devinit, Preg, Pval & ~(4 << 28));
+ if (mpll) {
+ Pval |= 8 << 20;
+ nv_wr32(devinit, 0x4020, Pval & ~(0xc << 28));
+ nv_wr32(devinit, 0x4038, Pval & ~(0xc << 28));
+ }
+
+ savedc040 = nv_rd32(devinit, 0xc040);
+ nv_wr32(devinit, 0xc040, savedc040 & maskc040);
+
+ nv_wr32(devinit, NMNMreg, NMNM);
+ if (NMNMreg == 0x4024)
+ nv_wr32(devinit, 0x403c, NMNM);
+
+ nv_wr32(devinit, Preg, Pval);
+ if (mpll) {
+ Pval &= ~(8 << 20);
+ nv_wr32(devinit, 0x4020, Pval);
+ nv_wr32(devinit, 0x4038, Pval);
+ nv_wr32(devinit, 0x4600, saved4600);
+ }
+
+ nv_wr32(devinit, 0xc040, savedc040);
+
+ if (mpll) {
+ nv_wr32(devinit, 0x4020, Pval & ~(1 << 28));
+ nv_wr32(devinit, 0x4038, Pval & ~(1 << 28));
+ }
+}
+
+int
+nv04_devinit_pll_set(struct nvkm_devinit *devinit, u32 type, u32 freq)
+{
+ struct nvkm_bios *bios = nvkm_bios(devinit);
+ struct nvkm_pll_vals pv;
+ struct nvbios_pll info;
+ int cv = bios->version.chip;
+ int N1, M1, N2, M2, P;
+ int ret;
+
+ ret = nvbios_pll_parse(bios, type > 0x405c ? type : type - 4, &info);
+ if (ret)
+ return ret;
+
+ ret = nv04_pll_calc(nv_subdev(devinit), &info, freq,
+ &N1, &M1, &N2, &M2, &P);
+ if (!ret)
+ return -EINVAL;
+
+ pv.refclk = info.refclk;
+ pv.N1 = N1;
+ pv.M1 = M1;
+ pv.N2 = N2;
+ pv.M2 = M2;
+ pv.log2P = P;
+
+ if (cv == 0x30 || cv == 0x31 || cv == 0x35 || cv == 0x36 ||
+ cv >= 0x40) {
+ if (type > 0x405c)
+ setPLL_double_highregs(devinit, type, &pv);
+ else
+ setPLL_double_lowregs(devinit, type, &pv);
+ } else
+ setPLL_single(devinit, type, &pv);
+
+ return 0;
+}
+
+int
+nv04_devinit_fini(struct nvkm_object *object, bool suspend)
+{
+ struct nv04_devinit_priv *priv = (void *)object;
+ int ret;
+
+ /* make i2c busses accessible */
+ nv_mask(priv, 0x000200, 0x00000001, 0x00000001);
+
+ ret = nvkm_devinit_fini(&priv->base, suspend);
+ if (ret)
+ return ret;
+
+ /* unslave crtcs */
+ if (priv->owner < 0)
+ priv->owner = nv_rdvgaowner(priv);
+ nv_wrvgaowner(priv, 0);
+ return 0;
+}
+
+int
+nv04_devinit_init(struct nvkm_object *object)
+{
+ struct nv04_devinit_priv *priv = (void *)object;
+
+ if (!priv->base.post) {
+ u32 htotal = nv_rdvgac(priv, 0, 0x06);
+ htotal |= (nv_rdvgac(priv, 0, 0x07) & 0x01) << 8;
+ htotal |= (nv_rdvgac(priv, 0, 0x07) & 0x20) << 4;
+ htotal |= (nv_rdvgac(priv, 0, 0x25) & 0x01) << 10;
+ htotal |= (nv_rdvgac(priv, 0, 0x41) & 0x01) << 11;
+ if (!htotal) {
+ nv_info(priv, "adaptor not initialised\n");
+ priv->base.post = true;
+ }
+ }
+
+ return nvkm_devinit_init(&priv->base);
+}
+
+void
+nv04_devinit_dtor(struct nvkm_object *object)
+{
+ struct nv04_devinit_priv *priv = (void *)object;
+
+ /* restore vga owner saved at first init */
+ nv_wrvgaowner(priv, priv->owner);
+
+ nvkm_devinit_destroy(&priv->base);
+}
+
+int
+nv04_devinit_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 size,
+ struct nvkm_object **pobject)
+{
+ struct nv04_devinit_priv *priv;
+ int ret;
+
+ ret = nvkm_devinit_create(parent, engine, oclass, &priv);
+ *pobject = nv_object(priv);
+ if (ret)
+ return ret;
+
+ priv->owner = -1;
+ return 0;
+}
+
+struct nvkm_oclass *
+nv04_devinit_oclass = &(struct nvkm_devinit_impl) {
+ .base.handle = NV_SUBDEV(DEVINIT, 0x04),
+ .base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv04_devinit_ctor,
+ .dtor = nv04_devinit_dtor,
+ .init = nv04_devinit_init,
+ .fini = nv04_devinit_fini,
+ },
+ .meminit = nv04_devinit_meminit,
+ .pll_set = nv04_devinit_pll_set,
+ .post = nvbios_init,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv04.h b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv04.h
new file mode 100644
index 000000000000..14a51a9ff7d0
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv04.h
@@ -0,0 +1,22 @@
+#ifndef __NVKM_DEVINIT_NV04_H__
+#define __NVKM_DEVINIT_NV04_H__
+#include "priv.h"
+struct nvkm_pll_vals;
+
+struct nv04_devinit_priv {
+ struct nvkm_devinit base;
+ u8 owner;
+};
+
+int nv04_devinit_ctor(struct nvkm_object *, struct nvkm_object *,
+ struct nvkm_oclass *, void *, u32,
+ struct nvkm_object **);
+void nv04_devinit_dtor(struct nvkm_object *);
+int nv04_devinit_init(struct nvkm_object *);
+int nv04_devinit_fini(struct nvkm_object *, bool);
+int nv04_devinit_pll_set(struct nvkm_devinit *, u32, u32);
+
+void setPLL_single(struct nvkm_devinit *, u32, struct nvkm_pll_vals *);
+void setPLL_double_highregs(struct nvkm_devinit *, u32, struct nvkm_pll_vals *);
+void setPLL_double_lowregs(struct nvkm_devinit *, u32, struct nvkm_pll_vals *);
+#endif
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv05.c b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv05.c
new file mode 100644
index 000000000000..def8649216c2
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv05.c
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2010 Francisco Jerez.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#include "nv04.h"
+#include "fbmem.h"
+
+#include <subdev/bios.h>
+#include <subdev/bios/bmp.h>
+#include <subdev/bios/init.h>
+#include <subdev/vga.h>
+
+static void
+nv05_devinit_meminit(struct nvkm_devinit *devinit)
+{
+ static const u8 default_config_tab[][2] = {
+ { 0x24, 0x00 },
+ { 0x28, 0x00 },
+ { 0x24, 0x01 },
+ { 0x1f, 0x00 },
+ { 0x0f, 0x00 },
+ { 0x17, 0x00 },
+ { 0x06, 0x00 },
+ { 0x00, 0x00 }
+ };
+ struct nv04_devinit_priv *priv = (void *)devinit;
+ struct nvkm_bios *bios = nvkm_bios(priv);
+ struct io_mapping *fb;
+ u32 patt = 0xdeadbeef;
+ u16 data;
+ u8 strap, ramcfg[2];
+ int i, v;
+
+ /* Map the framebuffer aperture */
+ fb = fbmem_init(nv_device(priv));
+ if (!fb) {
+ nv_error(priv, "failed to map fb\n");
+ return;
+ }
+
+ strap = (nv_rd32(priv, 0x101000) & 0x0000003c) >> 2;
+ if ((data = bmp_mem_init_table(bios))) {
+ ramcfg[0] = nv_ro08(bios, data + 2 * strap + 0);
+ ramcfg[1] = nv_ro08(bios, data + 2 * strap + 1);
+ } else {
+ ramcfg[0] = default_config_tab[strap][0];
+ ramcfg[1] = default_config_tab[strap][1];
+ }
+
+ /* Sequencer off */
+ nv_wrvgas(priv, 0, 1, nv_rdvgas(priv, 0, 1) | 0x20);
+
+ if (nv_rd32(priv, NV04_PFB_BOOT_0) & NV04_PFB_BOOT_0_UMA_ENABLE)
+ goto out;
+
+ nv_mask(priv, NV04_PFB_DEBUG_0, NV04_PFB_DEBUG_0_REFRESH_OFF, 0);
+
+ /* If present load the hardcoded scrambling table */
+ if (data) {
+ for (i = 0, data += 0x10; i < 8; i++, data += 4) {
+ u32 scramble = nv_ro32(bios, data);
+ nv_wr32(priv, NV04_PFB_SCRAMBLE(i), scramble);
+ }
+ }
+
+ /* Set memory type/width/length defaults depending on the straps */
+ nv_mask(priv, NV04_PFB_BOOT_0, 0x3f, ramcfg[0]);
+
+ if (ramcfg[1] & 0x80)
+ nv_mask(priv, NV04_PFB_CFG0, 0, NV04_PFB_CFG0_SCRAMBLE);
+
+ nv_mask(priv, NV04_PFB_CFG1, 0x700001, (ramcfg[1] & 1) << 20);
+ nv_mask(priv, NV04_PFB_CFG1, 0, 1);
+
+ /* Probe memory bus width */
+ for (i = 0; i < 4; i++)
+ fbmem_poke(fb, 4 * i, patt);
+
+ if (fbmem_peek(fb, 0xc) != patt)
+ nv_mask(priv, NV04_PFB_BOOT_0,
+ NV04_PFB_BOOT_0_RAM_WIDTH_128, 0);
+
+ /* Probe memory length */
+ v = nv_rd32(priv, NV04_PFB_BOOT_0) & NV04_PFB_BOOT_0_RAM_AMOUNT;
+
+ if (v == NV04_PFB_BOOT_0_RAM_AMOUNT_32MB &&
+ (!fbmem_readback(fb, 0x1000000, ++patt) ||
+ !fbmem_readback(fb, 0, ++patt)))
+ nv_mask(priv, NV04_PFB_BOOT_0, NV04_PFB_BOOT_0_RAM_AMOUNT,
+ NV04_PFB_BOOT_0_RAM_AMOUNT_16MB);
+
+ if (v == NV04_PFB_BOOT_0_RAM_AMOUNT_16MB &&
+ !fbmem_readback(fb, 0x800000, ++patt))
+ nv_mask(priv, NV04_PFB_BOOT_0, NV04_PFB_BOOT_0_RAM_AMOUNT,
+ NV04_PFB_BOOT_0_RAM_AMOUNT_8MB);
+
+ if (!fbmem_readback(fb, 0x400000, ++patt))
+ nv_mask(priv, NV04_PFB_BOOT_0, NV04_PFB_BOOT_0_RAM_AMOUNT,
+ NV04_PFB_BOOT_0_RAM_AMOUNT_4MB);
+
+out:
+ /* Sequencer on */
+ nv_wrvgas(priv, 0, 1, nv_rdvgas(priv, 0, 1) & ~0x20);
+ fbmem_fini(fb);
+}
+
+struct nvkm_oclass *
+nv05_devinit_oclass = &(struct nvkm_devinit_impl) {
+ .base.handle = NV_SUBDEV(DEVINIT, 0x05),
+ .base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv04_devinit_ctor,
+ .dtor = nv04_devinit_dtor,
+ .init = nv04_devinit_init,
+ .fini = nv04_devinit_fini,
+ },
+ .meminit = nv05_devinit_meminit,
+ .pll_set = nv04_devinit_pll_set,
+ .post = nvbios_init,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv10.c b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv10.c
new file mode 100644
index 000000000000..7aabc1bf0640
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv10.c
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2010 Francisco Jerez.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#include "nv04.h"
+#include "fbmem.h"
+
+#include <subdev/bios.h>
+#include <subdev/bios/init.h>
+
+static void
+nv10_devinit_meminit(struct nvkm_devinit *devinit)
+{
+ struct nv04_devinit_priv *priv = (void *)devinit;
+ static const int mem_width[] = { 0x10, 0x00, 0x20 };
+ int mem_width_count;
+ uint32_t patt = 0xdeadbeef;
+ struct io_mapping *fb;
+ int i, j, k;
+
+ if (nv_device(priv)->card_type >= NV_11 &&
+ nv_device(priv)->chipset >= 0x17)
+ mem_width_count = 3;
+ else
+ mem_width_count = 2;
+
+ /* Map the framebuffer aperture */
+ fb = fbmem_init(nv_device(priv));
+ if (!fb) {
+ nv_error(priv, "failed to map fb\n");
+ return;
+ }
+
+ nv_wr32(priv, NV10_PFB_REFCTRL, NV10_PFB_REFCTRL_VALID_1);
+
+ /* Probe memory bus width */
+ for (i = 0; i < mem_width_count; i++) {
+ nv_mask(priv, NV04_PFB_CFG0, 0x30, mem_width[i]);
+
+ for (j = 0; j < 4; j++) {
+ for (k = 0; k < 4; k++)
+ fbmem_poke(fb, 0x1c, 0);
+
+ fbmem_poke(fb, 0x1c, patt);
+ fbmem_poke(fb, 0x3c, 0);
+
+ if (fbmem_peek(fb, 0x1c) == patt)
+ goto mem_width_found;
+ }
+ }
+
+mem_width_found:
+ patt <<= 1;
+
+ /* Probe amount of installed memory */
+ for (i = 0; i < 4; i++) {
+ int off = nv_rd32(priv, 0x10020c) - 0x100000;
+
+ fbmem_poke(fb, off, patt);
+ fbmem_poke(fb, 0, 0);
+
+ fbmem_peek(fb, 0);
+ fbmem_peek(fb, 0);
+ fbmem_peek(fb, 0);
+ fbmem_peek(fb, 0);
+
+ if (fbmem_peek(fb, off) == patt)
+ goto amount_found;
+ }
+
+ /* IC missing - disable the upper half memory space. */
+ nv_mask(priv, NV04_PFB_CFG0, 0x1000, 0);
+
+amount_found:
+ fbmem_fini(fb);
+}
+
+struct nvkm_oclass *
+nv10_devinit_oclass = &(struct nvkm_devinit_impl) {
+ .base.handle = NV_SUBDEV(DEVINIT, 0x10),
+ .base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv04_devinit_ctor,
+ .dtor = nv04_devinit_dtor,
+ .init = nv04_devinit_init,
+ .fini = nv04_devinit_fini,
+ },
+ .meminit = nv10_devinit_meminit,
+ .pll_set = nv04_devinit_pll_set,
+ .post = nvbios_init,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv1a.c b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv1a.c
new file mode 100644
index 000000000000..9f36fff5a1c3
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv1a.c
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "nv04.h"
+
+#include <subdev/bios.h>
+#include <subdev/bios/init.h>
+
+struct nvkm_oclass *
+nv1a_devinit_oclass = &(struct nvkm_devinit_impl) {
+ .base.handle = NV_SUBDEV(DEVINIT, 0x1a),
+ .base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv04_devinit_ctor,
+ .dtor = nv04_devinit_dtor,
+ .init = nv04_devinit_init,
+ .fini = nv04_devinit_fini,
+ },
+ .pll_set = nv04_devinit_pll_set,
+ .post = nvbios_init,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv20.c b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv20.c
new file mode 100644
index 000000000000..02fcfd921c42
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv20.c
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2010 Francisco Jerez.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#include "nv04.h"
+#include "fbmem.h"
+
+#include <subdev/bios.h>
+#include <subdev/bios/init.h>
+
+static void
+nv20_devinit_meminit(struct nvkm_devinit *devinit)
+{
+ struct nv04_devinit_priv *priv = (void *)devinit;
+ struct nvkm_device *device = nv_device(priv);
+ uint32_t mask = (device->chipset >= 0x25 ? 0x300 : 0x900);
+ uint32_t amount, off;
+ struct io_mapping *fb;
+
+ /* Map the framebuffer aperture */
+ fb = fbmem_init(nv_device(priv));
+ if (!fb) {
+ nv_error(priv, "failed to map fb\n");
+ return;
+ }
+
+ nv_wr32(priv, NV10_PFB_REFCTRL, NV10_PFB_REFCTRL_VALID_1);
+
+ /* Allow full addressing */
+ nv_mask(priv, NV04_PFB_CFG0, 0, mask);
+
+ amount = nv_rd32(priv, 0x10020c);
+ for (off = amount; off > 0x2000000; off -= 0x2000000)
+ fbmem_poke(fb, off - 4, off);
+
+ amount = nv_rd32(priv, 0x10020c);
+ if (amount != fbmem_peek(fb, amount - 4))
+ /* IC missing - disable the upper half memory space. */
+ nv_mask(priv, NV04_PFB_CFG0, mask, 0);
+
+ fbmem_fini(fb);
+}
+
+struct nvkm_oclass *
+nv20_devinit_oclass = &(struct nvkm_devinit_impl) {
+ .base.handle = NV_SUBDEV(DEVINIT, 0x20),
+ .base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv04_devinit_ctor,
+ .dtor = nv04_devinit_dtor,
+ .init = nv04_devinit_init,
+ .fini = nv04_devinit_fini,
+ },
+ .meminit = nv20_devinit_meminit,
+ .pll_set = nv04_devinit_pll_set,
+ .post = nvbios_init,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv50.c b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv50.c
new file mode 100644
index 000000000000..26b7cb13e167
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv50.c
@@ -0,0 +1,174 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "nv50.h"
+
+#include <subdev/bios.h>
+#include <subdev/bios/dcb.h>
+#include <subdev/bios/disp.h>
+#include <subdev/bios/init.h>
+#include <subdev/bios/pll.h>
+#include <subdev/clk/pll.h>
+#include <subdev/ibus.h>
+#include <subdev/vga.h>
+
+int
+nv50_devinit_pll_set(struct nvkm_devinit *devinit, u32 type, u32 freq)
+{
+ struct nv50_devinit_priv *priv = (void *)devinit;
+ struct nvkm_bios *bios = nvkm_bios(priv);
+ struct nvbios_pll info;
+ int N1, M1, N2, M2, P;
+ int ret;
+
+ ret = nvbios_pll_parse(bios, type, &info);
+ if (ret) {
+ nv_error(devinit, "failed to retrieve pll data, %d\n", ret);
+ return ret;
+ }
+
+ ret = nv04_pll_calc(nv_subdev(devinit), &info, freq, &N1, &M1, &N2, &M2, &P);
+ if (!ret) {
+ nv_error(devinit, "failed pll calculation\n");
+ return ret;
+ }
+
+ switch (info.type) {
+ case PLL_VPLL0:
+ case PLL_VPLL1:
+ nv_wr32(priv, info.reg + 0, 0x10000611);
+ nv_mask(priv, info.reg + 4, 0x00ff00ff, (M1 << 16) | N1);
+ nv_mask(priv, info.reg + 8, 0x7fff00ff, (P << 28) |
+ (M2 << 16) | N2);
+ break;
+ case PLL_MEMORY:
+ nv_mask(priv, info.reg + 0, 0x01ff0000, (P << 22) |
+ (info.bias_p << 19) |
+ (P << 16));
+ nv_wr32(priv, info.reg + 4, (N1 << 8) | M1);
+ break;
+ default:
+ nv_mask(priv, info.reg + 0, 0x00070000, (P << 16));
+ nv_wr32(priv, info.reg + 4, (N1 << 8) | M1);
+ break;
+ }
+
+ return 0;
+}
+
+static u64
+nv50_devinit_disable(struct nvkm_devinit *devinit)
+{
+ struct nv50_devinit_priv *priv = (void *)devinit;
+ u32 r001540 = nv_rd32(priv, 0x001540);
+ u64 disable = 0ULL;
+
+ if (!(r001540 & 0x40000000))
+ disable |= (1ULL << NVDEV_ENGINE_MPEG);
+
+ return disable;
+}
+
+int
+nv50_devinit_init(struct nvkm_object *object)
+{
+ struct nvkm_bios *bios = nvkm_bios(object);
+ struct nvkm_ibus *ibus = nvkm_ibus(object);
+ struct nv50_devinit_priv *priv = (void *)object;
+ struct nvbios_outp info;
+ struct dcb_output outp;
+ u8 ver = 0xff, hdr, cnt, len;
+ int ret, i = 0;
+
+ if (!priv->base.post) {
+ if (!nv_rdvgac(priv, 0, 0x00) &&
+ !nv_rdvgac(priv, 0, 0x1a)) {
+ nv_info(priv, "adaptor not initialised\n");
+ priv->base.post = true;
+ }
+ }
+
+ /* some boards appear to require certain priv register timeouts
+ * to be bumped before runing devinit scripts. not a clue why
+ * the vbios engineers didn't make the scripts just work...
+ */
+ if (priv->base.post && ibus)
+ nv_ofuncs(ibus)->init(nv_object(ibus));
+
+ ret = nvkm_devinit_init(&priv->base);
+ if (ret)
+ return ret;
+
+ /* if we ran the init tables, we have to execute the first script
+ * pointer of each dcb entry's display encoder table in order
+ * to properly initialise each encoder.
+ */
+ while (priv->base.post && dcb_outp_parse(bios, i, &ver, &hdr, &outp)) {
+ if (nvbios_outp_match(bios, outp.hasht, outp.hashm,
+ &ver, &hdr, &cnt, &len, &info)) {
+ struct nvbios_init init = {
+ .subdev = nv_subdev(priv),
+ .bios = bios,
+ .offset = info.script[0],
+ .outp = &outp,
+ .crtc = -1,
+ .execute = 1,
+ };
+
+ nvbios_exec(&init);
+ }
+ i++;
+ }
+
+ return 0;
+}
+
+int
+nv50_devinit_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 size,
+ struct nvkm_object **pobject)
+{
+ struct nv50_devinit_priv *priv;
+ int ret;
+
+ ret = nvkm_devinit_create(parent, engine, oclass, &priv);
+ *pobject = nv_object(priv);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+struct nvkm_oclass *
+nv50_devinit_oclass = &(struct nvkm_devinit_impl) {
+ .base.handle = NV_SUBDEV(DEVINIT, 0x50),
+ .base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv50_devinit_ctor,
+ .dtor = _nvkm_devinit_dtor,
+ .init = nv50_devinit_init,
+ .fini = _nvkm_devinit_fini,
+ },
+ .pll_set = nv50_devinit_pll_set,
+ .disable = nv50_devinit_disable,
+ .post = nvbios_init,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv50.h b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv50.h
new file mode 100644
index 000000000000..b882b65ff3cd
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv50.h
@@ -0,0 +1,21 @@
+#ifndef __NVKM_DEVINIT_NV50_H__
+#define __NVKM_DEVINIT_NV50_H__
+#include "priv.h"
+
+struct nv50_devinit_priv {
+ struct nvkm_devinit base;
+ u32 r001540;
+};
+
+int nv50_devinit_ctor(struct nvkm_object *, struct nvkm_object *,
+ struct nvkm_oclass *, void *, u32,
+ struct nvkm_object **);
+int nv50_devinit_init(struct nvkm_object *);
+int nv50_devinit_pll_set(struct nvkm_devinit *, u32, u32);
+
+int gt215_devinit_pll_set(struct nvkm_devinit *, u32, u32);
+
+int gf100_devinit_pll_set(struct nvkm_devinit *, u32, u32);
+
+u64 gm107_devinit_disable(struct nvkm_devinit *);
+#endif
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/priv.h b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/priv.h
new file mode 100644
index 000000000000..bb51a95d8012
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/priv.h
@@ -0,0 +1,34 @@
+#ifndef __NVKM_DEVINIT_PRIV_H__
+#define __NVKM_DEVINIT_PRIV_H__
+#include <subdev/devinit.h>
+
+struct nvkm_devinit_impl {
+ struct nvkm_oclass base;
+ void (*meminit)(struct nvkm_devinit *);
+ int (*pll_set)(struct nvkm_devinit *, u32 type, u32 freq);
+ u64 (*disable)(struct nvkm_devinit *);
+ u32 (*mmio)(struct nvkm_devinit *, u32);
+ int (*post)(struct nvkm_subdev *, bool);
+};
+
+#define nvkm_devinit_create(p,e,o,d) \
+ nvkm_devinit_create_((p), (e), (o), sizeof(**d), (void **)d)
+#define nvkm_devinit_destroy(p) ({ \
+ struct nvkm_devinit *d = (p); \
+ _nvkm_devinit_dtor(nv_object(d)); \
+})
+#define nvkm_devinit_init(p) ({ \
+ struct nvkm_devinit *d = (p); \
+ _nvkm_devinit_init(nv_object(d)); \
+})
+#define nvkm_devinit_fini(p,s) ({ \
+ struct nvkm_devinit *d = (p); \
+ _nvkm_devinit_fini(nv_object(d), (s)); \
+})
+
+int nvkm_devinit_create_(struct nvkm_object *, struct nvkm_object *,
+ struct nvkm_oclass *, int, void **);
+void _nvkm_devinit_dtor(struct nvkm_object *);
+int _nvkm_devinit_init(struct nvkm_object *);
+int _nvkm_devinit_fini(struct nvkm_object *, bool suspend);
+#endif
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/Kbuild b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/Kbuild
new file mode 100644
index 000000000000..904d601e8a50
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/Kbuild
@@ -0,0 +1,45 @@
+nvkm-y += nvkm/subdev/fb/base.o
+nvkm-y += nvkm/subdev/fb/nv04.o
+nvkm-y += nvkm/subdev/fb/nv10.o
+nvkm-y += nvkm/subdev/fb/nv1a.o
+nvkm-y += nvkm/subdev/fb/nv20.o
+nvkm-y += nvkm/subdev/fb/nv25.o
+nvkm-y += nvkm/subdev/fb/nv30.o
+nvkm-y += nvkm/subdev/fb/nv35.o
+nvkm-y += nvkm/subdev/fb/nv36.o
+nvkm-y += nvkm/subdev/fb/nv40.o
+nvkm-y += nvkm/subdev/fb/nv41.o
+nvkm-y += nvkm/subdev/fb/nv44.o
+nvkm-y += nvkm/subdev/fb/nv46.o
+nvkm-y += nvkm/subdev/fb/nv47.o
+nvkm-y += nvkm/subdev/fb/nv49.o
+nvkm-y += nvkm/subdev/fb/nv4e.o
+nvkm-y += nvkm/subdev/fb/nv50.o
+nvkm-y += nvkm/subdev/fb/g84.o
+nvkm-y += nvkm/subdev/fb/gt215.o
+nvkm-y += nvkm/subdev/fb/mcp77.o
+nvkm-y += nvkm/subdev/fb/mcp89.o
+nvkm-y += nvkm/subdev/fb/gf100.o
+nvkm-y += nvkm/subdev/fb/gk104.o
+nvkm-y += nvkm/subdev/fb/gk20a.o
+nvkm-y += nvkm/subdev/fb/gm107.o
+nvkm-y += nvkm/subdev/fb/ramnv04.o
+nvkm-y += nvkm/subdev/fb/ramnv10.o
+nvkm-y += nvkm/subdev/fb/ramnv1a.o
+nvkm-y += nvkm/subdev/fb/ramnv20.o
+nvkm-y += nvkm/subdev/fb/ramnv40.o
+nvkm-y += nvkm/subdev/fb/ramnv41.o
+nvkm-y += nvkm/subdev/fb/ramnv44.o
+nvkm-y += nvkm/subdev/fb/ramnv49.o
+nvkm-y += nvkm/subdev/fb/ramnv4e.o
+nvkm-y += nvkm/subdev/fb/ramnv50.o
+nvkm-y += nvkm/subdev/fb/ramgt215.o
+nvkm-y += nvkm/subdev/fb/rammcp77.o
+nvkm-y += nvkm/subdev/fb/ramgf100.o
+nvkm-y += nvkm/subdev/fb/ramgk104.o
+nvkm-y += nvkm/subdev/fb/ramgk20a.o
+nvkm-y += nvkm/subdev/fb/ramgm107.o
+nvkm-y += nvkm/subdev/fb/sddr2.o
+nvkm-y += nvkm/subdev/fb/sddr3.o
+nvkm-y += nvkm/subdev/fb/gddr3.o
+nvkm-y += nvkm/subdev/fb/gddr5.o
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/base.c
new file mode 100644
index 000000000000..16589fa613cd
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/base.c
@@ -0,0 +1,155 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "priv.h"
+
+#include <subdev/bios.h>
+#include <subdev/bios/M0203.h>
+
+int
+nvkm_fb_bios_memtype(struct nvkm_bios *bios)
+{
+ const u8 ramcfg = (nv_rd32(bios, 0x101000) & 0x0000003c) >> 2;
+ struct nvbios_M0203E M0203E;
+ u8 ver, hdr;
+
+ if (nvbios_M0203Em(bios, ramcfg, &ver, &hdr, &M0203E)) {
+ switch (M0203E.type) {
+ case M0203E_TYPE_DDR2 : return NV_MEM_TYPE_DDR2;
+ case M0203E_TYPE_DDR3 : return NV_MEM_TYPE_DDR3;
+ case M0203E_TYPE_GDDR3: return NV_MEM_TYPE_GDDR3;
+ case M0203E_TYPE_GDDR5: return NV_MEM_TYPE_GDDR5;
+ default:
+ nv_warn(bios, "M0203E type %02x\n", M0203E.type);
+ return NV_MEM_TYPE_UNKNOWN;
+ }
+ }
+
+ nv_warn(bios, "M0203E not matched!\n");
+ return NV_MEM_TYPE_UNKNOWN;
+}
+
+int
+_nvkm_fb_fini(struct nvkm_object *object, bool suspend)
+{
+ struct nvkm_fb *pfb = (void *)object;
+ int ret;
+
+ ret = nv_ofuncs(pfb->ram)->fini(nv_object(pfb->ram), suspend);
+ if (ret && suspend)
+ return ret;
+
+ return nvkm_subdev_fini(&pfb->base, suspend);
+}
+
+int
+_nvkm_fb_init(struct nvkm_object *object)
+{
+ struct nvkm_fb *pfb = (void *)object;
+ int ret, i;
+
+ ret = nvkm_subdev_init(&pfb->base);
+ if (ret)
+ return ret;
+
+ ret = nv_ofuncs(pfb->ram)->init(nv_object(pfb->ram));
+ if (ret)
+ return ret;
+
+ for (i = 0; i < pfb->tile.regions; i++)
+ pfb->tile.prog(pfb, i, &pfb->tile.region[i]);
+
+ return 0;
+}
+
+void
+_nvkm_fb_dtor(struct nvkm_object *object)
+{
+ struct nvkm_fb *pfb = (void *)object;
+ int i;
+
+ for (i = 0; i < pfb->tile.regions; i++)
+ pfb->tile.fini(pfb, i, &pfb->tile.region[i]);
+ nvkm_mm_fini(&pfb->tags);
+ nvkm_mm_fini(&pfb->vram);
+
+ nvkm_object_ref(NULL, (struct nvkm_object **)&pfb->ram);
+ nvkm_subdev_destroy(&pfb->base);
+}
+
+int
+nvkm_fb_create_(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, int length, void **pobject)
+{
+ struct nvkm_fb_impl *impl = (void *)oclass;
+ static const char *name[] = {
+ [NV_MEM_TYPE_UNKNOWN] = "unknown",
+ [NV_MEM_TYPE_STOLEN ] = "stolen system memory",
+ [NV_MEM_TYPE_SGRAM ] = "SGRAM",
+ [NV_MEM_TYPE_SDRAM ] = "SDRAM",
+ [NV_MEM_TYPE_DDR1 ] = "DDR1",
+ [NV_MEM_TYPE_DDR2 ] = "DDR2",
+ [NV_MEM_TYPE_DDR3 ] = "DDR3",
+ [NV_MEM_TYPE_GDDR2 ] = "GDDR2",
+ [NV_MEM_TYPE_GDDR3 ] = "GDDR3",
+ [NV_MEM_TYPE_GDDR4 ] = "GDDR4",
+ [NV_MEM_TYPE_GDDR5 ] = "GDDR5",
+ };
+ struct nvkm_object *ram;
+ struct nvkm_fb *pfb;
+ int ret;
+
+ ret = nvkm_subdev_create_(parent, engine, oclass, 0, "PFB", "fb",
+ length, pobject);
+ pfb = *pobject;
+ if (ret)
+ return ret;
+
+ pfb->memtype_valid = impl->memtype;
+
+ ret = nvkm_object_ctor(nv_object(pfb), NULL, impl->ram, NULL, 0, &ram);
+ if (ret) {
+ nv_fatal(pfb, "error detecting memory configuration!!\n");
+ return ret;
+ }
+
+ pfb->ram = (void *)ram;
+
+ if (!nvkm_mm_initialised(&pfb->vram)) {
+ ret = nvkm_mm_init(&pfb->vram, 0, pfb->ram->size >> 12, 1);
+ if (ret)
+ return ret;
+ }
+
+ if (!nvkm_mm_initialised(&pfb->tags)) {
+ ret = nvkm_mm_init(&pfb->tags, 0, pfb->ram->tags ?
+ ++pfb->ram->tags : 0, 1);
+ if (ret)
+ return ret;
+ }
+
+ nv_info(pfb, "RAM type: %s\n", name[pfb->ram->type]);
+ nv_info(pfb, "RAM size: %d MiB\n", (int)(pfb->ram->size >> 20));
+ nv_info(pfb, " ZCOMP: %d tags\n", pfb->ram->tags);
+ return 0;
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/g84.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/g84.c
new file mode 100644
index 000000000000..6c968d1e98b3
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/g84.c
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "nv50.h"
+
+struct nvkm_oclass *
+g84_fb_oclass = &(struct nv50_fb_impl) {
+ .base.base.handle = NV_SUBDEV(FB, 0x84),
+ .base.base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv50_fb_ctor,
+ .dtor = nv50_fb_dtor,
+ .init = nv50_fb_init,
+ .fini = _nvkm_fb_fini,
+ },
+ .base.memtype = nv50_fb_memtype_valid,
+ .base.ram = &nv50_ram_oclass,
+ .trap = 0x001d07ff,
+}.base.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gddr3.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gddr3.c
new file mode 100644
index 000000000000..15b462ae33cb
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gddr3.c
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs <bskeggs@redhat.com>
+ * Roy Spliet <rspliet@eclipso.eu>
+ */
+#include "priv.h"
+
+struct ramxlat {
+ int id;
+ u8 enc;
+};
+
+static inline int
+ramxlat(const struct ramxlat *xlat, int id)
+{
+ while (xlat->id >= 0) {
+ if (xlat->id == id)
+ return xlat->enc;
+ xlat++;
+ }
+ return -EINVAL;
+}
+
+static const struct ramxlat
+ramgddr3_cl_lo[] = {
+ { 7, 7 }, { 8, 0 }, { 9, 1 }, { 10, 2 }, { 11, 3 },
+ /* the below are mentioned in some, but not all, gddr3 docs */
+ { 12, 4 }, { 13, 5 }, { 14, 6 },
+ /* XXX: Per Samsung docs, are these used? They overlap with Qimonda */
+ /* { 4, 4 }, { 5, 5 }, { 6, 6 }, { 12, 8 }, { 13, 9 }, { 14, 10 },
+ * { 15, 11 }, */
+ { -1 }
+};
+
+static const struct ramxlat
+ramgddr3_cl_hi[] = {
+ { 10, 2 }, { 11, 3 }, { 12, 4 }, { 13, 5 }, { 14, 6 }, { 15, 7 },
+ { 16, 0 }, { 17, 1 },
+ { -1 }
+};
+
+static const struct ramxlat
+ramgddr3_wr_lo[] = {
+ { 5, 2 }, { 7, 4 }, { 8, 5 }, { 9, 6 }, { 10, 7 },
+ { 11, 0 },
+ /* the below are mentioned in some, but not all, gddr3 docs */
+ { 4, 1 }, { 6, 3 }, { 12, 1 }, { 13 , 2 },
+ { -1 }
+};
+
+int
+nvkm_gddr3_calc(struct nvkm_ram *ram)
+{
+ int CL, WR, CWL, DLL = 0, ODT = 0, hi;
+
+ switch (ram->next->bios.timing_ver) {
+ case 0x10:
+ CWL = ram->next->bios.timing_10_CWL;
+ CL = ram->next->bios.timing_10_CL;
+ WR = ram->next->bios.timing_10_WR;
+ DLL = !ram->next->bios.ramcfg_10_DLLoff;
+ ODT = ram->next->bios.timing_10_ODT;
+ break;
+ case 0x20:
+ CWL = (ram->next->bios.timing[1] & 0x00000f80) >> 7;
+ CL = (ram->next->bios.timing[1] & 0x0000001f) >> 0;
+ WR = (ram->next->bios.timing[2] & 0x007f0000) >> 16;
+ /* XXX: Get these values from the VBIOS instead */
+ DLL = !(ram->mr[1] & 0x1);
+ ODT = (ram->mr[1] & 0x004) >> 2 |
+ (ram->mr[1] & 0x040) >> 5 |
+ (ram->mr[1] & 0x200) >> 7;
+ break;
+ default:
+ return -ENOSYS;
+ }
+
+ hi = ram->mr[2] & 0x1;
+ CL = ramxlat(hi ? ramgddr3_cl_hi : ramgddr3_cl_lo, CL);
+ WR = ramxlat(ramgddr3_wr_lo, WR);
+ if (CL < 0 || CWL < 1 || CWL > 7 || WR < 0)
+ return -EINVAL;
+
+ ram->mr[0] &= ~0xf74;
+ ram->mr[0] |= (CWL & 0x07) << 9;
+ ram->mr[0] |= (CL & 0x07) << 4;
+ ram->mr[0] |= (CL & 0x08) >> 1;
+
+ ram->mr[1] &= ~0x3fc;
+ ram->mr[1] |= (ODT & 0x03) << 2;
+ ram->mr[1] |= (ODT & 0x03) << 8;
+ ram->mr[1] |= (WR & 0x03) << 4;
+ ram->mr[1] |= (WR & 0x04) << 5;
+ ram->mr[1] |= !DLL << 6;
+ return 0;
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gddr5.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gddr5.c
new file mode 100644
index 000000000000..f6f9eee1dcd0
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gddr5.c
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs <bskeggs@redhat.com>
+ */
+#include "priv.h"
+
+/* binary driver only executes this path if the condition (a) is true
+ * for any configuration (combination of rammap+ramcfg+timing) that
+ * can be reached on a given card. for now, we will execute the branch
+ * unconditionally in the hope that a "false everywhere" in the bios
+ * tables doesn't actually mean "don't touch this".
+ */
+#define NOTE00(a) 1
+
+int
+nvkm_gddr5_calc(struct nvkm_ram *ram, bool nuts)
+{
+ int pd, lf, xd, vh, vr, vo, l3;
+ int WL, CL, WR, at[2], dt, ds;
+ int rq = ram->freq < 1000000; /* XXX */
+
+ switch (ram->next->bios.ramcfg_ver) {
+ case 0x11:
+ pd = ram->next->bios.ramcfg_11_01_80;
+ lf = ram->next->bios.ramcfg_11_01_40;
+ xd = !ram->next->bios.ramcfg_11_01_20;
+ vh = ram->next->bios.ramcfg_11_02_10;
+ vr = ram->next->bios.ramcfg_11_02_04;
+ vo = ram->next->bios.ramcfg_11_06;
+ l3 = !ram->next->bios.ramcfg_11_07_02;
+ break;
+ default:
+ return -ENOSYS;
+ }
+
+ switch (ram->next->bios.timing_ver) {
+ case 0x20:
+ WL = (ram->next->bios.timing[1] & 0x00000f80) >> 7;
+ CL = (ram->next->bios.timing[1] & 0x0000001f);
+ WR = (ram->next->bios.timing[2] & 0x007f0000) >> 16;
+ at[0] = ram->next->bios.timing_20_2e_c0;
+ at[1] = ram->next->bios.timing_20_2e_30;
+ dt = ram->next->bios.timing_20_2e_03;
+ ds = ram->next->bios.timing_20_2f_03;
+ break;
+ default:
+ return -ENOSYS;
+ }
+
+ if (WL < 1 || WL > 7 || CL < 5 || CL > 36 || WR < 4 || WR > 35)
+ return -EINVAL;
+ CL -= 5;
+ WR -= 4;
+
+ ram->mr[0] &= ~0xf7f;
+ ram->mr[0] |= (WR & 0x0f) << 8;
+ ram->mr[0] |= (CL & 0x0f) << 3;
+ ram->mr[0] |= (WL & 0x07) << 0;
+
+ ram->mr[1] &= ~0x0bf;
+ ram->mr[1] |= (xd & 0x01) << 7;
+ ram->mr[1] |= (at[0] & 0x03) << 4;
+ ram->mr[1] |= (dt & 0x03) << 2;
+ ram->mr[1] |= (ds & 0x03) << 0;
+
+ /* this seems wrong, alternate field used for the broadcast
+ * on nuts vs non-nuts configs.. meh, it matches for now.
+ */
+ ram->mr1_nuts = ram->mr[1];
+ if (nuts) {
+ ram->mr[1] &= ~0x030;
+ ram->mr[1] |= (at[1] & 0x03) << 4;
+ }
+
+ ram->mr[3] &= ~0x020;
+ ram->mr[3] |= (rq & 0x01) << 5;
+
+ ram->mr[5] &= ~0x004;
+ ram->mr[5] |= (l3 << 2);
+
+ if (!vo)
+ vo = (ram->mr[6] & 0xff0) >> 4;
+ if (ram->mr[6] & 0x001)
+ pd = 1; /* binary driver does this.. bug? */
+ ram->mr[6] &= ~0xff1;
+ ram->mr[6] |= (vo & 0xff) << 4;
+ ram->mr[6] |= (pd & 0x01) << 0;
+
+ if (NOTE00(vr)) {
+ ram->mr[7] &= ~0x300;
+ ram->mr[7] |= (vr & 0x03) << 8;
+ }
+ ram->mr[7] &= ~0x088;
+ ram->mr[7] |= (vh & 0x01) << 7;
+ ram->mr[7] |= (lf & 0x01) << 3;
+
+ ram->mr[8] &= ~0x003;
+ ram->mr[8] |= (WR & 0x10) >> 3;
+ ram->mr[8] |= (CL & 0x10) >> 4;
+ return 0;
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gf100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gf100.c
new file mode 100644
index 000000000000..d51aa0237baf
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gf100.c
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "gf100.h"
+
+#include <core/device.h>
+
+extern const u8 gf100_pte_storage_type_map[256];
+
+bool
+gf100_fb_memtype_valid(struct nvkm_fb *pfb, u32 tile_flags)
+{
+ u8 memtype = (tile_flags & 0x0000ff00) >> 8;
+ return likely((gf100_pte_storage_type_map[memtype] != 0xff));
+}
+
+static void
+gf100_fb_intr(struct nvkm_subdev *subdev)
+{
+ struct gf100_fb_priv *priv = (void *)subdev;
+ u32 intr = nv_rd32(priv, 0x000100);
+ if (intr & 0x08000000) {
+ nv_debug(priv, "PFFB intr\n");
+ intr &= ~0x08000000;
+ }
+ if (intr & 0x00002000) {
+ nv_debug(priv, "PBFB intr\n");
+ intr &= ~0x00002000;
+ }
+}
+
+int
+gf100_fb_init(struct nvkm_object *object)
+{
+ struct gf100_fb_priv *priv = (void *)object;
+ int ret;
+
+ ret = nvkm_fb_init(&priv->base);
+ if (ret)
+ return ret;
+
+ if (priv->r100c10_page)
+ nv_wr32(priv, 0x100c10, priv->r100c10 >> 8);
+
+ nv_mask(priv, 0x100c80, 0x00000001, 0x00000000); /* 128KiB lpg */
+ return 0;
+}
+
+void
+gf100_fb_dtor(struct nvkm_object *object)
+{
+ struct nvkm_device *device = nv_device(object);
+ struct gf100_fb_priv *priv = (void *)object;
+
+ if (priv->r100c10_page) {
+ dma_unmap_page(nv_device_base(device), priv->r100c10, PAGE_SIZE,
+ DMA_BIDIRECTIONAL);
+ __free_page(priv->r100c10_page);
+ }
+
+ nvkm_fb_destroy(&priv->base);
+}
+
+int
+gf100_fb_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 size,
+ struct nvkm_object **pobject)
+{
+ struct nvkm_device *device = nv_device(parent);
+ struct gf100_fb_priv *priv;
+ int ret;
+
+ ret = nvkm_fb_create(parent, engine, oclass, &priv);
+ *pobject = nv_object(priv);
+ if (ret)
+ return ret;
+
+ priv->r100c10_page = alloc_page(GFP_KERNEL | __GFP_ZERO);
+ if (priv->r100c10_page) {
+ priv->r100c10 = dma_map_page(nv_device_base(device),
+ priv->r100c10_page, 0, PAGE_SIZE,
+ DMA_BIDIRECTIONAL);
+ if (dma_mapping_error(nv_device_base(device), priv->r100c10))
+ return -EFAULT;
+ }
+
+ nv_subdev(priv)->intr = gf100_fb_intr;
+ return 0;
+}
+
+struct nvkm_oclass *
+gf100_fb_oclass = &(struct nvkm_fb_impl) {
+ .base.handle = NV_SUBDEV(FB, 0xc0),
+ .base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = gf100_fb_ctor,
+ .dtor = gf100_fb_dtor,
+ .init = gf100_fb_init,
+ .fini = _nvkm_fb_fini,
+ },
+ .memtype = gf100_fb_memtype_valid,
+ .ram = &gf100_ram_oclass,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gf100.h b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gf100.h
new file mode 100644
index 000000000000..0af4da259471
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gf100.h
@@ -0,0 +1,28 @@
+#ifndef __NVKM_RAM_NVC0_H__
+#define __NVKM_RAM_NVC0_H__
+#include "priv.h"
+#include "nv50.h"
+
+struct gf100_fb_priv {
+ struct nvkm_fb base;
+ struct page *r100c10_page;
+ dma_addr_t r100c10;
+};
+
+int gf100_fb_ctor(struct nvkm_object *, struct nvkm_object *,
+ struct nvkm_oclass *, void *, u32,
+ struct nvkm_object **);
+void gf100_fb_dtor(struct nvkm_object *);
+int gf100_fb_init(struct nvkm_object *);
+bool gf100_fb_memtype_valid(struct nvkm_fb *, u32);
+
+#define gf100_ram_create(p,e,o,m,d) \
+ gf100_ram_create_((p), (e), (o), (m), sizeof(**d), (void **)d)
+int gf100_ram_create_(struct nvkm_object *, struct nvkm_object *,
+ struct nvkm_oclass *, u32, int, void **);
+int gf100_ram_get(struct nvkm_fb *, u64, u32, u32, u32,
+ struct nvkm_mem **);
+void gf100_ram_put(struct nvkm_fb *, struct nvkm_mem **);
+
+int gk104_ram_init(struct nvkm_object*);
+#endif
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gk104.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gk104.c
new file mode 100644
index 000000000000..1c08317665bb
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gk104.c
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "gf100.h"
+
+struct nvkm_oclass *
+gk104_fb_oclass = &(struct nvkm_fb_impl) {
+ .base.handle = NV_SUBDEV(FB, 0xe0),
+ .base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = gf100_fb_ctor,
+ .dtor = gf100_fb_dtor,
+ .init = gf100_fb_init,
+ .fini = _nvkm_fb_fini,
+ },
+ .memtype = gf100_fb_memtype_valid,
+ .ram = &gk104_ram_oclass,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gk20a.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gk20a.c
new file mode 100644
index 000000000000..6762847c05e8
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gk20a.c
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+#include "gf100.h"
+
+struct gk20a_fb_priv {
+ struct nvkm_fb base;
+};
+
+static int
+gk20a_fb_init(struct nvkm_object *object)
+{
+ struct gk20a_fb_priv *priv = (void *)object;
+ int ret;
+
+ ret = nvkm_fb_init(&priv->base);
+ if (ret)
+ return ret;
+
+ nv_mask(priv, 0x100c80, 0x00000001, 0x00000000); /* 128KiB lpg */
+ return 0;
+}
+
+static int
+gk20a_fb_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 size,
+ struct nvkm_object **pobject)
+{
+ struct gk20a_fb_priv *priv;
+ int ret;
+
+ ret = nvkm_fb_create(parent, engine, oclass, &priv);
+ *pobject = nv_object(priv);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+struct nvkm_oclass *
+gk20a_fb_oclass = &(struct nvkm_fb_impl) {
+ .base.handle = NV_SUBDEV(FB, 0xea),
+ .base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = gk20a_fb_ctor,
+ .dtor = _nvkm_fb_dtor,
+ .init = gk20a_fb_init,
+ .fini = _nvkm_fb_fini,
+ },
+ .memtype = gf100_fb_memtype_valid,
+ .ram = &gk20a_ram_oclass,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gm107.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gm107.c
new file mode 100644
index 000000000000..843f9356b360
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gm107.c
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "gf100.h"
+
+struct nvkm_oclass *
+gm107_fb_oclass = &(struct nvkm_fb_impl) {
+ .base.handle = NV_SUBDEV(FB, 0x07),
+ .base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = gf100_fb_ctor,
+ .dtor = gf100_fb_dtor,
+ .init = gf100_fb_init,
+ .fini = _nvkm_fb_fini,
+ },
+ .memtype = gf100_fb_memtype_valid,
+ .ram = &gm107_ram_oclass,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gt215.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gt215.c
new file mode 100644
index 000000000000..dd9b8a0a3c8e
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gt215.c
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "nv50.h"
+
+struct nvkm_oclass *
+gt215_fb_oclass = &(struct nv50_fb_impl) {
+ .base.base.handle = NV_SUBDEV(FB, 0xa3),
+ .base.base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv50_fb_ctor,
+ .dtor = nv50_fb_dtor,
+ .init = nv50_fb_init,
+ .fini = _nvkm_fb_fini,
+ },
+ .base.memtype = nv50_fb_memtype_valid,
+ .base.ram = &gt215_ram_oclass,
+ .trap = 0x000d0fff,
+}.base.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/mcp77.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/mcp77.c
new file mode 100644
index 000000000000..7be4a47ef4ad
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/mcp77.c
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "nv50.h"
+
+struct nvkm_oclass *
+mcp77_fb_oclass = &(struct nv50_fb_impl) {
+ .base.base.handle = NV_SUBDEV(FB, 0xaa),
+ .base.base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv50_fb_ctor,
+ .dtor = nv50_fb_dtor,
+ .init = nv50_fb_init,
+ .fini = _nvkm_fb_fini,
+ },
+ .base.memtype = nv50_fb_memtype_valid,
+ .base.ram = &mcp77_ram_oclass,
+ .trap = 0x001d07ff,
+}.base.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/mcp89.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/mcp89.c
new file mode 100644
index 000000000000..2d00656faef5
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/mcp89.c
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "nv50.h"
+
+struct nvkm_oclass *
+mcp89_fb_oclass = &(struct nv50_fb_impl) {
+ .base.base.handle = NV_SUBDEV(FB, 0xaf),
+ .base.base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv50_fb_ctor,
+ .dtor = nv50_fb_dtor,
+ .init = nv50_fb_init,
+ .fini = _nvkm_fb_fini,
+ },
+ .base.memtype = nv50_fb_memtype_valid,
+ .base.ram = &mcp77_ram_oclass,
+ .trap = 0x089d1fff,
+}.base.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv04.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv04.c
new file mode 100644
index 000000000000..c063dec7d03a
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv04.c
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "nv04.h"
+#include "regsnv04.h"
+
+bool
+nv04_fb_memtype_valid(struct nvkm_fb *pfb, u32 tile_flags)
+{
+ if (!(tile_flags & 0xff00))
+ return true;
+
+ return false;
+}
+
+static int
+nv04_fb_init(struct nvkm_object *object)
+{
+ struct nv04_fb_priv *priv = (void *)object;
+ int ret;
+
+ ret = nvkm_fb_init(&priv->base);
+ if (ret)
+ return ret;
+
+ /* This is what the DDX did for NV_ARCH_04, but a mmio-trace shows
+ * nvidia reading PFB_CFG_0, then writing back its original value.
+ * (which was 0x701114 in this case)
+ */
+ nv_wr32(priv, NV04_PFB_CFG0, 0x1114);
+ return 0;
+}
+
+int
+nv04_fb_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 size,
+ struct nvkm_object **pobject)
+{
+ struct nv04_fb_impl *impl = (void *)oclass;
+ struct nv04_fb_priv *priv;
+ int ret;
+
+ ret = nvkm_fb_create(parent, engine, oclass, &priv);
+ *pobject = nv_object(priv);
+ if (ret)
+ return ret;
+
+ priv->base.tile.regions = impl->tile.regions;
+ priv->base.tile.init = impl->tile.init;
+ priv->base.tile.comp = impl->tile.comp;
+ priv->base.tile.fini = impl->tile.fini;
+ priv->base.tile.prog = impl->tile.prog;
+ return 0;
+}
+
+struct nvkm_oclass *
+nv04_fb_oclass = &(struct nv04_fb_impl) {
+ .base.base.handle = NV_SUBDEV(FB, 0x04),
+ .base.base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv04_fb_ctor,
+ .dtor = _nvkm_fb_dtor,
+ .init = nv04_fb_init,
+ .fini = _nvkm_fb_fini,
+ },
+ .base.memtype = nv04_fb_memtype_valid,
+ .base.ram = &nv04_ram_oclass,
+}.base.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv04.h b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv04.h
new file mode 100644
index 000000000000..caa0d03aaacc
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv04.h
@@ -0,0 +1,53 @@
+#ifndef __NVKM_FB_NV04_H__
+#define __NVKM_FB_NV04_H__
+#include "priv.h"
+
+struct nv04_fb_priv {
+ struct nvkm_fb base;
+};
+
+int nv04_fb_ctor(struct nvkm_object *, struct nvkm_object *,
+ struct nvkm_oclass *, void *, u32,
+ struct nvkm_object **);
+
+struct nv04_fb_impl {
+ struct nvkm_fb_impl base;
+ struct {
+ int regions;
+ void (*init)(struct nvkm_fb *, int i, u32 addr, u32 size,
+ u32 pitch, u32 flags, struct nvkm_fb_tile *);
+ void (*comp)(struct nvkm_fb *, int i, u32 size, u32 flags,
+ struct nvkm_fb_tile *);
+ void (*fini)(struct nvkm_fb *, int i,
+ struct nvkm_fb_tile *);
+ void (*prog)(struct nvkm_fb *, int i,
+ struct nvkm_fb_tile *);
+ } tile;
+};
+
+void nv10_fb_tile_init(struct nvkm_fb *, int i, u32 addr, u32 size,
+ u32 pitch, u32 flags, struct nvkm_fb_tile *);
+void nv10_fb_tile_fini(struct nvkm_fb *, int i, struct nvkm_fb_tile *);
+void nv10_fb_tile_prog(struct nvkm_fb *, int, struct nvkm_fb_tile *);
+
+void nv20_fb_tile_init(struct nvkm_fb *, int i, u32 addr, u32 size,
+ u32 pitch, u32 flags, struct nvkm_fb_tile *);
+void nv20_fb_tile_fini(struct nvkm_fb *, int i, struct nvkm_fb_tile *);
+void nv20_fb_tile_prog(struct nvkm_fb *, int, struct nvkm_fb_tile *);
+
+int nv30_fb_init(struct nvkm_object *);
+void nv30_fb_tile_init(struct nvkm_fb *, int i, u32 addr, u32 size,
+ u32 pitch, u32 flags, struct nvkm_fb_tile *);
+
+void nv40_fb_tile_comp(struct nvkm_fb *, int i, u32 size, u32 flags,
+ struct nvkm_fb_tile *);
+
+int nv41_fb_init(struct nvkm_object *);
+void nv41_fb_tile_prog(struct nvkm_fb *, int, struct nvkm_fb_tile *);
+
+int nv44_fb_init(struct nvkm_object *);
+void nv44_fb_tile_prog(struct nvkm_fb *, int, struct nvkm_fb_tile *);
+
+void nv46_fb_tile_init(struct nvkm_fb *, int i, u32 addr, u32 size,
+ u32 pitch, u32 flags, struct nvkm_fb_tile *);
+#endif
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv10.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv10.c
new file mode 100644
index 000000000000..f3530e4a6760
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv10.c
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2010 Francisco Jerez.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#include "nv04.h"
+
+void
+nv10_fb_tile_init(struct nvkm_fb *pfb, int i, u32 addr, u32 size, u32 pitch,
+ u32 flags, struct nvkm_fb_tile *tile)
+{
+ tile->addr = 0x80000000 | addr;
+ tile->limit = max(1u, addr + size) - 1;
+ tile->pitch = pitch;
+}
+
+void
+nv10_fb_tile_fini(struct nvkm_fb *pfb, int i, struct nvkm_fb_tile *tile)
+{
+ tile->addr = 0;
+ tile->limit = 0;
+ tile->pitch = 0;
+ tile->zcomp = 0;
+}
+
+void
+nv10_fb_tile_prog(struct nvkm_fb *pfb, int i, struct nvkm_fb_tile *tile)
+{
+ nv_wr32(pfb, 0x100244 + (i * 0x10), tile->limit);
+ nv_wr32(pfb, 0x100248 + (i * 0x10), tile->pitch);
+ nv_wr32(pfb, 0x100240 + (i * 0x10), tile->addr);
+ nv_rd32(pfb, 0x100240 + (i * 0x10));
+}
+
+struct nvkm_oclass *
+nv10_fb_oclass = &(struct nv04_fb_impl) {
+ .base.base.handle = NV_SUBDEV(FB, 0x10),
+ .base.base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv04_fb_ctor,
+ .dtor = _nvkm_fb_dtor,
+ .init = _nvkm_fb_init,
+ .fini = _nvkm_fb_fini,
+ },
+ .base.memtype = nv04_fb_memtype_valid,
+ .base.ram = &nv10_ram_oclass,
+ .tile.regions = 8,
+ .tile.init = nv10_fb_tile_init,
+ .tile.fini = nv10_fb_tile_fini,
+ .tile.prog = nv10_fb_tile_prog,
+}.base.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv1a.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv1a.c
new file mode 100644
index 000000000000..83bcb73caf0a
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv1a.c
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2010 Francisco Jerez.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#include "nv04.h"
+
+struct nvkm_oclass *
+nv1a_fb_oclass = &(struct nv04_fb_impl) {
+ .base.base.handle = NV_SUBDEV(FB, 0x1a),
+ .base.base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv04_fb_ctor,
+ .dtor = _nvkm_fb_dtor,
+ .init = _nvkm_fb_init,
+ .fini = _nvkm_fb_fini,
+ },
+ .base.memtype = nv04_fb_memtype_valid,
+ .base.ram = &nv1a_ram_oclass,
+ .tile.regions = 8,
+ .tile.init = nv10_fb_tile_init,
+ .tile.fini = nv10_fb_tile_fini,
+ .tile.prog = nv10_fb_tile_prog,
+}.base.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv20.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv20.c
new file mode 100644
index 000000000000..e37084b8d05e
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv20.c
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2010 Francisco Jerez.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#include "nv04.h"
+
+void
+nv20_fb_tile_init(struct nvkm_fb *pfb, int i, u32 addr, u32 size, u32 pitch,
+ u32 flags, struct nvkm_fb_tile *tile)
+{
+ tile->addr = 0x00000001 | addr;
+ tile->limit = max(1u, addr + size) - 1;
+ tile->pitch = pitch;
+ if (flags & 4) {
+ pfb->tile.comp(pfb, i, size, flags, tile);
+ tile->addr |= 2;
+ }
+}
+
+static void
+nv20_fb_tile_comp(struct nvkm_fb *pfb, int i, u32 size, u32 flags,
+ struct nvkm_fb_tile *tile)
+{
+ u32 tiles = DIV_ROUND_UP(size, 0x40);
+ u32 tags = round_up(tiles / pfb->ram->parts, 0x40);
+ if (!nvkm_mm_head(&pfb->tags, 0, 1, tags, tags, 1, &tile->tag)) {
+ if (!(flags & 2)) tile->zcomp = 0x00000000; /* Z16 */
+ else tile->zcomp = 0x04000000; /* Z24S8 */
+ tile->zcomp |= tile->tag->offset;
+ tile->zcomp |= 0x80000000; /* enable */
+#ifdef __BIG_ENDIAN
+ tile->zcomp |= 0x08000000;
+#endif
+ }
+}
+
+void
+nv20_fb_tile_fini(struct nvkm_fb *pfb, int i, struct nvkm_fb_tile *tile)
+{
+ tile->addr = 0;
+ tile->limit = 0;
+ tile->pitch = 0;
+ tile->zcomp = 0;
+ nvkm_mm_free(&pfb->tags, &tile->tag);
+}
+
+void
+nv20_fb_tile_prog(struct nvkm_fb *pfb, int i, struct nvkm_fb_tile *tile)
+{
+ nv_wr32(pfb, 0x100244 + (i * 0x10), tile->limit);
+ nv_wr32(pfb, 0x100248 + (i * 0x10), tile->pitch);
+ nv_wr32(pfb, 0x100240 + (i * 0x10), tile->addr);
+ nv_rd32(pfb, 0x100240 + (i * 0x10));
+ nv_wr32(pfb, 0x100300 + (i * 0x04), tile->zcomp);
+}
+
+struct nvkm_oclass *
+nv20_fb_oclass = &(struct nv04_fb_impl) {
+ .base.base.handle = NV_SUBDEV(FB, 0x20),
+ .base.base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv04_fb_ctor,
+ .dtor = _nvkm_fb_dtor,
+ .init = _nvkm_fb_init,
+ .fini = _nvkm_fb_fini,
+ },
+ .base.memtype = nv04_fb_memtype_valid,
+ .base.ram = &nv20_ram_oclass,
+ .tile.regions = 8,
+ .tile.init = nv20_fb_tile_init,
+ .tile.comp = nv20_fb_tile_comp,
+ .tile.fini = nv20_fb_tile_fini,
+ .tile.prog = nv20_fb_tile_prog,
+}.base.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv25.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv25.c
new file mode 100644
index 000000000000..bc9f54f38fba
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv25.c
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2010 Francisco Jerez.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#include "nv04.h"
+
+static void
+nv25_fb_tile_comp(struct nvkm_fb *pfb, int i, u32 size, u32 flags,
+ struct nvkm_fb_tile *tile)
+{
+ u32 tiles = DIV_ROUND_UP(size, 0x40);
+ u32 tags = round_up(tiles / pfb->ram->parts, 0x40);
+ if (!nvkm_mm_head(&pfb->tags, 0, 1, tags, tags, 1, &tile->tag)) {
+ if (!(flags & 2)) tile->zcomp = 0x00100000; /* Z16 */
+ else tile->zcomp = 0x00200000; /* Z24S8 */
+ tile->zcomp |= tile->tag->offset;
+#ifdef __BIG_ENDIAN
+ tile->zcomp |= 0x01000000;
+#endif
+ }
+}
+
+struct nvkm_oclass *
+nv25_fb_oclass = &(struct nv04_fb_impl) {
+ .base.base.handle = NV_SUBDEV(FB, 0x25),
+ .base.base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv04_fb_ctor,
+ .dtor = _nvkm_fb_dtor,
+ .init = _nvkm_fb_init,
+ .fini = _nvkm_fb_fini,
+ },
+ .base.memtype = nv04_fb_memtype_valid,
+ .base.ram = &nv20_ram_oclass,
+ .tile.regions = 8,
+ .tile.init = nv20_fb_tile_init,
+ .tile.comp = nv25_fb_tile_comp,
+ .tile.fini = nv20_fb_tile_fini,
+ .tile.prog = nv20_fb_tile_prog,
+}.base.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv30.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv30.c
new file mode 100644
index 000000000000..09ebb9477e00
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv30.c
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2010 Francisco Jerez.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#include "nv04.h"
+
+#include <core/device.h>
+
+void
+nv30_fb_tile_init(struct nvkm_fb *pfb, int i, u32 addr, u32 size, u32 pitch,
+ u32 flags, struct nvkm_fb_tile *tile)
+{
+ /* for performance, select alternate bank offset for zeta */
+ if (!(flags & 4)) {
+ tile->addr = (0 << 4);
+ } else {
+ if (pfb->tile.comp) /* z compression */
+ pfb->tile.comp(pfb, i, size, flags, tile);
+ tile->addr = (1 << 4);
+ }
+
+ tile->addr |= 0x00000001; /* enable */
+ tile->addr |= addr;
+ tile->limit = max(1u, addr + size) - 1;
+ tile->pitch = pitch;
+}
+
+static void
+nv30_fb_tile_comp(struct nvkm_fb *pfb, int i, u32 size, u32 flags,
+ struct nvkm_fb_tile *tile)
+{
+ u32 tiles = DIV_ROUND_UP(size, 0x40);
+ u32 tags = round_up(tiles / pfb->ram->parts, 0x40);
+ if (!nvkm_mm_head(&pfb->tags, 0, 1, tags, tags, 1, &tile->tag)) {
+ if (flags & 2) tile->zcomp |= 0x01000000; /* Z16 */
+ else tile->zcomp |= 0x02000000; /* Z24S8 */
+ tile->zcomp |= ((tile->tag->offset ) >> 6);
+ tile->zcomp |= ((tile->tag->offset + tags - 1) >> 6) << 12;
+#ifdef __BIG_ENDIAN
+ tile->zcomp |= 0x10000000;
+#endif
+ }
+}
+
+static int
+calc_bias(struct nv04_fb_priv *priv, int k, int i, int j)
+{
+ struct nvkm_device *device = nv_device(priv);
+ int b = (device->chipset > 0x30 ?
+ nv_rd32(priv, 0x122c + 0x10 * k + 0x4 * j) >> (4 * (i ^ 1)) :
+ 0) & 0xf;
+
+ return 2 * (b & 0x8 ? b - 0x10 : b);
+}
+
+static int
+calc_ref(struct nv04_fb_priv *priv, int l, int k, int i)
+{
+ int j, x = 0;
+
+ for (j = 0; j < 4; j++) {
+ int m = (l >> (8 * i) & 0xff) + calc_bias(priv, k, i, j);
+
+ x |= (0x80 | clamp(m, 0, 0x1f)) << (8 * j);
+ }
+
+ return x;
+}
+
+int
+nv30_fb_init(struct nvkm_object *object)
+{
+ struct nvkm_device *device = nv_device(object);
+ struct nv04_fb_priv *priv = (void *)object;
+ int ret, i, j;
+
+ ret = nvkm_fb_init(&priv->base);
+ if (ret)
+ return ret;
+
+ /* Init the memory timing regs at 0x10037c/0x1003ac */
+ if (device->chipset == 0x30 ||
+ device->chipset == 0x31 ||
+ device->chipset == 0x35) {
+ /* Related to ROP count */
+ int n = (device->chipset == 0x31 ? 2 : 4);
+ int l = nv_rd32(priv, 0x1003d0);
+
+ for (i = 0; i < n; i++) {
+ for (j = 0; j < 3; j++)
+ nv_wr32(priv, 0x10037c + 0xc * i + 0x4 * j,
+ calc_ref(priv, l, 0, j));
+
+ for (j = 0; j < 2; j++)
+ nv_wr32(priv, 0x1003ac + 0x8 * i + 0x4 * j,
+ calc_ref(priv, l, 1, j));
+ }
+ }
+
+ return 0;
+}
+
+struct nvkm_oclass *
+nv30_fb_oclass = &(struct nv04_fb_impl) {
+ .base.base.handle = NV_SUBDEV(FB, 0x30),
+ .base.base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv04_fb_ctor,
+ .dtor = _nvkm_fb_dtor,
+ .init = nv30_fb_init,
+ .fini = _nvkm_fb_fini,
+ },
+ .base.memtype = nv04_fb_memtype_valid,
+ .base.ram = &nv20_ram_oclass,
+ .tile.regions = 8,
+ .tile.init = nv30_fb_tile_init,
+ .tile.comp = nv30_fb_tile_comp,
+ .tile.fini = nv20_fb_tile_fini,
+ .tile.prog = nv20_fb_tile_prog,
+}.base.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv35.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv35.c
new file mode 100644
index 000000000000..c01dc1839ea4
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv35.c
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2010 Francisco Jerez.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#include "nv04.h"
+
+static void
+nv35_fb_tile_comp(struct nvkm_fb *pfb, int i, u32 size, u32 flags,
+ struct nvkm_fb_tile *tile)
+{
+ u32 tiles = DIV_ROUND_UP(size, 0x40);
+ u32 tags = round_up(tiles / pfb->ram->parts, 0x40);
+ if (!nvkm_mm_head(&pfb->tags, 0, 1, tags, tags, 1, &tile->tag)) {
+ if (flags & 2) tile->zcomp |= 0x04000000; /* Z16 */
+ else tile->zcomp |= 0x08000000; /* Z24S8 */
+ tile->zcomp |= ((tile->tag->offset ) >> 6);
+ tile->zcomp |= ((tile->tag->offset + tags - 1) >> 6) << 13;
+#ifdef __BIG_ENDIAN
+ tile->zcomp |= 0x40000000;
+#endif
+ }
+}
+
+struct nvkm_oclass *
+nv35_fb_oclass = &(struct nv04_fb_impl) {
+ .base.base.handle = NV_SUBDEV(FB, 0x35),
+ .base.base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv04_fb_ctor,
+ .dtor = _nvkm_fb_dtor,
+ .init = nv30_fb_init,
+ .fini = _nvkm_fb_fini,
+ },
+ .base.memtype = nv04_fb_memtype_valid,
+ .base.ram = &nv20_ram_oclass,
+ .tile.regions = 8,
+ .tile.init = nv30_fb_tile_init,
+ .tile.comp = nv35_fb_tile_comp,
+ .tile.fini = nv20_fb_tile_fini,
+ .tile.prog = nv20_fb_tile_prog,
+}.base.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv36.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv36.c
new file mode 100644
index 000000000000..cad75a1cef22
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv36.c
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2010 Francisco Jerez.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#include "nv04.h"
+
+static void
+nv36_fb_tile_comp(struct nvkm_fb *pfb, int i, u32 size, u32 flags,
+ struct nvkm_fb_tile *tile)
+{
+ u32 tiles = DIV_ROUND_UP(size, 0x40);
+ u32 tags = round_up(tiles / pfb->ram->parts, 0x40);
+ if (!nvkm_mm_head(&pfb->tags, 0, 1, tags, tags, 1, &tile->tag)) {
+ if (flags & 2) tile->zcomp |= 0x10000000; /* Z16 */
+ else tile->zcomp |= 0x20000000; /* Z24S8 */
+ tile->zcomp |= ((tile->tag->offset ) >> 6);
+ tile->zcomp |= ((tile->tag->offset + tags - 1) >> 6) << 14;
+#ifdef __BIG_ENDIAN
+ tile->zcomp |= 0x80000000;
+#endif
+ }
+}
+
+struct nvkm_oclass *
+nv36_fb_oclass = &(struct nv04_fb_impl) {
+ .base.base.handle = NV_SUBDEV(FB, 0x36),
+ .base.base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv04_fb_ctor,
+ .dtor = _nvkm_fb_dtor,
+ .init = nv30_fb_init,
+ .fini = _nvkm_fb_fini,
+ },
+ .base.memtype = nv04_fb_memtype_valid,
+ .base.ram = &nv20_ram_oclass,
+ .tile.regions = 8,
+ .tile.init = nv30_fb_tile_init,
+ .tile.comp = nv36_fb_tile_comp,
+ .tile.fini = nv20_fb_tile_fini,
+ .tile.prog = nv20_fb_tile_prog,
+}.base.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv40.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv40.c
new file mode 100644
index 000000000000..dbe5c1910c2c
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv40.c
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2010 Francisco Jerez.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#include "nv04.h"
+
+void
+nv40_fb_tile_comp(struct nvkm_fb *pfb, int i, u32 size, u32 flags,
+ struct nvkm_fb_tile *tile)
+{
+ u32 tiles = DIV_ROUND_UP(size, 0x80);
+ u32 tags = round_up(tiles / pfb->ram->parts, 0x100);
+ if ( (flags & 2) &&
+ !nvkm_mm_head(&pfb->tags, 0, 1, tags, tags, 1, &tile->tag)) {
+ tile->zcomp = 0x28000000; /* Z24S8_SPLIT_GRAD */
+ tile->zcomp |= ((tile->tag->offset ) >> 8);
+ tile->zcomp |= ((tile->tag->offset + tags - 1) >> 8) << 13;
+#ifdef __BIG_ENDIAN
+ tile->zcomp |= 0x40000000;
+#endif
+ }
+}
+
+static int
+nv40_fb_init(struct nvkm_object *object)
+{
+ struct nv04_fb_priv *priv = (void *)object;
+ int ret;
+
+ ret = nvkm_fb_init(&priv->base);
+ if (ret)
+ return ret;
+
+ nv_mask(priv, 0x10033c, 0x00008000, 0x00000000);
+ return 0;
+}
+
+struct nvkm_oclass *
+nv40_fb_oclass = &(struct nv04_fb_impl) {
+ .base.base.handle = NV_SUBDEV(FB, 0x40),
+ .base.base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv04_fb_ctor,
+ .dtor = _nvkm_fb_dtor,
+ .init = nv40_fb_init,
+ .fini = _nvkm_fb_fini,
+ },
+ .base.memtype = nv04_fb_memtype_valid,
+ .base.ram = &nv40_ram_oclass,
+ .tile.regions = 8,
+ .tile.init = nv30_fb_tile_init,
+ .tile.comp = nv40_fb_tile_comp,
+ .tile.fini = nv20_fb_tile_fini,
+ .tile.prog = nv20_fb_tile_prog,
+}.base.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv40.h b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv40.h
new file mode 100644
index 000000000000..602182661820
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv40.h
@@ -0,0 +1,14 @@
+#ifndef __NVKM_FB_NV40_H__
+#define __NVKM_FB_NV40_H__
+#include "priv.h"
+
+struct nv40_ram {
+ struct nvkm_ram base;
+ u32 ctrl;
+ u32 coef;
+};
+
+int nv40_ram_calc(struct nvkm_fb *, u32);
+int nv40_ram_prog(struct nvkm_fb *);
+void nv40_ram_tidy(struct nvkm_fb *);
+#endif
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv41.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv41.c
new file mode 100644
index 000000000000..d9e1a40a2955
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv41.c
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2010 Francisco Jerez.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#include "nv04.h"
+
+void
+nv41_fb_tile_prog(struct nvkm_fb *pfb, int i, struct nvkm_fb_tile *tile)
+{
+ nv_wr32(pfb, 0x100604 + (i * 0x10), tile->limit);
+ nv_wr32(pfb, 0x100608 + (i * 0x10), tile->pitch);
+ nv_wr32(pfb, 0x100600 + (i * 0x10), tile->addr);
+ nv_rd32(pfb, 0x100600 + (i * 0x10));
+ nv_wr32(pfb, 0x100700 + (i * 0x04), tile->zcomp);
+}
+
+int
+nv41_fb_init(struct nvkm_object *object)
+{
+ struct nv04_fb_priv *priv = (void *)object;
+ int ret;
+
+ ret = nvkm_fb_init(&priv->base);
+ if (ret)
+ return ret;
+
+ nv_wr32(priv, 0x100800, 0x00000001);
+ return 0;
+}
+
+struct nvkm_oclass *
+nv41_fb_oclass = &(struct nv04_fb_impl) {
+ .base.base.handle = NV_SUBDEV(FB, 0x41),
+ .base.base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv04_fb_ctor,
+ .dtor = _nvkm_fb_dtor,
+ .init = nv41_fb_init,
+ .fini = _nvkm_fb_fini,
+ },
+ .base.memtype = nv04_fb_memtype_valid,
+ .base.ram = &nv41_ram_oclass,
+ .tile.regions = 12,
+ .tile.init = nv30_fb_tile_init,
+ .tile.comp = nv40_fb_tile_comp,
+ .tile.fini = nv20_fb_tile_fini,
+ .tile.prog = nv41_fb_tile_prog,
+}.base.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv44.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv44.c
new file mode 100644
index 000000000000..20b97c83c4af
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv44.c
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2010 Francisco Jerez.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#include "nv04.h"
+
+static void
+nv44_fb_tile_init(struct nvkm_fb *pfb, int i, u32 addr, u32 size, u32 pitch,
+ u32 flags, struct nvkm_fb_tile *tile)
+{
+ tile->addr = 0x00000001; /* mode = vram */
+ tile->addr |= addr;
+ tile->limit = max(1u, addr + size) - 1;
+ tile->pitch = pitch;
+}
+
+void
+nv44_fb_tile_prog(struct nvkm_fb *pfb, int i, struct nvkm_fb_tile *tile)
+{
+ nv_wr32(pfb, 0x100604 + (i * 0x10), tile->limit);
+ nv_wr32(pfb, 0x100608 + (i * 0x10), tile->pitch);
+ nv_wr32(pfb, 0x100600 + (i * 0x10), tile->addr);
+ nv_rd32(pfb, 0x100600 + (i * 0x10));
+}
+
+int
+nv44_fb_init(struct nvkm_object *object)
+{
+ struct nv04_fb_priv *priv = (void *)object;
+ int ret;
+
+ ret = nvkm_fb_init(&priv->base);
+ if (ret)
+ return ret;
+
+ nv_wr32(priv, 0x100850, 0x80000000);
+ nv_wr32(priv, 0x100800, 0x00000001);
+ return 0;
+}
+
+struct nvkm_oclass *
+nv44_fb_oclass = &(struct nv04_fb_impl) {
+ .base.base.handle = NV_SUBDEV(FB, 0x44),
+ .base.base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv04_fb_ctor,
+ .dtor = _nvkm_fb_dtor,
+ .init = nv44_fb_init,
+ .fini = _nvkm_fb_fini,
+ },
+ .base.memtype = nv04_fb_memtype_valid,
+ .base.ram = &nv44_ram_oclass,
+ .tile.regions = 12,
+ .tile.init = nv44_fb_tile_init,
+ .tile.fini = nv20_fb_tile_fini,
+ .tile.prog = nv44_fb_tile_prog,
+}.base.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv46.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv46.c
new file mode 100644
index 000000000000..5bfac38cdf24
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv46.c
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2010 Francisco Jerez.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#include "nv04.h"
+
+void
+nv46_fb_tile_init(struct nvkm_fb *pfb, int i, u32 addr, u32 size, u32 pitch,
+ u32 flags, struct nvkm_fb_tile *tile)
+{
+ /* for performance, select alternate bank offset for zeta */
+ if (!(flags & 4)) tile->addr = (0 << 3);
+ else tile->addr = (1 << 3);
+
+ tile->addr |= 0x00000001; /* mode = vram */
+ tile->addr |= addr;
+ tile->limit = max(1u, addr + size) - 1;
+ tile->pitch = pitch;
+}
+
+struct nvkm_oclass *
+nv46_fb_oclass = &(struct nv04_fb_impl) {
+ .base.base.handle = NV_SUBDEV(FB, 0x46),
+ .base.base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv04_fb_ctor,
+ .dtor = _nvkm_fb_dtor,
+ .init = nv44_fb_init,
+ .fini = _nvkm_fb_fini,
+ },
+ .base.memtype = nv04_fb_memtype_valid,
+ .base.ram = &nv44_ram_oclass,
+ .tile.regions = 15,
+ .tile.init = nv46_fb_tile_init,
+ .tile.fini = nv20_fb_tile_fini,
+ .tile.prog = nv44_fb_tile_prog,
+}.base.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv47.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv47.c
new file mode 100644
index 000000000000..d3b3988d1d49
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv47.c
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2010 Francisco Jerez.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#include "nv04.h"
+
+struct nvkm_oclass *
+nv47_fb_oclass = &(struct nv04_fb_impl) {
+ .base.base.handle = NV_SUBDEV(FB, 0x47),
+ .base.base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv04_fb_ctor,
+ .dtor = _nvkm_fb_dtor,
+ .init = nv41_fb_init,
+ .fini = _nvkm_fb_fini,
+ },
+ .base.memtype = nv04_fb_memtype_valid,
+ .base.ram = &nv41_ram_oclass,
+ .tile.regions = 15,
+ .tile.init = nv30_fb_tile_init,
+ .tile.comp = nv40_fb_tile_comp,
+ .tile.fini = nv20_fb_tile_fini,
+ .tile.prog = nv41_fb_tile_prog,
+}.base.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv49.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv49.c
new file mode 100644
index 000000000000..236e36c5054e
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv49.c
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2010 Francisco Jerez.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#include "nv04.h"
+
+struct nvkm_oclass *
+nv49_fb_oclass = &(struct nv04_fb_impl) {
+ .base.base.handle = NV_SUBDEV(FB, 0x49),
+ .base.base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv04_fb_ctor,
+ .dtor = _nvkm_fb_dtor,
+ .init = nv41_fb_init,
+ .fini = _nvkm_fb_fini,
+ },
+ .base.memtype = nv04_fb_memtype_valid,
+ .base.ram = &nv49_ram_oclass,
+ .tile.regions = 15,
+ .tile.init = nv30_fb_tile_init,
+ .tile.comp = nv40_fb_tile_comp,
+ .tile.fini = nv20_fb_tile_fini,
+ .tile.prog = nv41_fb_tile_prog,
+}.base.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv4e.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv4e.c
new file mode 100644
index 000000000000..1352b6a73fb0
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv4e.c
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2010 Francisco Jerez.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#include "nv04.h"
+
+struct nvkm_oclass *
+nv4e_fb_oclass = &(struct nv04_fb_impl) {
+ .base.base.handle = NV_SUBDEV(FB, 0x4e),
+ .base.base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv04_fb_ctor,
+ .dtor = _nvkm_fb_dtor,
+ .init = nv44_fb_init,
+ .fini = _nvkm_fb_fini,
+ },
+ .base.memtype = nv04_fb_memtype_valid,
+ .base.ram = &nv4e_ram_oclass,
+ .tile.regions = 12,
+ .tile.init = nv46_fb_tile_init,
+ .tile.fini = nv20_fb_tile_fini,
+ .tile.prog = nv44_fb_tile_prog,
+}.base.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv50.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv50.c
new file mode 100644
index 000000000000..0480ce52aa06
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv50.c
@@ -0,0 +1,320 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "nv50.h"
+
+#include <core/client.h>
+#include <core/device.h>
+#include <core/engctx.h>
+#include <core/enum.h>
+
+int
+nv50_fb_memtype[0x80] = {
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 1, 1, 1, 0, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0,
+ 1, 1, 1, 1, 1, 1, 1, 0, 2, 2, 2, 2, 2, 2, 2, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 0, 0,
+ 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 2, 2, 2, 2,
+ 1, 0, 2, 0, 1, 0, 2, 0, 1, 1, 2, 2, 1, 1, 0, 0
+};
+
+bool
+nv50_fb_memtype_valid(struct nvkm_fb *pfb, u32 memtype)
+{
+ return nv50_fb_memtype[(memtype & 0xff00) >> 8] != 0;
+}
+
+static const struct nvkm_enum vm_dispatch_subclients[] = {
+ { 0x00000000, "GRCTX", NULL },
+ { 0x00000001, "NOTIFY", NULL },
+ { 0x00000002, "QUERY", NULL },
+ { 0x00000003, "COND", NULL },
+ { 0x00000004, "M2M_IN", NULL },
+ { 0x00000005, "M2M_OUT", NULL },
+ { 0x00000006, "M2M_NOTIFY", NULL },
+ {}
+};
+
+static const struct nvkm_enum vm_ccache_subclients[] = {
+ { 0x00000000, "CB", NULL },
+ { 0x00000001, "TIC", NULL },
+ { 0x00000002, "TSC", NULL },
+ {}
+};
+
+static const struct nvkm_enum vm_prop_subclients[] = {
+ { 0x00000000, "RT0", NULL },
+ { 0x00000001, "RT1", NULL },
+ { 0x00000002, "RT2", NULL },
+ { 0x00000003, "RT3", NULL },
+ { 0x00000004, "RT4", NULL },
+ { 0x00000005, "RT5", NULL },
+ { 0x00000006, "RT6", NULL },
+ { 0x00000007, "RT7", NULL },
+ { 0x00000008, "ZETA", NULL },
+ { 0x00000009, "LOCAL", NULL },
+ { 0x0000000a, "GLOBAL", NULL },
+ { 0x0000000b, "STACK", NULL },
+ { 0x0000000c, "DST2D", NULL },
+ {}
+};
+
+static const struct nvkm_enum vm_pfifo_subclients[] = {
+ { 0x00000000, "PUSHBUF", NULL },
+ { 0x00000001, "SEMAPHORE", NULL },
+ {}
+};
+
+static const struct nvkm_enum vm_bar_subclients[] = {
+ { 0x00000000, "FB", NULL },
+ { 0x00000001, "IN", NULL },
+ {}
+};
+
+static const struct nvkm_enum vm_client[] = {
+ { 0x00000000, "STRMOUT", NULL },
+ { 0x00000003, "DISPATCH", vm_dispatch_subclients },
+ { 0x00000004, "PFIFO_WRITE", NULL },
+ { 0x00000005, "CCACHE", vm_ccache_subclients },
+ { 0x00000006, "PMSPPP", NULL },
+ { 0x00000007, "CLIPID", NULL },
+ { 0x00000008, "PFIFO_READ", NULL },
+ { 0x00000009, "VFETCH", NULL },
+ { 0x0000000a, "TEXTURE", NULL },
+ { 0x0000000b, "PROP", vm_prop_subclients },
+ { 0x0000000c, "PVP", NULL },
+ { 0x0000000d, "PBSP", NULL },
+ { 0x0000000e, "PCRYPT", NULL },
+ { 0x0000000f, "PCOUNTER", NULL },
+ { 0x00000011, "PDAEMON", NULL },
+ {}
+};
+
+static const struct nvkm_enum vm_engine[] = {
+ { 0x00000000, "PGRAPH", NULL, NVDEV_ENGINE_GR },
+ { 0x00000001, "PVP", NULL, NVDEV_ENGINE_VP },
+ { 0x00000004, "PEEPHOLE", NULL },
+ { 0x00000005, "PFIFO", vm_pfifo_subclients, NVDEV_ENGINE_FIFO },
+ { 0x00000006, "BAR", vm_bar_subclients },
+ { 0x00000008, "PMSPPP", NULL, NVDEV_ENGINE_MSPPP },
+ { 0x00000008, "PMPEG", NULL, NVDEV_ENGINE_MPEG },
+ { 0x00000009, "PBSP", NULL, NVDEV_ENGINE_BSP },
+ { 0x0000000a, "PCRYPT", NULL, NVDEV_ENGINE_CIPHER },
+ { 0x0000000b, "PCOUNTER", NULL },
+ { 0x0000000c, "SEMAPHORE_BG", NULL },
+ { 0x0000000d, "PCE0", NULL, NVDEV_ENGINE_CE0 },
+ { 0x0000000e, "PDAEMON", NULL },
+ {}
+};
+
+static const struct nvkm_enum vm_fault[] = {
+ { 0x00000000, "PT_NOT_PRESENT", NULL },
+ { 0x00000001, "PT_TOO_SHORT", NULL },
+ { 0x00000002, "PAGE_NOT_PRESENT", NULL },
+ { 0x00000003, "PAGE_SYSTEM_ONLY", NULL },
+ { 0x00000004, "PAGE_READ_ONLY", NULL },
+ { 0x00000006, "NULL_DMAOBJ", NULL },
+ { 0x00000007, "WRONG_MEMTYPE", NULL },
+ { 0x0000000b, "VRAM_LIMIT", NULL },
+ { 0x0000000f, "DMAOBJ_LIMIT", NULL },
+ {}
+};
+
+static void
+nv50_fb_intr(struct nvkm_subdev *subdev)
+{
+ struct nvkm_device *device = nv_device(subdev);
+ struct nvkm_engine *engine;
+ struct nv50_fb_priv *priv = (void *)subdev;
+ const struct nvkm_enum *en, *cl;
+ struct nvkm_object *engctx = NULL;
+ u32 trap[6], idx, chan;
+ u8 st0, st1, st2, st3;
+ int i;
+
+ idx = nv_rd32(priv, 0x100c90);
+ if (!(idx & 0x80000000))
+ return;
+ idx &= 0x00ffffff;
+
+ for (i = 0; i < 6; i++) {
+ nv_wr32(priv, 0x100c90, idx | i << 24);
+ trap[i] = nv_rd32(priv, 0x100c94);
+ }
+ nv_wr32(priv, 0x100c90, idx | 0x80000000);
+
+ /* decode status bits into something more useful */
+ if (device->chipset < 0xa3 ||
+ device->chipset == 0xaa || device->chipset == 0xac) {
+ st0 = (trap[0] & 0x0000000f) >> 0;
+ st1 = (trap[0] & 0x000000f0) >> 4;
+ st2 = (trap[0] & 0x00000f00) >> 8;
+ st3 = (trap[0] & 0x0000f000) >> 12;
+ } else {
+ st0 = (trap[0] & 0x000000ff) >> 0;
+ st1 = (trap[0] & 0x0000ff00) >> 8;
+ st2 = (trap[0] & 0x00ff0000) >> 16;
+ st3 = (trap[0] & 0xff000000) >> 24;
+ }
+ chan = (trap[2] << 16) | trap[1];
+
+ en = nvkm_enum_find(vm_engine, st0);
+
+ if (en && en->data2) {
+ const struct nvkm_enum *orig_en = en;
+ while (en->name && en->value == st0 && en->data2) {
+ engine = nvkm_engine(subdev, en->data2);
+ /*XXX: clean this up */
+ if (!engine && en->data2 == NVDEV_ENGINE_BSP)
+ engine = nvkm_engine(subdev, NVDEV_ENGINE_MSVLD);
+ if (!engine && en->data2 == NVDEV_ENGINE_CIPHER)
+ engine = nvkm_engine(subdev, NVDEV_ENGINE_SEC);
+ if (!engine && en->data2 == NVDEV_ENGINE_VP)
+ engine = nvkm_engine(subdev, NVDEV_ENGINE_MSPDEC);
+ if (engine) {
+ engctx = nvkm_engctx_get(engine, chan);
+ if (engctx)
+ break;
+ }
+ en++;
+ }
+ if (!engctx)
+ en = orig_en;
+ }
+
+ nv_error(priv, "trapped %s at 0x%02x%04x%04x on channel 0x%08x [%s] ",
+ (trap[5] & 0x00000100) ? "read" : "write",
+ trap[5] & 0xff, trap[4] & 0xffff, trap[3] & 0xffff, chan,
+ nvkm_client_name(engctx));
+
+ nvkm_engctx_put(engctx);
+
+ if (en)
+ pr_cont("%s/", en->name);
+ else
+ pr_cont("%02x/", st0);
+
+ cl = nvkm_enum_find(vm_client, st2);
+ if (cl)
+ pr_cont("%s/", cl->name);
+ else
+ pr_cont("%02x/", st2);
+
+ if (cl && cl->data) cl = nvkm_enum_find(cl->data, st3);
+ else if (en && en->data) cl = nvkm_enum_find(en->data, st3);
+ else cl = NULL;
+ if (cl)
+ pr_cont("%s", cl->name);
+ else
+ pr_cont("%02x", st3);
+
+ pr_cont(" reason: ");
+ en = nvkm_enum_find(vm_fault, st1);
+ if (en)
+ pr_cont("%s\n", en->name);
+ else
+ pr_cont("0x%08x\n", st1);
+}
+
+int
+nv50_fb_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 size,
+ struct nvkm_object **pobject)
+{
+ struct nvkm_device *device = nv_device(parent);
+ struct nv50_fb_priv *priv;
+ int ret;
+
+ ret = nvkm_fb_create(parent, engine, oclass, &priv);
+ *pobject = nv_object(priv);
+ if (ret)
+ return ret;
+
+ priv->r100c08_page = alloc_page(GFP_KERNEL | __GFP_ZERO);
+ if (priv->r100c08_page) {
+ priv->r100c08 = dma_map_page(nv_device_base(device),
+ priv->r100c08_page, 0, PAGE_SIZE,
+ DMA_BIDIRECTIONAL);
+ if (dma_mapping_error(nv_device_base(device), priv->r100c08))
+ return -EFAULT;
+ } else {
+ nv_warn(priv, "failed 0x100c08 page alloc\n");
+ }
+
+ nv_subdev(priv)->intr = nv50_fb_intr;
+ return 0;
+}
+
+void
+nv50_fb_dtor(struct nvkm_object *object)
+{
+ struct nvkm_device *device = nv_device(object);
+ struct nv50_fb_priv *priv = (void *)object;
+
+ if (priv->r100c08_page) {
+ dma_unmap_page(nv_device_base(device), priv->r100c08, PAGE_SIZE,
+ DMA_BIDIRECTIONAL);
+ __free_page(priv->r100c08_page);
+ }
+
+ nvkm_fb_destroy(&priv->base);
+}
+
+int
+nv50_fb_init(struct nvkm_object *object)
+{
+ struct nv50_fb_impl *impl = (void *)object->oclass;
+ struct nv50_fb_priv *priv = (void *)object;
+ int ret;
+
+ ret = nvkm_fb_init(&priv->base);
+ if (ret)
+ return ret;
+
+ /* Not a clue what this is exactly. Without pointing it at a
+ * scratch page, VRAM->GART blits with M2MF (as in DDX DFS)
+ * cause IOMMU "read from address 0" errors (rh#561267)
+ */
+ nv_wr32(priv, 0x100c08, priv->r100c08 >> 8);
+
+ /* This is needed to get meaningful information from 100c90
+ * on traps. No idea what these values mean exactly. */
+ nv_wr32(priv, 0x100c90, impl->trap);
+ return 0;
+}
+
+struct nvkm_oclass *
+nv50_fb_oclass = &(struct nv50_fb_impl) {
+ .base.base.handle = NV_SUBDEV(FB, 0x50),
+ .base.base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv50_fb_ctor,
+ .dtor = nv50_fb_dtor,
+ .init = nv50_fb_init,
+ .fini = _nvkm_fb_fini,
+ },
+ .base.memtype = nv50_fb_memtype_valid,
+ .base.ram = &nv50_ram_oclass,
+ .trap = 0x000707ff,
+}.base.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv50.h b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv50.h
new file mode 100644
index 000000000000..f3cde3f1f511
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv50.h
@@ -0,0 +1,31 @@
+#ifndef __NVKM_FB_NV50_H__
+#define __NVKM_FB_NV50_H__
+#include "priv.h"
+
+struct nv50_fb_priv {
+ struct nvkm_fb base;
+ struct page *r100c08_page;
+ dma_addr_t r100c08;
+};
+
+int nv50_fb_ctor(struct nvkm_object *, struct nvkm_object *,
+ struct nvkm_oclass *, void *, u32,
+ struct nvkm_object **);
+void nv50_fb_dtor(struct nvkm_object *);
+int nv50_fb_init(struct nvkm_object *);
+
+struct nv50_fb_impl {
+ struct nvkm_fb_impl base;
+ u32 trap;
+};
+
+#define nv50_ram_create(p,e,o,d) \
+ nv50_ram_create_((p), (e), (o), sizeof(**d), (void **)d)
+int nv50_ram_create_(struct nvkm_object *, struct nvkm_object *,
+ struct nvkm_oclass *, int, void **);
+int nv50_ram_get(struct nvkm_fb *, u64 size, u32 align, u32 ncmin,
+ u32 memtype, struct nvkm_mem **);
+void nv50_ram_put(struct nvkm_fb *, struct nvkm_mem **);
+void __nv50_ram_put(struct nvkm_fb *, struct nvkm_mem *);
+extern int nv50_fb_memtype[0x80];
+#endif
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/priv.h b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/priv.h
new file mode 100644
index 000000000000..d82da02daa1f
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/priv.h
@@ -0,0 +1,74 @@
+#ifndef __NVKM_FB_PRIV_H__
+#define __NVKM_FB_PRIV_H__
+#include <subdev/fb.h>
+struct nvkm_bios;
+
+#define nvkm_ram_create(p,e,o,d) \
+ nvkm_object_create_((p), (e), (o), 0, sizeof(**d), (void **)d)
+#define nvkm_ram_destroy(p) \
+ nvkm_object_destroy(&(p)->base)
+#define nvkm_ram_init(p) \
+ nvkm_object_init(&(p)->base)
+#define nvkm_ram_fini(p,s) \
+ nvkm_object_fini(&(p)->base, (s))
+
+#define nvkm_ram_create_(p,e,o,s,d) \
+ nvkm_object_create_((p), (e), (o), 0, (s), (void **)d)
+#define _nvkm_ram_dtor nvkm_object_destroy
+#define _nvkm_ram_init nvkm_object_init
+#define _nvkm_ram_fini nvkm_object_fini
+
+extern struct nvkm_oclass nv04_ram_oclass;
+extern struct nvkm_oclass nv10_ram_oclass;
+extern struct nvkm_oclass nv1a_ram_oclass;
+extern struct nvkm_oclass nv20_ram_oclass;
+extern struct nvkm_oclass nv40_ram_oclass;
+extern struct nvkm_oclass nv41_ram_oclass;
+extern struct nvkm_oclass nv44_ram_oclass;
+extern struct nvkm_oclass nv49_ram_oclass;
+extern struct nvkm_oclass nv4e_ram_oclass;
+extern struct nvkm_oclass nv50_ram_oclass;
+extern struct nvkm_oclass gt215_ram_oclass;
+extern struct nvkm_oclass mcp77_ram_oclass;
+extern struct nvkm_oclass gf100_ram_oclass;
+extern struct nvkm_oclass gk104_ram_oclass;
+extern struct nvkm_oclass gk20a_ram_oclass;
+extern struct nvkm_oclass gm107_ram_oclass;
+
+int nvkm_sddr2_calc(struct nvkm_ram *ram);
+int nvkm_sddr3_calc(struct nvkm_ram *ram);
+int nvkm_gddr3_calc(struct nvkm_ram *ram);
+int nvkm_gddr5_calc(struct nvkm_ram *ram, bool nuts);
+
+#define nvkm_fb_create(p,e,c,d) \
+ nvkm_fb_create_((p), (e), (c), sizeof(**d), (void **)d)
+#define nvkm_fb_destroy(p) ({ \
+ struct nvkm_fb *pfb = (p); \
+ _nvkm_fb_dtor(nv_object(pfb)); \
+})
+#define nvkm_fb_init(p) ({ \
+ struct nvkm_fb *pfb = (p); \
+ _nvkm_fb_init(nv_object(pfb)); \
+})
+#define nvkm_fb_fini(p,s) ({ \
+ struct nvkm_fb *pfb = (p); \
+ _nvkm_fb_fini(nv_object(pfb), (s)); \
+})
+
+int nvkm_fb_create_(struct nvkm_object *, struct nvkm_object *,
+ struct nvkm_oclass *, int, void **);
+void _nvkm_fb_dtor(struct nvkm_object *);
+int _nvkm_fb_init(struct nvkm_object *);
+int _nvkm_fb_fini(struct nvkm_object *, bool);
+
+struct nvkm_fb_impl {
+ struct nvkm_oclass base;
+ struct nvkm_oclass *ram;
+ bool (*memtype)(struct nvkm_fb *, u32);
+};
+
+bool nv04_fb_memtype_valid(struct nvkm_fb *, u32 memtype);
+bool nv50_fb_memtype_valid(struct nvkm_fb *, u32 memtype);
+
+int nvkm_fb_bios_memtype(struct nvkm_bios *);
+#endif
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramfuc.h b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramfuc.h
new file mode 100644
index 000000000000..f343682b1387
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramfuc.h
@@ -0,0 +1,180 @@
+#ifndef __NVKM_FBRAM_FUC_H__
+#define __NVKM_FBRAM_FUC_H__
+#include <subdev/pmu.h>
+
+struct ramfuc {
+ struct nvkm_memx *memx;
+ struct nvkm_fb *pfb;
+ int sequence;
+};
+
+struct ramfuc_reg {
+ int sequence;
+ bool force;
+ u32 addr;
+ u32 stride; /* in bytes */
+ u32 mask;
+ u32 data;
+};
+
+static inline struct ramfuc_reg
+ramfuc_stride(u32 addr, u32 stride, u32 mask)
+{
+ return (struct ramfuc_reg) {
+ .sequence = 0,
+ .addr = addr,
+ .stride = stride,
+ .mask = mask,
+ .data = 0xdeadbeef,
+ };
+}
+
+static inline struct ramfuc_reg
+ramfuc_reg2(u32 addr1, u32 addr2)
+{
+ return (struct ramfuc_reg) {
+ .sequence = 0,
+ .addr = addr1,
+ .stride = addr2 - addr1,
+ .mask = 0x3,
+ .data = 0xdeadbeef,
+ };
+}
+
+static noinline struct ramfuc_reg
+ramfuc_reg(u32 addr)
+{
+ return (struct ramfuc_reg) {
+ .sequence = 0,
+ .addr = addr,
+ .stride = 0,
+ .mask = 0x1,
+ .data = 0xdeadbeef,
+ };
+}
+
+static inline int
+ramfuc_init(struct ramfuc *ram, struct nvkm_fb *pfb)
+{
+ struct nvkm_pmu *pmu = nvkm_pmu(pfb);
+ int ret;
+
+ ret = nvkm_memx_init(pmu, &ram->memx);
+ if (ret)
+ return ret;
+
+ ram->sequence++;
+ ram->pfb = pfb;
+ return 0;
+}
+
+static inline int
+ramfuc_exec(struct ramfuc *ram, bool exec)
+{
+ int ret = 0;
+ if (ram->pfb) {
+ ret = nvkm_memx_fini(&ram->memx, exec);
+ ram->pfb = NULL;
+ }
+ return ret;
+}
+
+static inline u32
+ramfuc_rd32(struct ramfuc *ram, struct ramfuc_reg *reg)
+{
+ if (reg->sequence != ram->sequence)
+ reg->data = nv_rd32(ram->pfb, reg->addr);
+ return reg->data;
+}
+
+static inline void
+ramfuc_wr32(struct ramfuc *ram, struct ramfuc_reg *reg, u32 data)
+{
+ unsigned int mask, off = 0;
+
+ reg->sequence = ram->sequence;
+ reg->data = data;
+
+ for (mask = reg->mask; mask > 0; mask = (mask & ~1) >> 1) {
+ if (mask & 1)
+ nvkm_memx_wr32(ram->memx, reg->addr+off, reg->data);
+ off += reg->stride;
+ }
+}
+
+static inline void
+ramfuc_nuke(struct ramfuc *ram, struct ramfuc_reg *reg)
+{
+ reg->force = true;
+}
+
+static inline u32
+ramfuc_mask(struct ramfuc *ram, struct ramfuc_reg *reg, u32 mask, u32 data)
+{
+ u32 temp = ramfuc_rd32(ram, reg);
+ if (temp != ((temp & ~mask) | data) || reg->force) {
+ ramfuc_wr32(ram, reg, (temp & ~mask) | data);
+ reg->force = false;
+ }
+ return temp;
+}
+
+static inline void
+ramfuc_wait(struct ramfuc *ram, u32 addr, u32 mask, u32 data, u32 nsec)
+{
+ nvkm_memx_wait(ram->memx, addr, mask, data, nsec);
+}
+
+static inline void
+ramfuc_nsec(struct ramfuc *ram, u32 nsec)
+{
+ nvkm_memx_nsec(ram->memx, nsec);
+}
+
+static inline void
+ramfuc_wait_vblank(struct ramfuc *ram)
+{
+ nvkm_memx_wait_vblank(ram->memx);
+}
+
+static inline void
+ramfuc_train(struct ramfuc *ram)
+{
+ nvkm_memx_train(ram->memx);
+}
+
+static inline int
+ramfuc_train_result(struct nvkm_fb *pfb, u32 *result, u32 rsize)
+{
+ struct nvkm_pmu *pmu = nvkm_pmu(pfb);
+
+ return nvkm_memx_train_result(pmu, result, rsize);
+}
+
+static inline void
+ramfuc_block(struct ramfuc *ram)
+{
+ nvkm_memx_block(ram->memx);
+}
+
+static inline void
+ramfuc_unblock(struct ramfuc *ram)
+{
+ nvkm_memx_unblock(ram->memx);
+}
+
+#define ram_init(s,p) ramfuc_init(&(s)->base, (p))
+#define ram_exec(s,e) ramfuc_exec(&(s)->base, (e))
+#define ram_have(s,r) ((s)->r_##r.addr != 0x000000)
+#define ram_rd32(s,r) ramfuc_rd32(&(s)->base, &(s)->r_##r)
+#define ram_wr32(s,r,d) ramfuc_wr32(&(s)->base, &(s)->r_##r, (d))
+#define ram_nuke(s,r) ramfuc_nuke(&(s)->base, &(s)->r_##r)
+#define ram_mask(s,r,m,d) ramfuc_mask(&(s)->base, &(s)->r_##r, (m), (d))
+#define ram_wait(s,r,m,d,n) ramfuc_wait(&(s)->base, (r), (m), (d), (n))
+#define ram_nsec(s,n) ramfuc_nsec(&(s)->base, (n))
+#define ram_wait_vblank(s) ramfuc_wait_vblank(&(s)->base)
+#define ram_train(s) ramfuc_train(&(s)->base)
+#define ram_train_result(s,r,l) ramfuc_train_result((s), (r), (l))
+#define ram_block(s) ramfuc_block(&(s)->base)
+#define ram_unblock(s) ramfuc_unblock(&(s)->base)
+#endif
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgf100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgf100.c
new file mode 100644
index 000000000000..de9f39569943
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgf100.c
@@ -0,0 +1,731 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "gf100.h"
+#include "ramfuc.h"
+
+#include <core/device.h>
+#include <core/option.h>
+#include <subdev/bios.h>
+#include <subdev/bios/pll.h>
+#include <subdev/bios/rammap.h>
+#include <subdev/bios/timing.h>
+#include <subdev/clk.h>
+#include <subdev/clk/pll.h>
+#include <subdev/ltc.h>
+
+struct gf100_ramfuc {
+ struct ramfuc base;
+
+ struct ramfuc_reg r_0x10fe20;
+ struct ramfuc_reg r_0x10fe24;
+ struct ramfuc_reg r_0x137320;
+ struct ramfuc_reg r_0x137330;
+
+ struct ramfuc_reg r_0x132000;
+ struct ramfuc_reg r_0x132004;
+ struct ramfuc_reg r_0x132100;
+
+ struct ramfuc_reg r_0x137390;
+
+ struct ramfuc_reg r_0x10f290;
+ struct ramfuc_reg r_0x10f294;
+ struct ramfuc_reg r_0x10f298;
+ struct ramfuc_reg r_0x10f29c;
+ struct ramfuc_reg r_0x10f2a0;
+
+ struct ramfuc_reg r_0x10f300;
+ struct ramfuc_reg r_0x10f338;
+ struct ramfuc_reg r_0x10f340;
+ struct ramfuc_reg r_0x10f344;
+ struct ramfuc_reg r_0x10f348;
+
+ struct ramfuc_reg r_0x10f910;
+ struct ramfuc_reg r_0x10f914;
+
+ struct ramfuc_reg r_0x100b0c;
+ struct ramfuc_reg r_0x10f050;
+ struct ramfuc_reg r_0x10f090;
+ struct ramfuc_reg r_0x10f200;
+ struct ramfuc_reg r_0x10f210;
+ struct ramfuc_reg r_0x10f310;
+ struct ramfuc_reg r_0x10f314;
+ struct ramfuc_reg r_0x10f610;
+ struct ramfuc_reg r_0x10f614;
+ struct ramfuc_reg r_0x10f800;
+ struct ramfuc_reg r_0x10f808;
+ struct ramfuc_reg r_0x10f824;
+ struct ramfuc_reg r_0x10f830;
+ struct ramfuc_reg r_0x10f988;
+ struct ramfuc_reg r_0x10f98c;
+ struct ramfuc_reg r_0x10f990;
+ struct ramfuc_reg r_0x10f998;
+ struct ramfuc_reg r_0x10f9b0;
+ struct ramfuc_reg r_0x10f9b4;
+ struct ramfuc_reg r_0x10fb04;
+ struct ramfuc_reg r_0x10fb08;
+ struct ramfuc_reg r_0x137300;
+ struct ramfuc_reg r_0x137310;
+ struct ramfuc_reg r_0x137360;
+ struct ramfuc_reg r_0x1373ec;
+ struct ramfuc_reg r_0x1373f0;
+ struct ramfuc_reg r_0x1373f8;
+
+ struct ramfuc_reg r_0x61c140;
+ struct ramfuc_reg r_0x611200;
+
+ struct ramfuc_reg r_0x13d8f4;
+};
+
+struct gf100_ram {
+ struct nvkm_ram base;
+ struct gf100_ramfuc fuc;
+ struct nvbios_pll refpll;
+ struct nvbios_pll mempll;
+};
+
+static void
+gf100_ram_train(struct gf100_ramfuc *fuc, u32 magic)
+{
+ struct gf100_ram *ram = container_of(fuc, typeof(*ram), fuc);
+ struct nvkm_fb *pfb = nvkm_fb(ram);
+ u32 part = nv_rd32(pfb, 0x022438), i;
+ u32 mask = nv_rd32(pfb, 0x022554);
+ u32 addr = 0x110974;
+
+ ram_wr32(fuc, 0x10f910, magic);
+ ram_wr32(fuc, 0x10f914, magic);
+
+ for (i = 0; (magic & 0x80000000) && i < part; addr += 0x1000, i++) {
+ if (mask & (1 << i))
+ continue;
+ ram_wait(fuc, addr, 0x0000000f, 0x00000000, 500000);
+ }
+}
+
+static int
+gf100_ram_calc(struct nvkm_fb *pfb, u32 freq)
+{
+ struct nvkm_clk *clk = nvkm_clk(pfb);
+ struct nvkm_bios *bios = nvkm_bios(pfb);
+ struct gf100_ram *ram = (void *)pfb->ram;
+ struct gf100_ramfuc *fuc = &ram->fuc;
+ struct nvbios_ramcfg cfg;
+ u8 ver, cnt, len, strap;
+ struct {
+ u32 data;
+ u8 size;
+ } rammap, ramcfg, timing;
+ int ref, div, out;
+ int from, mode;
+ int N1, M1, P;
+ int ret;
+
+ /* lookup memory config data relevant to the target frequency */
+ rammap.data = nvbios_rammapEm(bios, freq / 1000, &ver, &rammap.size,
+ &cnt, &ramcfg.size, &cfg);
+ if (!rammap.data || ver != 0x10 || rammap.size < 0x0e) {
+ nv_error(pfb, "invalid/missing rammap entry\n");
+ return -EINVAL;
+ }
+
+ /* locate specific data set for the attached memory */
+ strap = nvbios_ramcfg_index(nv_subdev(pfb));
+ if (strap >= cnt) {
+ nv_error(pfb, "invalid ramcfg strap\n");
+ return -EINVAL;
+ }
+
+ ramcfg.data = rammap.data + rammap.size + (strap * ramcfg.size);
+ if (!ramcfg.data || ver != 0x10 || ramcfg.size < 0x0e) {
+ nv_error(pfb, "invalid/missing ramcfg entry\n");
+ return -EINVAL;
+ }
+
+ /* lookup memory timings, if bios says they're present */
+ strap = nv_ro08(bios, ramcfg.data + 0x01);
+ if (strap != 0xff) {
+ timing.data = nvbios_timingEe(bios, strap, &ver, &timing.size,
+ &cnt, &len);
+ if (!timing.data || ver != 0x10 || timing.size < 0x19) {
+ nv_error(pfb, "invalid/missing timing entry\n");
+ return -EINVAL;
+ }
+ } else {
+ timing.data = 0;
+ }
+
+ ret = ram_init(fuc, pfb);
+ if (ret)
+ return ret;
+
+ /* determine current mclk configuration */
+ from = !!(ram_rd32(fuc, 0x1373f0) & 0x00000002); /*XXX: ok? */
+
+ /* determine target mclk configuration */
+ if (!(ram_rd32(fuc, 0x137300) & 0x00000100))
+ ref = clk->read(clk, nv_clk_src_sppll0);
+ else
+ ref = clk->read(clk, nv_clk_src_sppll1);
+ div = max(min((ref * 2) / freq, (u32)65), (u32)2) - 2;
+ out = (ref * 2) / (div + 2);
+ mode = freq != out;
+
+ ram_mask(fuc, 0x137360, 0x00000002, 0x00000000);
+
+ if ((ram_rd32(fuc, 0x132000) & 0x00000002) || 0 /*XXX*/) {
+ ram_nuke(fuc, 0x132000);
+ ram_mask(fuc, 0x132000, 0x00000002, 0x00000002);
+ ram_mask(fuc, 0x132000, 0x00000002, 0x00000000);
+ }
+
+ if (mode == 1) {
+ ram_nuke(fuc, 0x10fe20);
+ ram_mask(fuc, 0x10fe20, 0x00000002, 0x00000002);
+ ram_mask(fuc, 0x10fe20, 0x00000002, 0x00000000);
+ }
+
+// 0x00020034 // 0x0000000a
+ ram_wr32(fuc, 0x132100, 0x00000001);
+
+ if (mode == 1 && from == 0) {
+ /* calculate refpll */
+ ret = gt215_pll_calc(nv_subdev(pfb), &ram->refpll,
+ ram->mempll.refclk, &N1, NULL, &M1, &P);
+ if (ret <= 0) {
+ nv_error(pfb, "unable to calc refpll\n");
+ return ret ? ret : -ERANGE;
+ }
+
+ ram_wr32(fuc, 0x10fe20, 0x20010000);
+ ram_wr32(fuc, 0x137320, 0x00000003);
+ ram_wr32(fuc, 0x137330, 0x81200006);
+ ram_wr32(fuc, 0x10fe24, (P << 16) | (N1 << 8) | M1);
+ ram_wr32(fuc, 0x10fe20, 0x20010001);
+ ram_wait(fuc, 0x137390, 0x00020000, 0x00020000, 64000);
+
+ /* calculate mempll */
+ ret = gt215_pll_calc(nv_subdev(pfb), &ram->mempll, freq,
+ &N1, NULL, &M1, &P);
+ if (ret <= 0) {
+ nv_error(pfb, "unable to calc refpll\n");
+ return ret ? ret : -ERANGE;
+ }
+
+ ram_wr32(fuc, 0x10fe20, 0x20010005);
+ ram_wr32(fuc, 0x132004, (P << 16) | (N1 << 8) | M1);
+ ram_wr32(fuc, 0x132000, 0x18010101);
+ ram_wait(fuc, 0x137390, 0x00000002, 0x00000002, 64000);
+ } else
+ if (mode == 0) {
+ ram_wr32(fuc, 0x137300, 0x00000003);
+ }
+
+ if (from == 0) {
+ ram_nuke(fuc, 0x10fb04);
+ ram_mask(fuc, 0x10fb04, 0x0000ffff, 0x00000000);
+ ram_nuke(fuc, 0x10fb08);
+ ram_mask(fuc, 0x10fb08, 0x0000ffff, 0x00000000);
+ ram_wr32(fuc, 0x10f988, 0x2004ff00);
+ ram_wr32(fuc, 0x10f98c, 0x003fc040);
+ ram_wr32(fuc, 0x10f990, 0x20012001);
+ ram_wr32(fuc, 0x10f998, 0x00011a00);
+ ram_wr32(fuc, 0x13d8f4, 0x00000000);
+ } else {
+ ram_wr32(fuc, 0x10f988, 0x20010000);
+ ram_wr32(fuc, 0x10f98c, 0x00000000);
+ ram_wr32(fuc, 0x10f990, 0x20012001);
+ ram_wr32(fuc, 0x10f998, 0x00010a00);
+ }
+
+ if (from == 0) {
+// 0x00020039 // 0x000000ba
+ }
+
+// 0x0002003a // 0x00000002
+ ram_wr32(fuc, 0x100b0c, 0x00080012);
+// 0x00030014 // 0x00000000 // 0x02b5f070
+// 0x00030014 // 0x00010000 // 0x02b5f070
+ ram_wr32(fuc, 0x611200, 0x00003300);
+// 0x00020034 // 0x0000000a
+// 0x00030020 // 0x00000001 // 0x00000000
+
+ ram_mask(fuc, 0x10f200, 0x00000800, 0x00000000);
+ ram_wr32(fuc, 0x10f210, 0x00000000);
+ ram_nsec(fuc, 1000);
+ if (mode == 0)
+ gf100_ram_train(fuc, 0x000c1001);
+ ram_wr32(fuc, 0x10f310, 0x00000001);
+ ram_nsec(fuc, 1000);
+ ram_wr32(fuc, 0x10f090, 0x00000061);
+ ram_wr32(fuc, 0x10f090, 0xc000007f);
+ ram_nsec(fuc, 1000);
+
+ if (from == 0) {
+ ram_wr32(fuc, 0x10f824, 0x00007fd4);
+ } else {
+ ram_wr32(fuc, 0x1373ec, 0x00020404);
+ }
+
+ if (mode == 0) {
+ ram_mask(fuc, 0x10f808, 0x00080000, 0x00000000);
+ ram_mask(fuc, 0x10f200, 0x00008000, 0x00008000);
+ ram_wr32(fuc, 0x10f830, 0x41500010);
+ ram_mask(fuc, 0x10f830, 0x01000000, 0x00000000);
+ ram_mask(fuc, 0x132100, 0x00000100, 0x00000100);
+ ram_wr32(fuc, 0x10f050, 0xff000090);
+ ram_wr32(fuc, 0x1373ec, 0x00020f0f);
+ ram_wr32(fuc, 0x1373f0, 0x00000003);
+ ram_wr32(fuc, 0x137310, 0x81201616);
+ ram_wr32(fuc, 0x132100, 0x00000001);
+// 0x00020039 // 0x000000ba
+ ram_wr32(fuc, 0x10f830, 0x00300017);
+ ram_wr32(fuc, 0x1373f0, 0x00000001);
+ ram_wr32(fuc, 0x10f824, 0x00007e77);
+ ram_wr32(fuc, 0x132000, 0x18030001);
+ ram_wr32(fuc, 0x10f090, 0x4000007e);
+ ram_nsec(fuc, 2000);
+ ram_wr32(fuc, 0x10f314, 0x00000001);
+ ram_wr32(fuc, 0x10f210, 0x80000000);
+ ram_wr32(fuc, 0x10f338, 0x00300220);
+ ram_wr32(fuc, 0x10f300, 0x0000011d);
+ ram_nsec(fuc, 1000);
+ ram_wr32(fuc, 0x10f290, 0x02060505);
+ ram_wr32(fuc, 0x10f294, 0x34208288);
+ ram_wr32(fuc, 0x10f298, 0x44050411);
+ ram_wr32(fuc, 0x10f29c, 0x0000114c);
+ ram_wr32(fuc, 0x10f2a0, 0x42e10069);
+ ram_wr32(fuc, 0x10f614, 0x40044f77);
+ ram_wr32(fuc, 0x10f610, 0x40044f77);
+ ram_wr32(fuc, 0x10f344, 0x00600009);
+ ram_nsec(fuc, 1000);
+ ram_wr32(fuc, 0x10f348, 0x00700008);
+ ram_wr32(fuc, 0x61c140, 0x19240000);
+ ram_wr32(fuc, 0x10f830, 0x00300017);
+ gf100_ram_train(fuc, 0x80021001);
+ gf100_ram_train(fuc, 0x80081001);
+ ram_wr32(fuc, 0x10f340, 0x00500004);
+ ram_nsec(fuc, 1000);
+ ram_wr32(fuc, 0x10f830, 0x01300017);
+ ram_wr32(fuc, 0x10f830, 0x00300017);
+// 0x00030020 // 0x00000000 // 0x00000000
+// 0x00020034 // 0x0000000b
+ ram_wr32(fuc, 0x100b0c, 0x00080028);
+ ram_wr32(fuc, 0x611200, 0x00003330);
+ } else {
+ ram_wr32(fuc, 0x10f800, 0x00001800);
+ ram_wr32(fuc, 0x13d8f4, 0x00000000);
+ ram_wr32(fuc, 0x1373ec, 0x00020404);
+ ram_wr32(fuc, 0x1373f0, 0x00000003);
+ ram_wr32(fuc, 0x10f830, 0x40700010);
+ ram_wr32(fuc, 0x10f830, 0x40500010);
+ ram_wr32(fuc, 0x13d8f4, 0x00000000);
+ ram_wr32(fuc, 0x1373f8, 0x00000000);
+ ram_wr32(fuc, 0x132100, 0x00000101);
+ ram_wr32(fuc, 0x137310, 0x89201616);
+ ram_wr32(fuc, 0x10f050, 0xff000090);
+ ram_wr32(fuc, 0x1373ec, 0x00030404);
+ ram_wr32(fuc, 0x1373f0, 0x00000002);
+ // 0x00020039 // 0x00000011
+ ram_wr32(fuc, 0x132100, 0x00000001);
+ ram_wr32(fuc, 0x1373f8, 0x00002000);
+ ram_nsec(fuc, 2000);
+ ram_wr32(fuc, 0x10f808, 0x7aaa0050);
+ ram_wr32(fuc, 0x10f830, 0x00500010);
+ ram_wr32(fuc, 0x10f200, 0x00ce1000);
+ ram_wr32(fuc, 0x10f090, 0x4000007e);
+ ram_nsec(fuc, 2000);
+ ram_wr32(fuc, 0x10f314, 0x00000001);
+ ram_wr32(fuc, 0x10f210, 0x80000000);
+ ram_wr32(fuc, 0x10f338, 0x00300200);
+ ram_wr32(fuc, 0x10f300, 0x0000084d);
+ ram_nsec(fuc, 1000);
+ ram_wr32(fuc, 0x10f290, 0x0b343825);
+ ram_wr32(fuc, 0x10f294, 0x3483028e);
+ ram_wr32(fuc, 0x10f298, 0x440c0600);
+ ram_wr32(fuc, 0x10f29c, 0x0000214c);
+ ram_wr32(fuc, 0x10f2a0, 0x42e20069);
+ ram_wr32(fuc, 0x10f200, 0x00ce0000);
+ ram_wr32(fuc, 0x10f614, 0x60044e77);
+ ram_wr32(fuc, 0x10f610, 0x60044e77);
+ ram_wr32(fuc, 0x10f340, 0x00500000);
+ ram_nsec(fuc, 1000);
+ ram_wr32(fuc, 0x10f344, 0x00600228);
+ ram_nsec(fuc, 1000);
+ ram_wr32(fuc, 0x10f348, 0x00700000);
+ ram_wr32(fuc, 0x13d8f4, 0x00000000);
+ ram_wr32(fuc, 0x61c140, 0x09a40000);
+
+ gf100_ram_train(fuc, 0x800e1008);
+
+ ram_nsec(fuc, 1000);
+ ram_wr32(fuc, 0x10f800, 0x00001804);
+ // 0x00030020 // 0x00000000 // 0x00000000
+ // 0x00020034 // 0x0000000b
+ ram_wr32(fuc, 0x13d8f4, 0x00000000);
+ ram_wr32(fuc, 0x100b0c, 0x00080028);
+ ram_wr32(fuc, 0x611200, 0x00003330);
+ ram_nsec(fuc, 100000);
+ ram_wr32(fuc, 0x10f9b0, 0x05313f41);
+ ram_wr32(fuc, 0x10f9b4, 0x00002f50);
+
+ gf100_ram_train(fuc, 0x010c1001);
+ }
+
+ ram_mask(fuc, 0x10f200, 0x00000800, 0x00000800);
+// 0x00020016 // 0x00000000
+
+ if (mode == 0)
+ ram_mask(fuc, 0x132000, 0x00000001, 0x00000000);
+
+ return 0;
+}
+
+static int
+gf100_ram_prog(struct nvkm_fb *pfb)
+{
+ struct nvkm_device *device = nv_device(pfb);
+ struct gf100_ram *ram = (void *)pfb->ram;
+ struct gf100_ramfuc *fuc = &ram->fuc;
+ ram_exec(fuc, nvkm_boolopt(device->cfgopt, "NvMemExec", true));
+ return 0;
+}
+
+static void
+gf100_ram_tidy(struct nvkm_fb *pfb)
+{
+ struct gf100_ram *ram = (void *)pfb->ram;
+ struct gf100_ramfuc *fuc = &ram->fuc;
+ ram_exec(fuc, false);
+}
+
+extern const u8 gf100_pte_storage_type_map[256];
+
+void
+gf100_ram_put(struct nvkm_fb *pfb, struct nvkm_mem **pmem)
+{
+ struct nvkm_ltc *ltc = nvkm_ltc(pfb);
+ struct nvkm_mem *mem = *pmem;
+
+ *pmem = NULL;
+ if (unlikely(mem == NULL))
+ return;
+
+ mutex_lock(&pfb->base.mutex);
+ if (mem->tag)
+ ltc->tags_free(ltc, &mem->tag);
+ __nv50_ram_put(pfb, mem);
+ mutex_unlock(&pfb->base.mutex);
+
+ kfree(mem);
+}
+
+int
+gf100_ram_get(struct nvkm_fb *pfb, u64 size, u32 align, u32 ncmin,
+ u32 memtype, struct nvkm_mem **pmem)
+{
+ struct nvkm_mm *mm = &pfb->vram;
+ struct nvkm_mm_node *r;
+ struct nvkm_mem *mem;
+ int type = (memtype & 0x0ff);
+ int back = (memtype & 0x800);
+ const bool comp = gf100_pte_storage_type_map[type] != type;
+ int ret;
+
+ size >>= 12;
+ align >>= 12;
+ ncmin >>= 12;
+ if (!ncmin)
+ ncmin = size;
+
+ mem = kzalloc(sizeof(*mem), GFP_KERNEL);
+ if (!mem)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&mem->regions);
+ mem->size = size;
+
+ mutex_lock(&pfb->base.mutex);
+ if (comp) {
+ struct nvkm_ltc *ltc = nvkm_ltc(pfb);
+
+ /* compression only works with lpages */
+ if (align == (1 << (17 - 12))) {
+ int n = size >> 5;
+ ltc->tags_alloc(ltc, n, &mem->tag);
+ }
+
+ if (unlikely(!mem->tag))
+ type = gf100_pte_storage_type_map[type];
+ }
+ mem->memtype = type;
+
+ do {
+ if (back)
+ ret = nvkm_mm_tail(mm, 0, 1, size, ncmin, align, &r);
+ else
+ ret = nvkm_mm_head(mm, 0, 1, size, ncmin, align, &r);
+ if (ret) {
+ mutex_unlock(&pfb->base.mutex);
+ pfb->ram->put(pfb, &mem);
+ return ret;
+ }
+
+ list_add_tail(&r->rl_entry, &mem->regions);
+ size -= r->length;
+ } while (size);
+ mutex_unlock(&pfb->base.mutex);
+
+ r = list_first_entry(&mem->regions, struct nvkm_mm_node, rl_entry);
+ mem->offset = (u64)r->offset << 12;
+ *pmem = mem;
+ return 0;
+}
+
+int
+gf100_ram_create_(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, u32 maskaddr, int size,
+ void **pobject)
+{
+ struct nvkm_fb *pfb = nvkm_fb(parent);
+ struct nvkm_bios *bios = nvkm_bios(pfb);
+ struct nvkm_ram *ram;
+ const u32 rsvd_head = ( 256 * 1024) >> 12; /* vga memory */
+ const u32 rsvd_tail = (1024 * 1024) >> 12; /* vbios etc */
+ u32 parts = nv_rd32(pfb, 0x022438);
+ u32 pmask = nv_rd32(pfb, maskaddr);
+ u32 bsize = nv_rd32(pfb, 0x10f20c);
+ u32 offset, length;
+ bool uniform = true;
+ int ret, part;
+
+ ret = nvkm_ram_create_(parent, engine, oclass, size, pobject);
+ ram = *pobject;
+ if (ret)
+ return ret;
+
+ nv_debug(pfb, "0x100800: 0x%08x\n", nv_rd32(pfb, 0x100800));
+ nv_debug(pfb, "parts 0x%08x mask 0x%08x\n", parts, pmask);
+
+ ram->type = nvkm_fb_bios_memtype(bios);
+ ram->ranks = (nv_rd32(pfb, 0x10f200) & 0x00000004) ? 2 : 1;
+
+ /* read amount of vram attached to each memory controller */
+ for (part = 0; part < parts; part++) {
+ if (!(pmask & (1 << part))) {
+ u32 psize = nv_rd32(pfb, 0x11020c + (part * 0x1000));
+ if (psize != bsize) {
+ if (psize < bsize)
+ bsize = psize;
+ uniform = false;
+ }
+
+ nv_debug(pfb, "%d: mem_amount 0x%08x\n", part, psize);
+ ram->size += (u64)psize << 20;
+ }
+ }
+
+ /* if all controllers have the same amount attached, there's no holes */
+ if (uniform) {
+ offset = rsvd_head;
+ length = (ram->size >> 12) - rsvd_head - rsvd_tail;
+ ret = nvkm_mm_init(&pfb->vram, offset, length, 1);
+ } else {
+ /* otherwise, address lowest common amount from 0GiB */
+ ret = nvkm_mm_init(&pfb->vram, rsvd_head,
+ (bsize << 8) * parts - rsvd_head, 1);
+ if (ret)
+ return ret;
+
+ /* and the rest starting from (8GiB + common_size) */
+ offset = (0x0200000000ULL >> 12) + (bsize << 8);
+ length = (ram->size >> 12) - ((bsize * parts) << 8) - rsvd_tail;
+
+ ret = nvkm_mm_init(&pfb->vram, offset, length, 1);
+ if (ret)
+ nvkm_mm_fini(&pfb->vram);
+ }
+
+ if (ret)
+ return ret;
+
+ ram->get = gf100_ram_get;
+ ram->put = gf100_ram_put;
+ return 0;
+}
+
+static int
+gf100_ram_init(struct nvkm_object *object)
+{
+ struct nvkm_fb *pfb = (void *)object->parent;
+ struct gf100_ram *ram = (void *)object;
+ int ret, i;
+
+ ret = nvkm_ram_init(&ram->base);
+ if (ret)
+ return ret;
+
+ /* prepare for ddr link training, and load training patterns */
+ switch (ram->base.type) {
+ case NV_MEM_TYPE_GDDR5: {
+ static const u8 train0[] = {
+ 0x00, 0xff, 0x55, 0xaa, 0x33, 0xcc,
+ 0x00, 0xff, 0xff, 0x00, 0xff, 0x00,
+ };
+ static const u32 train1[] = {
+ 0x00000000, 0xffffffff,
+ 0x55555555, 0xaaaaaaaa,
+ 0x33333333, 0xcccccccc,
+ 0xf0f0f0f0, 0x0f0f0f0f,
+ 0x00ff00ff, 0xff00ff00,
+ 0x0000ffff, 0xffff0000,
+ };
+
+ for (i = 0; i < 0x30; i++) {
+ nv_wr32(pfb, 0x10f968, 0x00000000 | (i << 8));
+ nv_wr32(pfb, 0x10f96c, 0x00000000 | (i << 8));
+ nv_wr32(pfb, 0x10f920, 0x00000100 | train0[i % 12]);
+ nv_wr32(pfb, 0x10f924, 0x00000100 | train0[i % 12]);
+ nv_wr32(pfb, 0x10f918, train1[i % 12]);
+ nv_wr32(pfb, 0x10f91c, train1[i % 12]);
+ nv_wr32(pfb, 0x10f920, 0x00000000 | train0[i % 12]);
+ nv_wr32(pfb, 0x10f924, 0x00000000 | train0[i % 12]);
+ nv_wr32(pfb, 0x10f918, train1[i % 12]);
+ nv_wr32(pfb, 0x10f91c, train1[i % 12]);
+ }
+ } break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int
+gf100_ram_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 size,
+ struct nvkm_object **pobject)
+{
+ struct nvkm_bios *bios = nvkm_bios(parent);
+ struct gf100_ram *ram;
+ int ret;
+
+ ret = gf100_ram_create(parent, engine, oclass, 0x022554, &ram);
+ *pobject = nv_object(ram);
+ if (ret)
+ return ret;
+
+ ret = nvbios_pll_parse(bios, 0x0c, &ram->refpll);
+ if (ret) {
+ nv_error(ram, "mclk refpll data not found\n");
+ return ret;
+ }
+
+ ret = nvbios_pll_parse(bios, 0x04, &ram->mempll);
+ if (ret) {
+ nv_error(ram, "mclk pll data not found\n");
+ return ret;
+ }
+
+ switch (ram->base.type) {
+ case NV_MEM_TYPE_GDDR5:
+ ram->base.calc = gf100_ram_calc;
+ ram->base.prog = gf100_ram_prog;
+ ram->base.tidy = gf100_ram_tidy;
+ break;
+ default:
+ nv_warn(ram, "reclocking of this ram type unsupported\n");
+ return 0;
+ }
+
+ ram->fuc.r_0x10fe20 = ramfuc_reg(0x10fe20);
+ ram->fuc.r_0x10fe24 = ramfuc_reg(0x10fe24);
+ ram->fuc.r_0x137320 = ramfuc_reg(0x137320);
+ ram->fuc.r_0x137330 = ramfuc_reg(0x137330);
+
+ ram->fuc.r_0x132000 = ramfuc_reg(0x132000);
+ ram->fuc.r_0x132004 = ramfuc_reg(0x132004);
+ ram->fuc.r_0x132100 = ramfuc_reg(0x132100);
+
+ ram->fuc.r_0x137390 = ramfuc_reg(0x137390);
+
+ ram->fuc.r_0x10f290 = ramfuc_reg(0x10f290);
+ ram->fuc.r_0x10f294 = ramfuc_reg(0x10f294);
+ ram->fuc.r_0x10f298 = ramfuc_reg(0x10f298);
+ ram->fuc.r_0x10f29c = ramfuc_reg(0x10f29c);
+ ram->fuc.r_0x10f2a0 = ramfuc_reg(0x10f2a0);
+
+ ram->fuc.r_0x10f300 = ramfuc_reg(0x10f300);
+ ram->fuc.r_0x10f338 = ramfuc_reg(0x10f338);
+ ram->fuc.r_0x10f340 = ramfuc_reg(0x10f340);
+ ram->fuc.r_0x10f344 = ramfuc_reg(0x10f344);
+ ram->fuc.r_0x10f348 = ramfuc_reg(0x10f348);
+
+ ram->fuc.r_0x10f910 = ramfuc_reg(0x10f910);
+ ram->fuc.r_0x10f914 = ramfuc_reg(0x10f914);
+
+ ram->fuc.r_0x100b0c = ramfuc_reg(0x100b0c);
+ ram->fuc.r_0x10f050 = ramfuc_reg(0x10f050);
+ ram->fuc.r_0x10f090 = ramfuc_reg(0x10f090);
+ ram->fuc.r_0x10f200 = ramfuc_reg(0x10f200);
+ ram->fuc.r_0x10f210 = ramfuc_reg(0x10f210);
+ ram->fuc.r_0x10f310 = ramfuc_reg(0x10f310);
+ ram->fuc.r_0x10f314 = ramfuc_reg(0x10f314);
+ ram->fuc.r_0x10f610 = ramfuc_reg(0x10f610);
+ ram->fuc.r_0x10f614 = ramfuc_reg(0x10f614);
+ ram->fuc.r_0x10f800 = ramfuc_reg(0x10f800);
+ ram->fuc.r_0x10f808 = ramfuc_reg(0x10f808);
+ ram->fuc.r_0x10f824 = ramfuc_reg(0x10f824);
+ ram->fuc.r_0x10f830 = ramfuc_reg(0x10f830);
+ ram->fuc.r_0x10f988 = ramfuc_reg(0x10f988);
+ ram->fuc.r_0x10f98c = ramfuc_reg(0x10f98c);
+ ram->fuc.r_0x10f990 = ramfuc_reg(0x10f990);
+ ram->fuc.r_0x10f998 = ramfuc_reg(0x10f998);
+ ram->fuc.r_0x10f9b0 = ramfuc_reg(0x10f9b0);
+ ram->fuc.r_0x10f9b4 = ramfuc_reg(0x10f9b4);
+ ram->fuc.r_0x10fb04 = ramfuc_reg(0x10fb04);
+ ram->fuc.r_0x10fb08 = ramfuc_reg(0x10fb08);
+ ram->fuc.r_0x137310 = ramfuc_reg(0x137300);
+ ram->fuc.r_0x137310 = ramfuc_reg(0x137310);
+ ram->fuc.r_0x137360 = ramfuc_reg(0x137360);
+ ram->fuc.r_0x1373ec = ramfuc_reg(0x1373ec);
+ ram->fuc.r_0x1373f0 = ramfuc_reg(0x1373f0);
+ ram->fuc.r_0x1373f8 = ramfuc_reg(0x1373f8);
+
+ ram->fuc.r_0x61c140 = ramfuc_reg(0x61c140);
+ ram->fuc.r_0x611200 = ramfuc_reg(0x611200);
+
+ ram->fuc.r_0x13d8f4 = ramfuc_reg(0x13d8f4);
+ return 0;
+}
+
+struct nvkm_oclass
+gf100_ram_oclass = {
+ .handle = 0,
+ .ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = gf100_ram_ctor,
+ .dtor = _nvkm_ram_dtor,
+ .init = gf100_ram_init,
+ .fini = _nvkm_ram_fini,
+ }
+};
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgk104.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgk104.c
new file mode 100644
index 000000000000..1ef15c3e6a81
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgk104.c
@@ -0,0 +1,1639 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "ramfuc.h"
+#include "gf100.h"
+
+#include <core/device.h>
+#include <core/option.h>
+#include <subdev/bios.h>
+#include <subdev/bios/init.h>
+#include <subdev/bios/M0205.h>
+#include <subdev/bios/M0209.h>
+#include <subdev/bios/pll.h>
+#include <subdev/bios/rammap.h>
+#include <subdev/bios/timing.h>
+#include <subdev/clk.h>
+#include <subdev/clk/pll.h>
+#include <subdev/gpio.h>
+
+struct gk104_ramfuc {
+ struct ramfuc base;
+
+ struct nvbios_pll refpll;
+ struct nvbios_pll mempll;
+
+ struct ramfuc_reg r_gpioMV;
+ u32 r_funcMV[2];
+ struct ramfuc_reg r_gpio2E;
+ u32 r_func2E[2];
+ struct ramfuc_reg r_gpiotrig;
+
+ struct ramfuc_reg r_0x132020;
+ struct ramfuc_reg r_0x132028;
+ struct ramfuc_reg r_0x132024;
+ struct ramfuc_reg r_0x132030;
+ struct ramfuc_reg r_0x132034;
+ struct ramfuc_reg r_0x132000;
+ struct ramfuc_reg r_0x132004;
+ struct ramfuc_reg r_0x132040;
+
+ struct ramfuc_reg r_0x10f248;
+ struct ramfuc_reg r_0x10f290;
+ struct ramfuc_reg r_0x10f294;
+ struct ramfuc_reg r_0x10f298;
+ struct ramfuc_reg r_0x10f29c;
+ struct ramfuc_reg r_0x10f2a0;
+ struct ramfuc_reg r_0x10f2a4;
+ struct ramfuc_reg r_0x10f2a8;
+ struct ramfuc_reg r_0x10f2ac;
+ struct ramfuc_reg r_0x10f2cc;
+ struct ramfuc_reg r_0x10f2e8;
+ struct ramfuc_reg r_0x10f250;
+ struct ramfuc_reg r_0x10f24c;
+ struct ramfuc_reg r_0x10fec4;
+ struct ramfuc_reg r_0x10fec8;
+ struct ramfuc_reg r_0x10f604;
+ struct ramfuc_reg r_0x10f614;
+ struct ramfuc_reg r_0x10f610;
+ struct ramfuc_reg r_0x100770;
+ struct ramfuc_reg r_0x100778;
+ struct ramfuc_reg r_0x10f224;
+
+ struct ramfuc_reg r_0x10f870;
+ struct ramfuc_reg r_0x10f698;
+ struct ramfuc_reg r_0x10f694;
+ struct ramfuc_reg r_0x10f6b8;
+ struct ramfuc_reg r_0x10f808;
+ struct ramfuc_reg r_0x10f670;
+ struct ramfuc_reg r_0x10f60c;
+ struct ramfuc_reg r_0x10f830;
+ struct ramfuc_reg r_0x1373ec;
+ struct ramfuc_reg r_0x10f800;
+ struct ramfuc_reg r_0x10f82c;
+
+ struct ramfuc_reg r_0x10f978;
+ struct ramfuc_reg r_0x10f910;
+ struct ramfuc_reg r_0x10f914;
+
+ struct ramfuc_reg r_mr[16]; /* MR0 - MR8, MR15 */
+
+ struct ramfuc_reg r_0x62c000;
+
+ struct ramfuc_reg r_0x10f200;
+
+ struct ramfuc_reg r_0x10f210;
+ struct ramfuc_reg r_0x10f310;
+ struct ramfuc_reg r_0x10f314;
+ struct ramfuc_reg r_0x10f318;
+ struct ramfuc_reg r_0x10f090;
+ struct ramfuc_reg r_0x10f69c;
+ struct ramfuc_reg r_0x10f824;
+ struct ramfuc_reg r_0x1373f0;
+ struct ramfuc_reg r_0x1373f4;
+ struct ramfuc_reg r_0x137320;
+ struct ramfuc_reg r_0x10f65c;
+ struct ramfuc_reg r_0x10f6bc;
+ struct ramfuc_reg r_0x100710;
+ struct ramfuc_reg r_0x100750;
+};
+
+struct gk104_ram {
+ struct nvkm_ram base;
+ struct gk104_ramfuc fuc;
+
+ struct list_head cfg;
+ u32 parts;
+ u32 pmask;
+ u32 pnuts;
+
+ struct nvbios_ramcfg diff;
+ int from;
+ int mode;
+ int N1, fN1, M1, P1;
+ int N2, M2, P2;
+};
+
+/*******************************************************************************
+ * GDDR5
+ ******************************************************************************/
+static void
+gk104_ram_train(struct gk104_ramfuc *fuc, u32 mask, u32 data)
+{
+ struct gk104_ram *ram = container_of(fuc, typeof(*ram), fuc);
+ u32 addr = 0x110974, i;
+
+ ram_mask(fuc, 0x10f910, mask, data);
+ ram_mask(fuc, 0x10f914, mask, data);
+
+ for (i = 0; (data & 0x80000000) && i < ram->parts; addr += 0x1000, i++) {
+ if (ram->pmask & (1 << i))
+ continue;
+ ram_wait(fuc, addr, 0x0000000f, 0x00000000, 500000);
+ }
+}
+
+static void
+r1373f4_init(struct gk104_ramfuc *fuc)
+{
+ struct gk104_ram *ram = container_of(fuc, typeof(*ram), fuc);
+ const u32 mcoef = ((--ram->P2 << 28) | (ram->N2 << 8) | ram->M2);
+ const u32 rcoef = (( ram->P1 << 16) | (ram->N1 << 8) | ram->M1);
+ const u32 runk0 = ram->fN1 << 16;
+ const u32 runk1 = ram->fN1;
+
+ if (ram->from == 2) {
+ ram_mask(fuc, 0x1373f4, 0x00000000, 0x00001100);
+ ram_mask(fuc, 0x1373f4, 0x00000000, 0x00000010);
+ } else {
+ ram_mask(fuc, 0x1373f4, 0x00000000, 0x00010010);
+ }
+
+ ram_mask(fuc, 0x1373f4, 0x00000003, 0x00000000);
+ ram_mask(fuc, 0x1373f4, 0x00000010, 0x00000000);
+
+ /* (re)program refpll, if required */
+ if ((ram_rd32(fuc, 0x132024) & 0xffffffff) != rcoef ||
+ (ram_rd32(fuc, 0x132034) & 0x0000ffff) != runk1) {
+ ram_mask(fuc, 0x132000, 0x00000001, 0x00000000);
+ ram_mask(fuc, 0x132020, 0x00000001, 0x00000000);
+ ram_wr32(fuc, 0x137320, 0x00000000);
+ ram_mask(fuc, 0x132030, 0xffff0000, runk0);
+ ram_mask(fuc, 0x132034, 0x0000ffff, runk1);
+ ram_wr32(fuc, 0x132024, rcoef);
+ ram_mask(fuc, 0x132028, 0x00080000, 0x00080000);
+ ram_mask(fuc, 0x132020, 0x00000001, 0x00000001);
+ ram_wait(fuc, 0x137390, 0x00020000, 0x00020000, 64000);
+ ram_mask(fuc, 0x132028, 0x00080000, 0x00000000);
+ }
+
+ /* (re)program mempll, if required */
+ if (ram->mode == 2) {
+ ram_mask(fuc, 0x1373f4, 0x00010000, 0x00000000);
+ ram_mask(fuc, 0x132000, 0x80000000, 0x80000000);
+ ram_mask(fuc, 0x132000, 0x00000001, 0x00000000);
+ ram_mask(fuc, 0x132004, 0x103fffff, mcoef);
+ ram_mask(fuc, 0x132000, 0x00000001, 0x00000001);
+ ram_wait(fuc, 0x137390, 0x00000002, 0x00000002, 64000);
+ ram_mask(fuc, 0x1373f4, 0x00000000, 0x00001100);
+ } else {
+ ram_mask(fuc, 0x1373f4, 0x00000000, 0x00010100);
+ }
+
+ ram_mask(fuc, 0x1373f4, 0x00000000, 0x00000010);
+}
+
+static void
+r1373f4_fini(struct gk104_ramfuc *fuc)
+{
+ struct gk104_ram *ram = container_of(fuc, typeof(*ram), fuc);
+ struct nvkm_ram_data *next = ram->base.next;
+ u8 v0 = next->bios.ramcfg_11_03_c0;
+ u8 v1 = next->bios.ramcfg_11_03_30;
+ u32 tmp;
+
+ tmp = ram_rd32(fuc, 0x1373ec) & ~0x00030000;
+ ram_wr32(fuc, 0x1373ec, tmp | (v1 << 16));
+ ram_mask(fuc, 0x1373f0, (~ram->mode & 3), 0x00000000);
+ if (ram->mode == 2) {
+ ram_mask(fuc, 0x1373f4, 0x00000003, 0x000000002);
+ ram_mask(fuc, 0x1373f4, 0x00001100, 0x000000000);
+ } else {
+ ram_mask(fuc, 0x1373f4, 0x00000003, 0x000000001);
+ ram_mask(fuc, 0x1373f4, 0x00010000, 0x000000000);
+ }
+ ram_mask(fuc, 0x10f800, 0x00000030, (v0 ^ v1) << 4);
+}
+
+static void
+gk104_ram_nuts(struct gk104_ram *ram, struct ramfuc_reg *reg,
+ u32 _mask, u32 _data, u32 _copy)
+{
+ struct gk104_fb_priv *priv = (void *)nvkm_fb(ram);
+ struct ramfuc *fuc = &ram->fuc.base;
+ u32 addr = 0x110000 + (reg->addr & 0xfff);
+ u32 mask = _mask | _copy;
+ u32 data = (_data & _mask) | (reg->data & _copy);
+ u32 i;
+
+ for (i = 0; i < 16; i++, addr += 0x1000) {
+ if (ram->pnuts & (1 << i)) {
+ u32 prev = nv_rd32(priv, addr);
+ u32 next = (prev & ~mask) | data;
+ nvkm_memx_wr32(fuc->memx, addr, next);
+ }
+ }
+}
+#define ram_nuts(s,r,m,d,c) \
+ gk104_ram_nuts((s), &(s)->fuc.r_##r, (m), (d), (c))
+
+static int
+gk104_ram_calc_gddr5(struct nvkm_fb *pfb, u32 freq)
+{
+ struct gk104_ram *ram = (void *)pfb->ram;
+ struct gk104_ramfuc *fuc = &ram->fuc;
+ struct nvkm_ram_data *next = ram->base.next;
+ int vc = !next->bios.ramcfg_11_02_08;
+ int mv = !next->bios.ramcfg_11_02_04;
+ u32 mask, data;
+
+ ram_mask(fuc, 0x10f808, 0x40000000, 0x40000000);
+ ram_block(fuc);
+ ram_wr32(fuc, 0x62c000, 0x0f0f0000);
+
+ /* MR1: turn termination on early, for some reason.. */
+ if ((ram->base.mr[1] & 0x03c) != 0x030) {
+ ram_mask(fuc, mr[1], 0x03c, ram->base.mr[1] & 0x03c);
+ ram_nuts(ram, mr[1], 0x03c, ram->base.mr1_nuts & 0x03c, 0x000);
+ }
+
+ if (vc == 1 && ram_have(fuc, gpio2E)) {
+ u32 temp = ram_mask(fuc, gpio2E, 0x3000, fuc->r_func2E[1]);
+ if (temp != ram_rd32(fuc, gpio2E)) {
+ ram_wr32(fuc, gpiotrig, 1);
+ ram_nsec(fuc, 20000);
+ }
+ }
+
+ ram_mask(fuc, 0x10f200, 0x00000800, 0x00000000);
+
+ gk104_ram_train(fuc, 0x01020000, 0x000c0000);
+
+ ram_wr32(fuc, 0x10f210, 0x00000000); /* REFRESH_AUTO = 0 */
+ ram_nsec(fuc, 1000);
+ ram_wr32(fuc, 0x10f310, 0x00000001); /* REFRESH */
+ ram_nsec(fuc, 1000);
+
+ ram_mask(fuc, 0x10f200, 0x80000000, 0x80000000);
+ ram_wr32(fuc, 0x10f314, 0x00000001); /* PRECHARGE */
+ ram_mask(fuc, 0x10f200, 0x80000000, 0x00000000);
+ ram_wr32(fuc, 0x10f090, 0x00000061);
+ ram_wr32(fuc, 0x10f090, 0xc000007f);
+ ram_nsec(fuc, 1000);
+
+ ram_wr32(fuc, 0x10f698, 0x00000000);
+ ram_wr32(fuc, 0x10f69c, 0x00000000);
+
+ /*XXX: there does appear to be some kind of condition here, simply
+ * modifying these bits in the vbios from the default pl0
+ * entries shows no change. however, the data does appear to
+ * be correct and may be required for the transition back
+ */
+ mask = 0x800f07e0;
+ data = 0x00030000;
+ if (ram_rd32(fuc, 0x10f978) & 0x00800000)
+ data |= 0x00040000;
+
+ if (1) {
+ data |= 0x800807e0;
+ switch (next->bios.ramcfg_11_03_c0) {
+ case 3: data &= ~0x00000040; break;
+ case 2: data &= ~0x00000100; break;
+ case 1: data &= ~0x80000000; break;
+ case 0: data &= ~0x00000400; break;
+ }
+
+ switch (next->bios.ramcfg_11_03_30) {
+ case 3: data &= ~0x00000020; break;
+ case 2: data &= ~0x00000080; break;
+ case 1: data &= ~0x00080000; break;
+ case 0: data &= ~0x00000200; break;
+ }
+ }
+
+ if (next->bios.ramcfg_11_02_80)
+ mask |= 0x03000000;
+ if (next->bios.ramcfg_11_02_40)
+ mask |= 0x00002000;
+ if (next->bios.ramcfg_11_07_10)
+ mask |= 0x00004000;
+ if (next->bios.ramcfg_11_07_08)
+ mask |= 0x00000003;
+ else {
+ mask |= 0x34000000;
+ if (ram_rd32(fuc, 0x10f978) & 0x00800000)
+ mask |= 0x40000000;
+ }
+ ram_mask(fuc, 0x10f824, mask, data);
+
+ ram_mask(fuc, 0x132040, 0x00010000, 0x00000000);
+
+ if (ram->from == 2 && ram->mode != 2) {
+ ram_mask(fuc, 0x10f808, 0x00080000, 0x00000000);
+ ram_mask(fuc, 0x10f200, 0x18008000, 0x00008000);
+ ram_mask(fuc, 0x10f800, 0x00000000, 0x00000004);
+ ram_mask(fuc, 0x10f830, 0x00008000, 0x01040010);
+ ram_mask(fuc, 0x10f830, 0x01000000, 0x00000000);
+ r1373f4_init(fuc);
+ ram_mask(fuc, 0x1373f0, 0x00000002, 0x00000001);
+ r1373f4_fini(fuc);
+ ram_mask(fuc, 0x10f830, 0x00c00000, 0x00240001);
+ } else
+ if (ram->from != 2 && ram->mode != 2) {
+ r1373f4_init(fuc);
+ r1373f4_fini(fuc);
+ }
+
+ if (ram_have(fuc, gpioMV)) {
+ u32 temp = ram_mask(fuc, gpioMV, 0x3000, fuc->r_funcMV[mv]);
+ if (temp != ram_rd32(fuc, gpioMV)) {
+ ram_wr32(fuc, gpiotrig, 1);
+ ram_nsec(fuc, 64000);
+ }
+ }
+
+ if (next->bios.ramcfg_11_02_40 ||
+ next->bios.ramcfg_11_07_10) {
+ ram_mask(fuc, 0x132040, 0x00010000, 0x00010000);
+ ram_nsec(fuc, 20000);
+ }
+
+ if (ram->from != 2 && ram->mode == 2) {
+ if (0 /*XXX: Titan */)
+ ram_mask(fuc, 0x10f200, 0x18000000, 0x18000000);
+ ram_mask(fuc, 0x10f800, 0x00000004, 0x00000000);
+ ram_mask(fuc, 0x1373f0, 0x00000000, 0x00000002);
+ ram_mask(fuc, 0x10f830, 0x00800001, 0x00408010);
+ r1373f4_init(fuc);
+ r1373f4_fini(fuc);
+ ram_mask(fuc, 0x10f808, 0x00000000, 0x00080000);
+ ram_mask(fuc, 0x10f200, 0x00808000, 0x00800000);
+ } else
+ if (ram->from == 2 && ram->mode == 2) {
+ ram_mask(fuc, 0x10f800, 0x00000004, 0x00000000);
+ r1373f4_init(fuc);
+ r1373f4_fini(fuc);
+ }
+
+ if (ram->mode != 2) /*XXX*/ {
+ if (next->bios.ramcfg_11_07_40)
+ ram_mask(fuc, 0x10f670, 0x80000000, 0x80000000);
+ }
+
+ ram_wr32(fuc, 0x10f65c, 0x00000011 * next->bios.rammap_11_11_0c);
+ ram_wr32(fuc, 0x10f6b8, 0x01010101 * next->bios.ramcfg_11_09);
+ ram_wr32(fuc, 0x10f6bc, 0x01010101 * next->bios.ramcfg_11_09);
+
+ if (!next->bios.ramcfg_11_07_08 && !next->bios.ramcfg_11_07_04) {
+ ram_wr32(fuc, 0x10f698, 0x01010101 * next->bios.ramcfg_11_04);
+ ram_wr32(fuc, 0x10f69c, 0x01010101 * next->bios.ramcfg_11_04);
+ } else
+ if (!next->bios.ramcfg_11_07_08) {
+ ram_wr32(fuc, 0x10f698, 0x00000000);
+ ram_wr32(fuc, 0x10f69c, 0x00000000);
+ }
+
+ if (ram->mode != 2) {
+ u32 data = 0x01000100 * next->bios.ramcfg_11_04;
+ ram_nuke(fuc, 0x10f694);
+ ram_mask(fuc, 0x10f694, 0xff00ff00, data);
+ }
+
+ if (ram->mode == 2 && next->bios.ramcfg_11_08_10)
+ data = 0x00000080;
+ else
+ data = 0x00000000;
+ ram_mask(fuc, 0x10f60c, 0x00000080, data);
+
+ mask = 0x00070000;
+ data = 0x00000000;
+ if (!next->bios.ramcfg_11_02_80)
+ data |= 0x03000000;
+ if (!next->bios.ramcfg_11_02_40)
+ data |= 0x00002000;
+ if (!next->bios.ramcfg_11_07_10)
+ data |= 0x00004000;
+ if (!next->bios.ramcfg_11_07_08)
+ data |= 0x00000003;
+ else
+ data |= 0x74000000;
+ ram_mask(fuc, 0x10f824, mask, data);
+
+ if (next->bios.ramcfg_11_01_08)
+ data = 0x00000000;
+ else
+ data = 0x00001000;
+ ram_mask(fuc, 0x10f200, 0x00001000, data);
+
+ if (ram_rd32(fuc, 0x10f670) & 0x80000000) {
+ ram_nsec(fuc, 10000);
+ ram_mask(fuc, 0x10f670, 0x80000000, 0x00000000);
+ }
+
+ if (next->bios.ramcfg_11_08_01)
+ data = 0x00100000;
+ else
+ data = 0x00000000;
+ ram_mask(fuc, 0x10f82c, 0x00100000, data);
+
+ data = 0x00000000;
+ if (next->bios.ramcfg_11_08_08)
+ data |= 0x00002000;
+ if (next->bios.ramcfg_11_08_04)
+ data |= 0x00001000;
+ if (next->bios.ramcfg_11_08_02)
+ data |= 0x00004000;
+ ram_mask(fuc, 0x10f830, 0x00007000, data);
+
+ /* PFB timing */
+ ram_mask(fuc, 0x10f248, 0xffffffff, next->bios.timing[10]);
+ ram_mask(fuc, 0x10f290, 0xffffffff, next->bios.timing[0]);
+ ram_mask(fuc, 0x10f294, 0xffffffff, next->bios.timing[1]);
+ ram_mask(fuc, 0x10f298, 0xffffffff, next->bios.timing[2]);
+ ram_mask(fuc, 0x10f29c, 0xffffffff, next->bios.timing[3]);
+ ram_mask(fuc, 0x10f2a0, 0xffffffff, next->bios.timing[4]);
+ ram_mask(fuc, 0x10f2a4, 0xffffffff, next->bios.timing[5]);
+ ram_mask(fuc, 0x10f2a8, 0xffffffff, next->bios.timing[6]);
+ ram_mask(fuc, 0x10f2ac, 0xffffffff, next->bios.timing[7]);
+ ram_mask(fuc, 0x10f2cc, 0xffffffff, next->bios.timing[8]);
+ ram_mask(fuc, 0x10f2e8, 0xffffffff, next->bios.timing[9]);
+
+ data = mask = 0x00000000;
+ if (ram->diff.ramcfg_11_08_20) {
+ if (next->bios.ramcfg_11_08_20)
+ data |= 0x01000000;
+ mask |= 0x01000000;
+ }
+ ram_mask(fuc, 0x10f200, mask, data);
+
+ data = mask = 0x00000000;
+ if (ram->diff.ramcfg_11_02_03) {
+ data |= next->bios.ramcfg_11_02_03 << 8;
+ mask |= 0x00000300;
+ }
+ if (ram->diff.ramcfg_11_01_10) {
+ if (next->bios.ramcfg_11_01_10)
+ data |= 0x70000000;
+ mask |= 0x70000000;
+ }
+ ram_mask(fuc, 0x10f604, mask, data);
+
+ data = mask = 0x00000000;
+ if (ram->diff.timing_20_30_07) {
+ data |= next->bios.timing_20_30_07 << 28;
+ mask |= 0x70000000;
+ }
+ if (ram->diff.ramcfg_11_01_01) {
+ if (next->bios.ramcfg_11_01_01)
+ data |= 0x00000100;
+ mask |= 0x00000100;
+ }
+ ram_mask(fuc, 0x10f614, mask, data);
+
+ data = mask = 0x00000000;
+ if (ram->diff.timing_20_30_07) {
+ data |= next->bios.timing_20_30_07 << 28;
+ mask |= 0x70000000;
+ }
+ if (ram->diff.ramcfg_11_01_02) {
+ if (next->bios.ramcfg_11_01_02)
+ data |= 0x00000100;
+ mask |= 0x00000100;
+ }
+ ram_mask(fuc, 0x10f610, mask, data);
+
+ mask = 0x33f00000;
+ data = 0x00000000;
+ if (!next->bios.ramcfg_11_01_04)
+ data |= 0x20200000;
+ if (!next->bios.ramcfg_11_07_80)
+ data |= 0x12800000;
+ /*XXX: see note above about there probably being some condition
+ * for the 10f824 stuff that uses ramcfg 3...
+ */
+ if (next->bios.ramcfg_11_03_f0) {
+ if (next->bios.rammap_11_08_0c) {
+ if (!next->bios.ramcfg_11_07_80)
+ mask |= 0x00000020;
+ else
+ data |= 0x00000020;
+ mask |= 0x00000004;
+ }
+ } else {
+ mask |= 0x40000020;
+ data |= 0x00000004;
+ }
+
+ ram_mask(fuc, 0x10f808, mask, data);
+
+ ram_wr32(fuc, 0x10f870, 0x11111111 * next->bios.ramcfg_11_03_0f);
+
+ data = mask = 0x00000000;
+ if (ram->diff.ramcfg_11_02_03) {
+ data |= next->bios.ramcfg_11_02_03;
+ mask |= 0x00000003;
+ }
+ if (ram->diff.ramcfg_11_01_10) {
+ if (next->bios.ramcfg_11_01_10)
+ data |= 0x00000004;
+ mask |= 0x00000004;
+ }
+
+ if ((ram_mask(fuc, 0x100770, mask, data) & mask & 4) != (data & 4)) {
+ ram_mask(fuc, 0x100750, 0x00000008, 0x00000008);
+ ram_wr32(fuc, 0x100710, 0x00000000);
+ ram_wait(fuc, 0x100710, 0x80000000, 0x80000000, 200000);
+ }
+
+ data = next->bios.timing_20_30_07 << 8;
+ if (next->bios.ramcfg_11_01_01)
+ data |= 0x80000000;
+ ram_mask(fuc, 0x100778, 0x00000700, data);
+
+ ram_mask(fuc, 0x10f250, 0x000003f0, next->bios.timing_20_2c_003f << 4);
+ data = (next->bios.timing[10] & 0x7f000000) >> 24;
+ if (data < next->bios.timing_20_2c_1fc0)
+ data = next->bios.timing_20_2c_1fc0;
+ ram_mask(fuc, 0x10f24c, 0x7f000000, data << 24);
+ ram_mask(fuc, 0x10f224, 0x001f0000, next->bios.timing_20_30_f8 << 16);
+
+ ram_mask(fuc, 0x10fec4, 0x041e0f07, next->bios.timing_20_31_0800 << 26 |
+ next->bios.timing_20_31_0780 << 17 |
+ next->bios.timing_20_31_0078 << 8 |
+ next->bios.timing_20_31_0007);
+ ram_mask(fuc, 0x10fec8, 0x00000027, next->bios.timing_20_31_8000 << 5 |
+ next->bios.timing_20_31_7000);
+
+ ram_wr32(fuc, 0x10f090, 0x4000007e);
+ ram_nsec(fuc, 2000);
+ ram_wr32(fuc, 0x10f314, 0x00000001); /* PRECHARGE */
+ ram_wr32(fuc, 0x10f310, 0x00000001); /* REFRESH */
+ ram_wr32(fuc, 0x10f210, 0x80000000); /* REFRESH_AUTO = 1 */
+
+ if (next->bios.ramcfg_11_08_10 && (ram->mode == 2) /*XXX*/) {
+ u32 temp = ram_mask(fuc, 0x10f294, 0xff000000, 0x24000000);
+ gk104_ram_train(fuc, 0xbc0e0000, 0xa4010000); /*XXX*/
+ ram_nsec(fuc, 1000);
+ ram_wr32(fuc, 0x10f294, temp);
+ }
+
+ ram_mask(fuc, mr[3], 0xfff, ram->base.mr[3]);
+ ram_wr32(fuc, mr[0], ram->base.mr[0]);
+ ram_mask(fuc, mr[8], 0xfff, ram->base.mr[8]);
+ ram_nsec(fuc, 1000);
+ ram_mask(fuc, mr[1], 0xfff, ram->base.mr[1]);
+ ram_mask(fuc, mr[5], 0xfff, ram->base.mr[5] & ~0x004); /* LP3 later */
+ ram_mask(fuc, mr[6], 0xfff, ram->base.mr[6]);
+ ram_mask(fuc, mr[7], 0xfff, ram->base.mr[7]);
+
+ if (vc == 0 && ram_have(fuc, gpio2E)) {
+ u32 temp = ram_mask(fuc, gpio2E, 0x3000, fuc->r_func2E[0]);
+ if (temp != ram_rd32(fuc, gpio2E)) {
+ ram_wr32(fuc, gpiotrig, 1);
+ ram_nsec(fuc, 20000);
+ }
+ }
+
+ ram_mask(fuc, 0x10f200, 0x80000000, 0x80000000);
+ ram_wr32(fuc, 0x10f318, 0x00000001); /* NOP? */
+ ram_mask(fuc, 0x10f200, 0x80000000, 0x00000000);
+ ram_nsec(fuc, 1000);
+ ram_nuts(ram, 0x10f200, 0x18808800, 0x00000000, 0x18808800);
+
+ data = ram_rd32(fuc, 0x10f978);
+ data &= ~0x00046144;
+ data |= 0x0000000b;
+ if (!next->bios.ramcfg_11_07_08) {
+ if (!next->bios.ramcfg_11_07_04)
+ data |= 0x0000200c;
+ else
+ data |= 0x00000000;
+ } else {
+ data |= 0x00040044;
+ }
+ ram_wr32(fuc, 0x10f978, data);
+
+ if (ram->mode == 1) {
+ data = ram_rd32(fuc, 0x10f830) | 0x00000001;
+ ram_wr32(fuc, 0x10f830, data);
+ }
+
+ if (!next->bios.ramcfg_11_07_08) {
+ data = 0x88020000;
+ if ( next->bios.ramcfg_11_07_04)
+ data |= 0x10000000;
+ if (!next->bios.rammap_11_08_10)
+ data |= 0x00080000;
+ } else {
+ data = 0xa40e0000;
+ }
+ gk104_ram_train(fuc, 0xbc0f0000, data);
+ if (1) /* XXX: not always? */
+ ram_nsec(fuc, 1000);
+
+ if (ram->mode == 2) { /*XXX*/
+ ram_mask(fuc, 0x10f800, 0x00000004, 0x00000004);
+ }
+
+ /* LP3 */
+ if (ram_mask(fuc, mr[5], 0x004, ram->base.mr[5]) != ram->base.mr[5])
+ ram_nsec(fuc, 1000);
+
+ if (ram->mode != 2) {
+ ram_mask(fuc, 0x10f830, 0x01000000, 0x01000000);
+ ram_mask(fuc, 0x10f830, 0x01000000, 0x00000000);
+ }
+
+ if (next->bios.ramcfg_11_07_02)
+ gk104_ram_train(fuc, 0x80020000, 0x01000000);
+
+ ram_unblock(fuc);
+ ram_wr32(fuc, 0x62c000, 0x0f0f0f00);
+
+ if (next->bios.rammap_11_08_01)
+ data = 0x00000800;
+ else
+ data = 0x00000000;
+ ram_mask(fuc, 0x10f200, 0x00000800, data);
+ ram_nuts(ram, 0x10f200, 0x18808800, data, 0x18808800);
+ return 0;
+}
+
+/*******************************************************************************
+ * DDR3
+ ******************************************************************************/
+
+static int
+gk104_ram_calc_sddr3(struct nvkm_fb *pfb, u32 freq)
+{
+ struct gk104_ram *ram = (void *)pfb->ram;
+ struct gk104_ramfuc *fuc = &ram->fuc;
+ const u32 rcoef = (( ram->P1 << 16) | (ram->N1 << 8) | ram->M1);
+ const u32 runk0 = ram->fN1 << 16;
+ const u32 runk1 = ram->fN1;
+ struct nvkm_ram_data *next = ram->base.next;
+ int vc = !next->bios.ramcfg_11_02_08;
+ int mv = !next->bios.ramcfg_11_02_04;
+ u32 mask, data;
+
+ ram_mask(fuc, 0x10f808, 0x40000000, 0x40000000);
+ ram_block(fuc);
+ ram_wr32(fuc, 0x62c000, 0x0f0f0000);
+
+ if (vc == 1 && ram_have(fuc, gpio2E)) {
+ u32 temp = ram_mask(fuc, gpio2E, 0x3000, fuc->r_func2E[1]);
+ if (temp != ram_rd32(fuc, gpio2E)) {
+ ram_wr32(fuc, gpiotrig, 1);
+ ram_nsec(fuc, 20000);
+ }
+ }
+
+ ram_mask(fuc, 0x10f200, 0x00000800, 0x00000000);
+ if (next->bios.ramcfg_11_03_f0)
+ ram_mask(fuc, 0x10f808, 0x04000000, 0x04000000);
+
+ ram_wr32(fuc, 0x10f314, 0x00000001); /* PRECHARGE */
+ ram_wr32(fuc, 0x10f210, 0x00000000); /* REFRESH_AUTO = 0 */
+ ram_wr32(fuc, 0x10f310, 0x00000001); /* REFRESH */
+ ram_mask(fuc, 0x10f200, 0x80000000, 0x80000000);
+ ram_wr32(fuc, 0x10f310, 0x00000001); /* REFRESH */
+ ram_mask(fuc, 0x10f200, 0x80000000, 0x00000000);
+ ram_nsec(fuc, 1000);
+
+ ram_wr32(fuc, 0x10f090, 0x00000060);
+ ram_wr32(fuc, 0x10f090, 0xc000007e);
+
+ /*XXX: there does appear to be some kind of condition here, simply
+ * modifying these bits in the vbios from the default pl0
+ * entries shows no change. however, the data does appear to
+ * be correct and may be required for the transition back
+ */
+ mask = 0x00010000;
+ data = 0x00010000;
+
+ if (1) {
+ mask |= 0x800807e0;
+ data |= 0x800807e0;
+ switch (next->bios.ramcfg_11_03_c0) {
+ case 3: data &= ~0x00000040; break;
+ case 2: data &= ~0x00000100; break;
+ case 1: data &= ~0x80000000; break;
+ case 0: data &= ~0x00000400; break;
+ }
+
+ switch (next->bios.ramcfg_11_03_30) {
+ case 3: data &= ~0x00000020; break;
+ case 2: data &= ~0x00000080; break;
+ case 1: data &= ~0x00080000; break;
+ case 0: data &= ~0x00000200; break;
+ }
+ }
+
+ if (next->bios.ramcfg_11_02_80)
+ mask |= 0x03000000;
+ if (next->bios.ramcfg_11_02_40)
+ mask |= 0x00002000;
+ if (next->bios.ramcfg_11_07_10)
+ mask |= 0x00004000;
+ if (next->bios.ramcfg_11_07_08)
+ mask |= 0x00000003;
+ else
+ mask |= 0x14000000;
+ ram_mask(fuc, 0x10f824, mask, data);
+
+ ram_mask(fuc, 0x132040, 0x00010000, 0x00000000);
+
+ ram_mask(fuc, 0x1373f4, 0x00000000, 0x00010010);
+ data = ram_rd32(fuc, 0x1373ec) & ~0x00030000;
+ data |= next->bios.ramcfg_11_03_30 << 16;
+ ram_wr32(fuc, 0x1373ec, data);
+ ram_mask(fuc, 0x1373f4, 0x00000003, 0x00000000);
+ ram_mask(fuc, 0x1373f4, 0x00000010, 0x00000000);
+
+ /* (re)program refpll, if required */
+ if ((ram_rd32(fuc, 0x132024) & 0xffffffff) != rcoef ||
+ (ram_rd32(fuc, 0x132034) & 0x0000ffff) != runk1) {
+ ram_mask(fuc, 0x132000, 0x00000001, 0x00000000);
+ ram_mask(fuc, 0x132020, 0x00000001, 0x00000000);
+ ram_wr32(fuc, 0x137320, 0x00000000);
+ ram_mask(fuc, 0x132030, 0xffff0000, runk0);
+ ram_mask(fuc, 0x132034, 0x0000ffff, runk1);
+ ram_wr32(fuc, 0x132024, rcoef);
+ ram_mask(fuc, 0x132028, 0x00080000, 0x00080000);
+ ram_mask(fuc, 0x132020, 0x00000001, 0x00000001);
+ ram_wait(fuc, 0x137390, 0x00020000, 0x00020000, 64000);
+ ram_mask(fuc, 0x132028, 0x00080000, 0x00000000);
+ }
+
+ ram_mask(fuc, 0x1373f4, 0x00000010, 0x00000010);
+ ram_mask(fuc, 0x1373f4, 0x00000003, 0x00000001);
+ ram_mask(fuc, 0x1373f4, 0x00010000, 0x00000000);
+
+ if (ram_have(fuc, gpioMV)) {
+ u32 temp = ram_mask(fuc, gpioMV, 0x3000, fuc->r_funcMV[mv]);
+ if (temp != ram_rd32(fuc, gpioMV)) {
+ ram_wr32(fuc, gpiotrig, 1);
+ ram_nsec(fuc, 64000);
+ }
+ }
+
+ if (next->bios.ramcfg_11_02_40 ||
+ next->bios.ramcfg_11_07_10) {
+ ram_mask(fuc, 0x132040, 0x00010000, 0x00010000);
+ ram_nsec(fuc, 20000);
+ }
+
+ if (ram->mode != 2) /*XXX*/ {
+ if (next->bios.ramcfg_11_07_40)
+ ram_mask(fuc, 0x10f670, 0x80000000, 0x80000000);
+ }
+
+ ram_wr32(fuc, 0x10f65c, 0x00000011 * next->bios.rammap_11_11_0c);
+ ram_wr32(fuc, 0x10f6b8, 0x01010101 * next->bios.ramcfg_11_09);
+ ram_wr32(fuc, 0x10f6bc, 0x01010101 * next->bios.ramcfg_11_09);
+
+ mask = 0x00010000;
+ data = 0x00000000;
+ if (!next->bios.ramcfg_11_02_80)
+ data |= 0x03000000;
+ if (!next->bios.ramcfg_11_02_40)
+ data |= 0x00002000;
+ if (!next->bios.ramcfg_11_07_10)
+ data |= 0x00004000;
+ if (!next->bios.ramcfg_11_07_08)
+ data |= 0x00000003;
+ else
+ data |= 0x14000000;
+ ram_mask(fuc, 0x10f824, mask, data);
+ ram_nsec(fuc, 1000);
+
+ if (next->bios.ramcfg_11_08_01)
+ data = 0x00100000;
+ else
+ data = 0x00000000;
+ ram_mask(fuc, 0x10f82c, 0x00100000, data);
+
+ /* PFB timing */
+ ram_mask(fuc, 0x10f248, 0xffffffff, next->bios.timing[10]);
+ ram_mask(fuc, 0x10f290, 0xffffffff, next->bios.timing[0]);
+ ram_mask(fuc, 0x10f294, 0xffffffff, next->bios.timing[1]);
+ ram_mask(fuc, 0x10f298, 0xffffffff, next->bios.timing[2]);
+ ram_mask(fuc, 0x10f29c, 0xffffffff, next->bios.timing[3]);
+ ram_mask(fuc, 0x10f2a0, 0xffffffff, next->bios.timing[4]);
+ ram_mask(fuc, 0x10f2a4, 0xffffffff, next->bios.timing[5]);
+ ram_mask(fuc, 0x10f2a8, 0xffffffff, next->bios.timing[6]);
+ ram_mask(fuc, 0x10f2ac, 0xffffffff, next->bios.timing[7]);
+ ram_mask(fuc, 0x10f2cc, 0xffffffff, next->bios.timing[8]);
+ ram_mask(fuc, 0x10f2e8, 0xffffffff, next->bios.timing[9]);
+
+ mask = 0x33f00000;
+ data = 0x00000000;
+ if (!next->bios.ramcfg_11_01_04)
+ data |= 0x20200000;
+ if (!next->bios.ramcfg_11_07_80)
+ data |= 0x12800000;
+ /*XXX: see note above about there probably being some condition
+ * for the 10f824 stuff that uses ramcfg 3...
+ */
+ if (next->bios.ramcfg_11_03_f0) {
+ if (next->bios.rammap_11_08_0c) {
+ if (!next->bios.ramcfg_11_07_80)
+ mask |= 0x00000020;
+ else
+ data |= 0x00000020;
+ mask |= 0x08000004;
+ }
+ data |= 0x04000000;
+ } else {
+ mask |= 0x44000020;
+ data |= 0x08000004;
+ }
+
+ ram_mask(fuc, 0x10f808, mask, data);
+
+ ram_wr32(fuc, 0x10f870, 0x11111111 * next->bios.ramcfg_11_03_0f);
+
+ ram_mask(fuc, 0x10f250, 0x000003f0, next->bios.timing_20_2c_003f << 4);
+
+ data = (next->bios.timing[10] & 0x7f000000) >> 24;
+ if (data < next->bios.timing_20_2c_1fc0)
+ data = next->bios.timing_20_2c_1fc0;
+ ram_mask(fuc, 0x10f24c, 0x7f000000, data << 24);
+
+ ram_mask(fuc, 0x10f224, 0x001f0000, next->bios.timing_20_30_f8 << 16);
+
+ ram_wr32(fuc, 0x10f090, 0x4000007f);
+ ram_nsec(fuc, 1000);
+
+ ram_wr32(fuc, 0x10f314, 0x00000001); /* PRECHARGE */
+ ram_wr32(fuc, 0x10f310, 0x00000001); /* REFRESH */
+ ram_wr32(fuc, 0x10f210, 0x80000000); /* REFRESH_AUTO = 1 */
+ ram_nsec(fuc, 1000);
+
+ ram_nuke(fuc, mr[0]);
+ ram_mask(fuc, mr[0], 0x100, 0x100);
+ ram_mask(fuc, mr[0], 0x100, 0x000);
+
+ ram_mask(fuc, mr[2], 0xfff, ram->base.mr[2]);
+ ram_wr32(fuc, mr[0], ram->base.mr[0]);
+ ram_nsec(fuc, 1000);
+
+ ram_nuke(fuc, mr[0]);
+ ram_mask(fuc, mr[0], 0x100, 0x100);
+ ram_mask(fuc, mr[0], 0x100, 0x000);
+
+ if (vc == 0 && ram_have(fuc, gpio2E)) {
+ u32 temp = ram_mask(fuc, gpio2E, 0x3000, fuc->r_func2E[0]);
+ if (temp != ram_rd32(fuc, gpio2E)) {
+ ram_wr32(fuc, gpiotrig, 1);
+ ram_nsec(fuc, 20000);
+ }
+ }
+
+ if (ram->mode != 2) {
+ ram_mask(fuc, 0x10f830, 0x01000000, 0x01000000);
+ ram_mask(fuc, 0x10f830, 0x01000000, 0x00000000);
+ }
+
+ ram_mask(fuc, 0x10f200, 0x80000000, 0x80000000);
+ ram_wr32(fuc, 0x10f318, 0x00000001); /* NOP? */
+ ram_mask(fuc, 0x10f200, 0x80000000, 0x00000000);
+ ram_nsec(fuc, 1000);
+
+ ram_unblock(fuc);
+ ram_wr32(fuc, 0x62c000, 0x0f0f0f00);
+
+ if (next->bios.rammap_11_08_01)
+ data = 0x00000800;
+ else
+ data = 0x00000000;
+ ram_mask(fuc, 0x10f200, 0x00000800, data);
+ return 0;
+}
+
+/*******************************************************************************
+ * main hooks
+ ******************************************************************************/
+
+static int
+gk104_ram_calc_data(struct nvkm_fb *pfb, u32 khz, struct nvkm_ram_data *data)
+{
+ struct gk104_ram *ram = (void *)pfb->ram;
+ struct nvkm_ram_data *cfg;
+ u32 mhz = khz / 1000;
+
+ list_for_each_entry(cfg, &ram->cfg, head) {
+ if (mhz >= cfg->bios.rammap_min &&
+ mhz <= cfg->bios.rammap_max) {
+ *data = *cfg;
+ data->freq = khz;
+ return 0;
+ }
+ }
+
+ nv_error(ram, "ramcfg data for %dMHz not found\n", mhz);
+ return -EINVAL;
+}
+
+static int
+gk104_ram_calc_xits(struct nvkm_fb *pfb, struct nvkm_ram_data *next)
+{
+ struct gk104_ram *ram = (void *)pfb->ram;
+ struct gk104_ramfuc *fuc = &ram->fuc;
+ int refclk, i;
+ int ret;
+
+ ret = ram_init(fuc, pfb);
+ if (ret)
+ return ret;
+
+ ram->mode = (next->freq > fuc->refpll.vco1.max_freq) ? 2 : 1;
+ ram->from = ram_rd32(fuc, 0x1373f4) & 0x0000000f;
+
+ /* XXX: this is *not* what nvidia do. on fermi nvidia generally
+ * select, based on some unknown condition, one of the two possible
+ * reference frequencies listed in the vbios table for mempll and
+ * program refpll to that frequency.
+ *
+ * so far, i've seen very weird values being chosen by nvidia on
+ * kepler boards, no idea how/why they're chosen.
+ */
+ refclk = next->freq;
+ if (ram->mode == 2)
+ refclk = fuc->mempll.refclk;
+
+ /* calculate refpll coefficients */
+ ret = gt215_pll_calc(nv_subdev(pfb), &fuc->refpll, refclk, &ram->N1,
+ &ram->fN1, &ram->M1, &ram->P1);
+ fuc->mempll.refclk = ret;
+ if (ret <= 0) {
+ nv_error(pfb, "unable to calc refpll\n");
+ return -EINVAL;
+ }
+
+ /* calculate mempll coefficients, if we're using it */
+ if (ram->mode == 2) {
+ /* post-divider doesn't work... the reg takes the values but
+ * appears to completely ignore it. there *is* a bit at
+ * bit 28 that appears to divide the clock by 2 if set.
+ */
+ fuc->mempll.min_p = 1;
+ fuc->mempll.max_p = 2;
+
+ ret = gt215_pll_calc(nv_subdev(pfb), &fuc->mempll, next->freq,
+ &ram->N2, NULL, &ram->M2, &ram->P2);
+ if (ret <= 0) {
+ nv_error(pfb, "unable to calc mempll\n");
+ return -EINVAL;
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(fuc->r_mr); i++) {
+ if (ram_have(fuc, mr[i]))
+ ram->base.mr[i] = ram_rd32(fuc, mr[i]);
+ }
+ ram->base.freq = next->freq;
+
+ switch (ram->base.type) {
+ case NV_MEM_TYPE_DDR3:
+ ret = nvkm_sddr3_calc(&ram->base);
+ if (ret == 0)
+ ret = gk104_ram_calc_sddr3(pfb, next->freq);
+ break;
+ case NV_MEM_TYPE_GDDR5:
+ ret = nvkm_gddr5_calc(&ram->base, ram->pnuts != 0);
+ if (ret == 0)
+ ret = gk104_ram_calc_gddr5(pfb, next->freq);
+ break;
+ default:
+ ret = -ENOSYS;
+ break;
+ }
+
+ return ret;
+}
+
+static int
+gk104_ram_calc(struct nvkm_fb *pfb, u32 freq)
+{
+ struct nvkm_clk *clk = nvkm_clk(pfb);
+ struct gk104_ram *ram = (void *)pfb->ram;
+ struct nvkm_ram_data *xits = &ram->base.xition;
+ struct nvkm_ram_data *copy;
+ int ret;
+
+ if (ram->base.next == NULL) {
+ ret = gk104_ram_calc_data(pfb, clk->read(clk, nv_clk_src_mem),
+ &ram->base.former);
+ if (ret)
+ return ret;
+
+ ret = gk104_ram_calc_data(pfb, freq, &ram->base.target);
+ if (ret)
+ return ret;
+
+ if (ram->base.target.freq < ram->base.former.freq) {
+ *xits = ram->base.target;
+ copy = &ram->base.former;
+ } else {
+ *xits = ram->base.former;
+ copy = &ram->base.target;
+ }
+
+ xits->bios.ramcfg_11_02_04 = copy->bios.ramcfg_11_02_04;
+ xits->bios.ramcfg_11_02_03 = copy->bios.ramcfg_11_02_03;
+ xits->bios.timing_20_30_07 = copy->bios.timing_20_30_07;
+
+ ram->base.next = &ram->base.target;
+ if (memcmp(xits, &ram->base.former, sizeof(xits->bios)))
+ ram->base.next = &ram->base.xition;
+ } else {
+ BUG_ON(ram->base.next != &ram->base.xition);
+ ram->base.next = &ram->base.target;
+ }
+
+ return gk104_ram_calc_xits(pfb, ram->base.next);
+}
+
+static void
+gk104_ram_prog_0(struct nvkm_fb *pfb, u32 freq)
+{
+ struct gk104_ram *ram = (void *)pfb->ram;
+ struct nvkm_ram_data *cfg;
+ u32 mhz = freq / 1000;
+ u32 mask, data;
+
+ list_for_each_entry(cfg, &ram->cfg, head) {
+ if (mhz >= cfg->bios.rammap_min &&
+ mhz <= cfg->bios.rammap_max)
+ break;
+ }
+
+ if (&cfg->head == &ram->cfg)
+ return;
+
+ if (mask = 0, data = 0, ram->diff.rammap_11_0a_03fe) {
+ data |= cfg->bios.rammap_11_0a_03fe << 12;
+ mask |= 0x001ff000;
+ }
+ if (ram->diff.rammap_11_09_01ff) {
+ data |= cfg->bios.rammap_11_09_01ff;
+ mask |= 0x000001ff;
+ }
+ nv_mask(pfb, 0x10f468, mask, data);
+
+ if (mask = 0, data = 0, ram->diff.rammap_11_0a_0400) {
+ data |= cfg->bios.rammap_11_0a_0400;
+ mask |= 0x00000001;
+ }
+ nv_mask(pfb, 0x10f420, mask, data);
+
+ if (mask = 0, data = 0, ram->diff.rammap_11_0a_0800) {
+ data |= cfg->bios.rammap_11_0a_0800;
+ mask |= 0x00000001;
+ }
+ nv_mask(pfb, 0x10f430, mask, data);
+
+ if (mask = 0, data = 0, ram->diff.rammap_11_0b_01f0) {
+ data |= cfg->bios.rammap_11_0b_01f0;
+ mask |= 0x0000001f;
+ }
+ nv_mask(pfb, 0x10f400, mask, data);
+
+ if (mask = 0, data = 0, ram->diff.rammap_11_0b_0200) {
+ data |= cfg->bios.rammap_11_0b_0200 << 9;
+ mask |= 0x00000200;
+ }
+ nv_mask(pfb, 0x10f410, mask, data);
+
+ if (mask = 0, data = 0, ram->diff.rammap_11_0d) {
+ data |= cfg->bios.rammap_11_0d << 16;
+ mask |= 0x00ff0000;
+ }
+ if (ram->diff.rammap_11_0f) {
+ data |= cfg->bios.rammap_11_0f << 8;
+ mask |= 0x0000ff00;
+ }
+ nv_mask(pfb, 0x10f440, mask, data);
+
+ if (mask = 0, data = 0, ram->diff.rammap_11_0e) {
+ data |= cfg->bios.rammap_11_0e << 8;
+ mask |= 0x0000ff00;
+ }
+ if (ram->diff.rammap_11_0b_0800) {
+ data |= cfg->bios.rammap_11_0b_0800 << 7;
+ mask |= 0x00000080;
+ }
+ if (ram->diff.rammap_11_0b_0400) {
+ data |= cfg->bios.rammap_11_0b_0400 << 5;
+ mask |= 0x00000020;
+ }
+ nv_mask(pfb, 0x10f444, mask, data);
+}
+
+static int
+gk104_ram_prog(struct nvkm_fb *pfb)
+{
+ struct nvkm_device *device = nv_device(pfb);
+ struct gk104_ram *ram = (void *)pfb->ram;
+ struct gk104_ramfuc *fuc = &ram->fuc;
+ struct nvkm_ram_data *next = ram->base.next;
+
+ if (!nvkm_boolopt(device->cfgopt, "NvMemExec", true)) {
+ ram_exec(fuc, false);
+ return (ram->base.next == &ram->base.xition);
+ }
+
+ gk104_ram_prog_0(pfb, 1000);
+ ram_exec(fuc, true);
+ gk104_ram_prog_0(pfb, next->freq);
+
+ return (ram->base.next == &ram->base.xition);
+}
+
+static void
+gk104_ram_tidy(struct nvkm_fb *pfb)
+{
+ struct gk104_ram *ram = (void *)pfb->ram;
+ struct gk104_ramfuc *fuc = &ram->fuc;
+ ram->base.next = NULL;
+ ram_exec(fuc, false);
+}
+
+struct gk104_ram_train {
+ u16 mask;
+ struct nvbios_M0209S remap;
+ struct nvbios_M0209S type00;
+ struct nvbios_M0209S type01;
+ struct nvbios_M0209S type04;
+ struct nvbios_M0209S type06;
+ struct nvbios_M0209S type07;
+ struct nvbios_M0209S type08;
+ struct nvbios_M0209S type09;
+};
+
+static int
+gk104_ram_train_type(struct nvkm_fb *pfb, int i, u8 ramcfg,
+ struct gk104_ram_train *train)
+{
+ struct nvkm_bios *bios = nvkm_bios(pfb);
+ struct nvbios_M0205E M0205E;
+ struct nvbios_M0205S M0205S;
+ struct nvbios_M0209E M0209E;
+ struct nvbios_M0209S *remap = &train->remap;
+ struct nvbios_M0209S *value;
+ u8 ver, hdr, cnt, len;
+ u32 data;
+
+ /* determine type of data for this index */
+ if (!(data = nvbios_M0205Ep(bios, i, &ver, &hdr, &cnt, &len, &M0205E)))
+ return -ENOENT;
+
+ switch (M0205E.type) {
+ case 0x00: value = &train->type00; break;
+ case 0x01: value = &train->type01; break;
+ case 0x04: value = &train->type04; break;
+ case 0x06: value = &train->type06; break;
+ case 0x07: value = &train->type07; break;
+ case 0x08: value = &train->type08; break;
+ case 0x09: value = &train->type09; break;
+ default:
+ return 0;
+ }
+
+ /* training data index determined by ramcfg strap */
+ if (!(data = nvbios_M0205Sp(bios, i, ramcfg, &ver, &hdr, &M0205S)))
+ return -EINVAL;
+ i = M0205S.data;
+
+ /* training data format information */
+ if (!(data = nvbios_M0209Ep(bios, i, &ver, &hdr, &cnt, &len, &M0209E)))
+ return -EINVAL;
+
+ /* ... and the raw data */
+ if (!(data = nvbios_M0209Sp(bios, i, 0, &ver, &hdr, value)))
+ return -EINVAL;
+
+ if (M0209E.v02_07 == 2) {
+ /* of course! why wouldn't we have a pointer to another entry
+ * in the same table, and use the first one as an array of
+ * remap indices...
+ */
+ if (!(data = nvbios_M0209Sp(bios, M0209E.v03, 0, &ver, &hdr,
+ remap)))
+ return -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(value->data); i++)
+ value->data[i] = remap->data[value->data[i]];
+ } else
+ if (M0209E.v02_07 != 1)
+ return -EINVAL;
+
+ train->mask |= 1 << M0205E.type;
+ return 0;
+}
+
+static int
+gk104_ram_train_init_0(struct nvkm_fb *pfb, struct gk104_ram_train *train)
+{
+ int i, j;
+
+ if ((train->mask & 0x03d3) != 0x03d3) {
+ nv_warn(pfb, "missing link training data\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < 0x30; i++) {
+ for (j = 0; j < 8; j += 4) {
+ nv_wr32(pfb, 0x10f968 + j, 0x00000000 | (i << 8));
+ nv_wr32(pfb, 0x10f920 + j, 0x00000000 |
+ train->type08.data[i] << 4 |
+ train->type06.data[i]);
+ nv_wr32(pfb, 0x10f918 + j, train->type00.data[i]);
+ nv_wr32(pfb, 0x10f920 + j, 0x00000100 |
+ train->type09.data[i] << 4 |
+ train->type07.data[i]);
+ nv_wr32(pfb, 0x10f918 + j, train->type01.data[i]);
+ }
+ }
+
+ for (j = 0; j < 8; j += 4) {
+ for (i = 0; i < 0x100; i++) {
+ nv_wr32(pfb, 0x10f968 + j, i);
+ nv_wr32(pfb, 0x10f900 + j, train->type04.data[i]);
+ }
+ }
+
+ return 0;
+}
+
+static int
+gk104_ram_train_init(struct nvkm_fb *pfb)
+{
+ u8 ramcfg = nvbios_ramcfg_index(nv_subdev(pfb));
+ struct gk104_ram_train *train;
+ int ret = -ENOMEM, i;
+
+ if ((train = kzalloc(sizeof(*train), GFP_KERNEL))) {
+ for (i = 0; i < 0x100; i++) {
+ ret = gk104_ram_train_type(pfb, i, ramcfg, train);
+ if (ret && ret != -ENOENT)
+ break;
+ }
+ }
+
+ switch (pfb->ram->type) {
+ case NV_MEM_TYPE_GDDR5:
+ ret = gk104_ram_train_init_0(pfb, train);
+ break;
+ default:
+ ret = 0;
+ break;
+ }
+
+ kfree(train);
+ return ret;
+}
+
+int
+gk104_ram_init(struct nvkm_object *object)
+{
+ struct nvkm_fb *pfb = (void *)object->parent;
+ struct gk104_ram *ram = (void *)object;
+ struct nvkm_bios *bios = nvkm_bios(pfb);
+ u8 ver, hdr, cnt, len, snr, ssz;
+ u32 data, save;
+ int ret, i;
+
+ ret = nvkm_ram_init(&ram->base);
+ if (ret)
+ return ret;
+
+ /* run a bunch of tables from rammap table. there's actually
+ * individual pointers for each rammap entry too, but, nvidia
+ * seem to just run the last two entries' scripts early on in
+ * their init, and never again.. we'll just run 'em all once
+ * for now.
+ *
+ * i strongly suspect that each script is for a separate mode
+ * (likely selected by 0x10f65c's lower bits?), and the
+ * binary driver skips the one that's already been setup by
+ * the init tables.
+ */
+ data = nvbios_rammapTe(bios, &ver, &hdr, &cnt, &len, &snr, &ssz);
+ if (!data || hdr < 0x15)
+ return -EINVAL;
+
+ cnt = nv_ro08(bios, data + 0x14); /* guess at count */
+ data = nv_ro32(bios, data + 0x10); /* guess u32... */
+ save = nv_rd32(pfb, 0x10f65c) & 0x000000f0;
+ for (i = 0; i < cnt; i++, data += 4) {
+ if (i != save >> 4) {
+ nv_mask(pfb, 0x10f65c, 0x000000f0, i << 4);
+ nvbios_exec(&(struct nvbios_init) {
+ .subdev = nv_subdev(pfb),
+ .bios = bios,
+ .offset = nv_ro32(bios, data),
+ .execute = 1,
+ });
+ }
+ }
+ nv_mask(pfb, 0x10f65c, 0x000000f0, save);
+ nv_mask(pfb, 0x10f584, 0x11000000, 0x00000000);
+ nv_wr32(pfb, 0x10ecc0, 0xffffffff);
+ nv_mask(pfb, 0x10f160, 0x00000010, 0x00000010);
+
+ return gk104_ram_train_init(pfb);
+}
+
+static int
+gk104_ram_ctor_data(struct gk104_ram *ram, u8 ramcfg, int i)
+{
+ struct nvkm_fb *pfb = (void *)nv_object(ram)->parent;
+ struct nvkm_bios *bios = nvkm_bios(pfb);
+ struct nvkm_ram_data *cfg;
+ struct nvbios_ramcfg *d = &ram->diff;
+ struct nvbios_ramcfg *p, *n;
+ u8 ver, hdr, cnt, len;
+ u32 data;
+ int ret;
+
+ if (!(cfg = kmalloc(sizeof(*cfg), GFP_KERNEL)))
+ return -ENOMEM;
+ p = &list_last_entry(&ram->cfg, typeof(*cfg), head)->bios;
+ n = &cfg->bios;
+
+ /* memory config data for a range of target frequencies */
+ data = nvbios_rammapEp(bios, i, &ver, &hdr, &cnt, &len, &cfg->bios);
+ if (ret = -ENOENT, !data)
+ goto done;
+ if (ret = -ENOSYS, ver != 0x11 || hdr < 0x12)
+ goto done;
+
+ /* ... and a portion specific to the attached memory */
+ data = nvbios_rammapSp(bios, data, ver, hdr, cnt, len, ramcfg,
+ &ver, &hdr, &cfg->bios);
+ if (ret = -EINVAL, !data)
+ goto done;
+ if (ret = -ENOSYS, ver != 0x11 || hdr < 0x0a)
+ goto done;
+
+ /* lookup memory timings, if bios says they're present */
+ if (cfg->bios.ramcfg_timing != 0xff) {
+ data = nvbios_timingEp(bios, cfg->bios.ramcfg_timing,
+ &ver, &hdr, &cnt, &len,
+ &cfg->bios);
+ if (ret = -EINVAL, !data)
+ goto done;
+ if (ret = -ENOSYS, ver != 0x20 || hdr < 0x33)
+ goto done;
+ }
+
+ list_add_tail(&cfg->head, &ram->cfg);
+ if (ret = 0, i == 0)
+ goto done;
+
+ d->rammap_11_0a_03fe |= p->rammap_11_0a_03fe != n->rammap_11_0a_03fe;
+ d->rammap_11_09_01ff |= p->rammap_11_09_01ff != n->rammap_11_09_01ff;
+ d->rammap_11_0a_0400 |= p->rammap_11_0a_0400 != n->rammap_11_0a_0400;
+ d->rammap_11_0a_0800 |= p->rammap_11_0a_0800 != n->rammap_11_0a_0800;
+ d->rammap_11_0b_01f0 |= p->rammap_11_0b_01f0 != n->rammap_11_0b_01f0;
+ d->rammap_11_0b_0200 |= p->rammap_11_0b_0200 != n->rammap_11_0b_0200;
+ d->rammap_11_0d |= p->rammap_11_0d != n->rammap_11_0d;
+ d->rammap_11_0f |= p->rammap_11_0f != n->rammap_11_0f;
+ d->rammap_11_0e |= p->rammap_11_0e != n->rammap_11_0e;
+ d->rammap_11_0b_0800 |= p->rammap_11_0b_0800 != n->rammap_11_0b_0800;
+ d->rammap_11_0b_0400 |= p->rammap_11_0b_0400 != n->rammap_11_0b_0400;
+ d->ramcfg_11_01_01 |= p->ramcfg_11_01_01 != n->ramcfg_11_01_01;
+ d->ramcfg_11_01_02 |= p->ramcfg_11_01_02 != n->ramcfg_11_01_02;
+ d->ramcfg_11_01_10 |= p->ramcfg_11_01_10 != n->ramcfg_11_01_10;
+ d->ramcfg_11_02_03 |= p->ramcfg_11_02_03 != n->ramcfg_11_02_03;
+ d->ramcfg_11_08_20 |= p->ramcfg_11_08_20 != n->ramcfg_11_08_20;
+ d->timing_20_30_07 |= p->timing_20_30_07 != n->timing_20_30_07;
+done:
+ if (ret)
+ kfree(cfg);
+ return ret;
+}
+
+static void
+gk104_ram_dtor(struct nvkm_object *object)
+{
+ struct gk104_ram *ram = (void *)object;
+ struct nvkm_ram_data *cfg, *tmp;
+
+ list_for_each_entry_safe(cfg, tmp, &ram->cfg, head) {
+ kfree(cfg);
+ }
+
+ nvkm_ram_destroy(&ram->base);
+}
+
+static int
+gk104_ram_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 size,
+ struct nvkm_object **pobject)
+{
+ struct nvkm_fb *pfb = nvkm_fb(parent);
+ struct nvkm_bios *bios = nvkm_bios(pfb);
+ struct nvkm_gpio *gpio = nvkm_gpio(pfb);
+ struct dcb_gpio_func func;
+ struct gk104_ram *ram;
+ int ret, i;
+ u8 ramcfg = nvbios_ramcfg_index(nv_subdev(pfb));
+ u32 tmp;
+
+ ret = gf100_ram_create(parent, engine, oclass, 0x022554, &ram);
+ *pobject = nv_object(ram);
+ if (ret)
+ return ret;
+
+ INIT_LIST_HEAD(&ram->cfg);
+
+ switch (ram->base.type) {
+ case NV_MEM_TYPE_DDR3:
+ case NV_MEM_TYPE_GDDR5:
+ ram->base.calc = gk104_ram_calc;
+ ram->base.prog = gk104_ram_prog;
+ ram->base.tidy = gk104_ram_tidy;
+ break;
+ default:
+ nv_warn(pfb, "reclocking of this RAM type is unsupported\n");
+ break;
+ }
+
+ /* calculate a mask of differently configured memory partitions,
+ * because, of course reclocking wasn't complicated enough
+ * already without having to treat some of them differently to
+ * the others....
+ */
+ ram->parts = nv_rd32(pfb, 0x022438);
+ ram->pmask = nv_rd32(pfb, 0x022554);
+ ram->pnuts = 0;
+ for (i = 0, tmp = 0; i < ram->parts; i++) {
+ if (!(ram->pmask & (1 << i))) {
+ u32 cfg1 = nv_rd32(pfb, 0x110204 + (i * 0x1000));
+ if (tmp && tmp != cfg1) {
+ ram->pnuts |= (1 << i);
+ continue;
+ }
+ tmp = cfg1;
+ }
+ }
+
+ /* parse bios data for all rammap table entries up-front, and
+ * build information on whether certain fields differ between
+ * any of the entries.
+ *
+ * the binary driver appears to completely ignore some fields
+ * when all entries contain the same value. at first, it was
+ * hoped that these were mere optimisations and the bios init
+ * tables had configured as per the values here, but there is
+ * evidence now to suggest that this isn't the case and we do
+ * need to treat this condition as a "don't touch" indicator.
+ */
+ for (i = 0; !ret; i++) {
+ ret = gk104_ram_ctor_data(ram, ramcfg, i);
+ if (ret && ret != -ENOENT) {
+ nv_error(pfb, "failed to parse ramcfg data\n");
+ return ret;
+ }
+ }
+
+ /* parse bios data for both pll's */
+ ret = nvbios_pll_parse(bios, 0x0c, &ram->fuc.refpll);
+ if (ret) {
+ nv_error(pfb, "mclk refpll data not found\n");
+ return ret;
+ }
+
+ ret = nvbios_pll_parse(bios, 0x04, &ram->fuc.mempll);
+ if (ret) {
+ nv_error(pfb, "mclk pll data not found\n");
+ return ret;
+ }
+
+ /* lookup memory voltage gpios */
+ ret = gpio->find(gpio, 0, 0x18, DCB_GPIO_UNUSED, &func);
+ if (ret == 0) {
+ ram->fuc.r_gpioMV = ramfuc_reg(0x00d610 + (func.line * 0x04));
+ ram->fuc.r_funcMV[0] = (func.log[0] ^ 2) << 12;
+ ram->fuc.r_funcMV[1] = (func.log[1] ^ 2) << 12;
+ }
+
+ ret = gpio->find(gpio, 0, 0x2e, DCB_GPIO_UNUSED, &func);
+ if (ret == 0) {
+ ram->fuc.r_gpio2E = ramfuc_reg(0x00d610 + (func.line * 0x04));
+ ram->fuc.r_func2E[0] = (func.log[0] ^ 2) << 12;
+ ram->fuc.r_func2E[1] = (func.log[1] ^ 2) << 12;
+ }
+
+ ram->fuc.r_gpiotrig = ramfuc_reg(0x00d604);
+
+ ram->fuc.r_0x132020 = ramfuc_reg(0x132020);
+ ram->fuc.r_0x132028 = ramfuc_reg(0x132028);
+ ram->fuc.r_0x132024 = ramfuc_reg(0x132024);
+ ram->fuc.r_0x132030 = ramfuc_reg(0x132030);
+ ram->fuc.r_0x132034 = ramfuc_reg(0x132034);
+ ram->fuc.r_0x132000 = ramfuc_reg(0x132000);
+ ram->fuc.r_0x132004 = ramfuc_reg(0x132004);
+ ram->fuc.r_0x132040 = ramfuc_reg(0x132040);
+
+ ram->fuc.r_0x10f248 = ramfuc_reg(0x10f248);
+ ram->fuc.r_0x10f290 = ramfuc_reg(0x10f290);
+ ram->fuc.r_0x10f294 = ramfuc_reg(0x10f294);
+ ram->fuc.r_0x10f298 = ramfuc_reg(0x10f298);
+ ram->fuc.r_0x10f29c = ramfuc_reg(0x10f29c);
+ ram->fuc.r_0x10f2a0 = ramfuc_reg(0x10f2a0);
+ ram->fuc.r_0x10f2a4 = ramfuc_reg(0x10f2a4);
+ ram->fuc.r_0x10f2a8 = ramfuc_reg(0x10f2a8);
+ ram->fuc.r_0x10f2ac = ramfuc_reg(0x10f2ac);
+ ram->fuc.r_0x10f2cc = ramfuc_reg(0x10f2cc);
+ ram->fuc.r_0x10f2e8 = ramfuc_reg(0x10f2e8);
+ ram->fuc.r_0x10f250 = ramfuc_reg(0x10f250);
+ ram->fuc.r_0x10f24c = ramfuc_reg(0x10f24c);
+ ram->fuc.r_0x10fec4 = ramfuc_reg(0x10fec4);
+ ram->fuc.r_0x10fec8 = ramfuc_reg(0x10fec8);
+ ram->fuc.r_0x10f604 = ramfuc_reg(0x10f604);
+ ram->fuc.r_0x10f614 = ramfuc_reg(0x10f614);
+ ram->fuc.r_0x10f610 = ramfuc_reg(0x10f610);
+ ram->fuc.r_0x100770 = ramfuc_reg(0x100770);
+ ram->fuc.r_0x100778 = ramfuc_reg(0x100778);
+ ram->fuc.r_0x10f224 = ramfuc_reg(0x10f224);
+
+ ram->fuc.r_0x10f870 = ramfuc_reg(0x10f870);
+ ram->fuc.r_0x10f698 = ramfuc_reg(0x10f698);
+ ram->fuc.r_0x10f694 = ramfuc_reg(0x10f694);
+ ram->fuc.r_0x10f6b8 = ramfuc_reg(0x10f6b8);
+ ram->fuc.r_0x10f808 = ramfuc_reg(0x10f808);
+ ram->fuc.r_0x10f670 = ramfuc_reg(0x10f670);
+ ram->fuc.r_0x10f60c = ramfuc_reg(0x10f60c);
+ ram->fuc.r_0x10f830 = ramfuc_reg(0x10f830);
+ ram->fuc.r_0x1373ec = ramfuc_reg(0x1373ec);
+ ram->fuc.r_0x10f800 = ramfuc_reg(0x10f800);
+ ram->fuc.r_0x10f82c = ramfuc_reg(0x10f82c);
+
+ ram->fuc.r_0x10f978 = ramfuc_reg(0x10f978);
+ ram->fuc.r_0x10f910 = ramfuc_reg(0x10f910);
+ ram->fuc.r_0x10f914 = ramfuc_reg(0x10f914);
+
+ switch (ram->base.type) {
+ case NV_MEM_TYPE_GDDR5:
+ ram->fuc.r_mr[0] = ramfuc_reg(0x10f300);
+ ram->fuc.r_mr[1] = ramfuc_reg(0x10f330);
+ ram->fuc.r_mr[2] = ramfuc_reg(0x10f334);
+ ram->fuc.r_mr[3] = ramfuc_reg(0x10f338);
+ ram->fuc.r_mr[4] = ramfuc_reg(0x10f33c);
+ ram->fuc.r_mr[5] = ramfuc_reg(0x10f340);
+ ram->fuc.r_mr[6] = ramfuc_reg(0x10f344);
+ ram->fuc.r_mr[7] = ramfuc_reg(0x10f348);
+ ram->fuc.r_mr[8] = ramfuc_reg(0x10f354);
+ ram->fuc.r_mr[15] = ramfuc_reg(0x10f34c);
+ break;
+ case NV_MEM_TYPE_DDR3:
+ ram->fuc.r_mr[0] = ramfuc_reg(0x10f300);
+ ram->fuc.r_mr[2] = ramfuc_reg(0x10f320);
+ break;
+ default:
+ break;
+ }
+
+ ram->fuc.r_0x62c000 = ramfuc_reg(0x62c000);
+ ram->fuc.r_0x10f200 = ramfuc_reg(0x10f200);
+ ram->fuc.r_0x10f210 = ramfuc_reg(0x10f210);
+ ram->fuc.r_0x10f310 = ramfuc_reg(0x10f310);
+ ram->fuc.r_0x10f314 = ramfuc_reg(0x10f314);
+ ram->fuc.r_0x10f318 = ramfuc_reg(0x10f318);
+ ram->fuc.r_0x10f090 = ramfuc_reg(0x10f090);
+ ram->fuc.r_0x10f69c = ramfuc_reg(0x10f69c);
+ ram->fuc.r_0x10f824 = ramfuc_reg(0x10f824);
+ ram->fuc.r_0x1373f0 = ramfuc_reg(0x1373f0);
+ ram->fuc.r_0x1373f4 = ramfuc_reg(0x1373f4);
+ ram->fuc.r_0x137320 = ramfuc_reg(0x137320);
+ ram->fuc.r_0x10f65c = ramfuc_reg(0x10f65c);
+ ram->fuc.r_0x10f6bc = ramfuc_reg(0x10f6bc);
+ ram->fuc.r_0x100710 = ramfuc_reg(0x100710);
+ ram->fuc.r_0x100750 = ramfuc_reg(0x100750);
+ return 0;
+}
+
+struct nvkm_oclass
+gk104_ram_oclass = {
+ .handle = 0,
+ .ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = gk104_ram_ctor,
+ .dtor = gk104_ram_dtor,
+ .init = gk104_ram_init,
+ .fini = _nvkm_ram_fini,
+ }
+};
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgk20a.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgk20a.c
new file mode 100644
index 000000000000..5f30db140b47
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgk20a.c
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+#include "priv.h"
+
+#include <core/device.h>
+
+struct gk20a_mem {
+ struct nvkm_mem base;
+ void *cpuaddr;
+ dma_addr_t handle;
+};
+#define to_gk20a_mem(m) container_of(m, struct gk20a_mem, base)
+
+static void
+gk20a_ram_put(struct nvkm_fb *pfb, struct nvkm_mem **pmem)
+{
+ struct device *dev = nv_device_base(nv_device(pfb));
+ struct gk20a_mem *mem = to_gk20a_mem(*pmem);
+
+ *pmem = NULL;
+ if (unlikely(mem == NULL))
+ return;
+
+ if (likely(mem->cpuaddr))
+ dma_free_coherent(dev, mem->base.size << PAGE_SHIFT,
+ mem->cpuaddr, mem->handle);
+
+ kfree(mem->base.pages);
+ kfree(mem);
+}
+
+static int
+gk20a_ram_get(struct nvkm_fb *pfb, u64 size, u32 align, u32 ncmin,
+ u32 memtype, struct nvkm_mem **pmem)
+{
+ struct device *dev = nv_device_base(nv_device(pfb));
+ struct gk20a_mem *mem;
+ u32 type = memtype & 0xff;
+ u32 npages, order;
+ int i;
+
+ nv_debug(pfb, "%s: size: %llx align: %x, ncmin: %x\n", __func__, size,
+ align, ncmin);
+
+ npages = size >> PAGE_SHIFT;
+ if (npages == 0)
+ npages = 1;
+
+ if (align == 0)
+ align = PAGE_SIZE;
+ align >>= PAGE_SHIFT;
+
+ /* round alignment to the next power of 2, if needed */
+ order = fls(align);
+ if ((align & (align - 1)) == 0)
+ order--;
+ align = BIT(order);
+
+ /* ensure returned address is correctly aligned */
+ npages = max(align, npages);
+
+ mem = kzalloc(sizeof(*mem), GFP_KERNEL);
+ if (!mem)
+ return -ENOMEM;
+
+ mem->base.size = npages;
+ mem->base.memtype = type;
+
+ mem->base.pages = kzalloc(sizeof(dma_addr_t) * npages, GFP_KERNEL);
+ if (!mem->base.pages) {
+ kfree(mem);
+ return -ENOMEM;
+ }
+
+ *pmem = &mem->base;
+
+ mem->cpuaddr = dma_alloc_coherent(dev, npages << PAGE_SHIFT,
+ &mem->handle, GFP_KERNEL);
+ if (!mem->cpuaddr) {
+ nv_error(pfb, "%s: cannot allocate memory!\n", __func__);
+ gk20a_ram_put(pfb, pmem);
+ return -ENOMEM;
+ }
+
+ align <<= PAGE_SHIFT;
+
+ /* alignment check */
+ if (unlikely(mem->handle & (align - 1)))
+ nv_warn(pfb, "memory not aligned as requested: %pad (0x%x)\n",
+ &mem->handle, align);
+
+ nv_debug(pfb, "alloc size: 0x%x, align: 0x%x, paddr: %pad, vaddr: %p\n",
+ npages << PAGE_SHIFT, align, &mem->handle, mem->cpuaddr);
+
+ for (i = 0; i < npages; i++)
+ mem->base.pages[i] = mem->handle + (PAGE_SIZE * i);
+
+ mem->base.offset = (u64)mem->base.pages[0];
+ return 0;
+}
+
+static int
+gk20a_ram_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 datasize,
+ struct nvkm_object **pobject)
+{
+ struct nvkm_ram *ram;
+ int ret;
+
+ ret = nvkm_ram_create(parent, engine, oclass, &ram);
+ *pobject = nv_object(ram);
+ if (ret)
+ return ret;
+ ram->type = NV_MEM_TYPE_STOLEN;
+ ram->size = get_num_physpages() << PAGE_SHIFT;
+
+ ram->get = gk20a_ram_get;
+ ram->put = gk20a_ram_put;
+ return 0;
+}
+
+struct nvkm_oclass
+gk20a_ram_oclass = {
+ .ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = gk20a_ram_ctor,
+ .dtor = _nvkm_ram_dtor,
+ .init = _nvkm_ram_init,
+ .fini = _nvkm_ram_fini,
+ },
+};
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgm107.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgm107.c
new file mode 100644
index 000000000000..a298b39f55c5
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgm107.c
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "gf100.h"
+
+struct gm107_ram {
+ struct nvkm_ram base;
+};
+
+static int
+gm107_ram_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 size,
+ struct nvkm_object **pobject)
+{
+ struct gm107_ram *ram;
+ int ret;
+
+ ret = gf100_ram_create(parent, engine, oclass, 0x021c14, &ram);
+ *pobject = nv_object(ram);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+struct nvkm_oclass
+gm107_ram_oclass = {
+ .handle = 0,
+ .ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = gm107_ram_ctor,
+ .dtor = _nvkm_ram_dtor,
+ .init = gk104_ram_init,
+ .fini = _nvkm_ram_fini,
+ }
+};
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgt215.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgt215.c
new file mode 100644
index 000000000000..24176401b49b
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgt215.c
@@ -0,0 +1,1012 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ * Roy Spliet <rspliet@eclipso.eu>
+ */
+
+#include "ramfuc.h"
+#include "nv50.h"
+
+#include <core/device.h>
+#include <core/option.h>
+#include <subdev/bios.h>
+#include <subdev/bios/M0205.h>
+#include <subdev/bios/rammap.h>
+#include <subdev/bios/timing.h>
+#include <subdev/clk/gt215.h>
+#include <subdev/gpio.h>
+
+/* XXX: Remove when memx gains GPIO support */
+extern int nv50_gpio_location(int line, u32 *reg, u32 *shift);
+
+struct gt215_ramfuc {
+ struct ramfuc base;
+ struct ramfuc_reg r_0x001610;
+ struct ramfuc_reg r_0x001700;
+ struct ramfuc_reg r_0x002504;
+ struct ramfuc_reg r_0x004000;
+ struct ramfuc_reg r_0x004004;
+ struct ramfuc_reg r_0x004018;
+ struct ramfuc_reg r_0x004128;
+ struct ramfuc_reg r_0x004168;
+ struct ramfuc_reg r_0x100080;
+ struct ramfuc_reg r_0x100200;
+ struct ramfuc_reg r_0x100210;
+ struct ramfuc_reg r_0x100220[9];
+ struct ramfuc_reg r_0x100264;
+ struct ramfuc_reg r_0x1002d0;
+ struct ramfuc_reg r_0x1002d4;
+ struct ramfuc_reg r_0x1002dc;
+ struct ramfuc_reg r_0x10053c;
+ struct ramfuc_reg r_0x1005a0;
+ struct ramfuc_reg r_0x1005a4;
+ struct ramfuc_reg r_0x100700;
+ struct ramfuc_reg r_0x100714;
+ struct ramfuc_reg r_0x100718;
+ struct ramfuc_reg r_0x10071c;
+ struct ramfuc_reg r_0x100720;
+ struct ramfuc_reg r_0x100760;
+ struct ramfuc_reg r_0x1007a0;
+ struct ramfuc_reg r_0x1007e0;
+ struct ramfuc_reg r_0x100da0;
+ struct ramfuc_reg r_0x10f804;
+ struct ramfuc_reg r_0x1110e0;
+ struct ramfuc_reg r_0x111100;
+ struct ramfuc_reg r_0x111104;
+ struct ramfuc_reg r_0x1111e0;
+ struct ramfuc_reg r_0x111400;
+ struct ramfuc_reg r_0x611200;
+ struct ramfuc_reg r_mr[4];
+ struct ramfuc_reg r_gpioFBVREF;
+};
+
+struct gt215_ltrain {
+ enum {
+ NVA3_TRAIN_UNKNOWN,
+ NVA3_TRAIN_UNSUPPORTED,
+ NVA3_TRAIN_ONCE,
+ NVA3_TRAIN_EXEC,
+ NVA3_TRAIN_DONE
+ } state;
+ u32 r_100720;
+ u32 r_1111e0;
+ u32 r_111400;
+ struct nvkm_mem *mem;
+};
+
+struct gt215_ram {
+ struct nvkm_ram base;
+ struct gt215_ramfuc fuc;
+ struct gt215_ltrain ltrain;
+};
+
+void
+gt215_link_train_calc(u32 *vals, struct gt215_ltrain *train)
+{
+ int i, lo, hi;
+ u8 median[8], bins[4] = {0, 0, 0, 0}, bin = 0, qty = 0;
+
+ for (i = 0; i < 8; i++) {
+ for (lo = 0; lo < 0x40; lo++) {
+ if (!(vals[lo] & 0x80000000))
+ continue;
+ if (vals[lo] & (0x101 << i))
+ break;
+ }
+
+ if (lo == 0x40)
+ return;
+
+ for (hi = lo + 1; hi < 0x40; hi++) {
+ if (!(vals[lo] & 0x80000000))
+ continue;
+ if (!(vals[hi] & (0x101 << i))) {
+ hi--;
+ break;
+ }
+ }
+
+ median[i] = ((hi - lo) >> 1) + lo;
+ bins[(median[i] & 0xf0) >> 4]++;
+ median[i] += 0x30;
+ }
+
+ /* Find the best value for 0x1111e0 */
+ for (i = 0; i < 4; i++) {
+ if (bins[i] > qty) {
+ bin = i + 3;
+ qty = bins[i];
+ }
+ }
+
+ train->r_100720 = 0;
+ for (i = 0; i < 8; i++) {
+ median[i] = max(median[i], (u8) (bin << 4));
+ median[i] = min(median[i], (u8) ((bin << 4) | 0xf));
+
+ train->r_100720 |= ((median[i] & 0x0f) << (i << 2));
+ }
+
+ train->r_1111e0 = 0x02000000 | (bin * 0x101);
+ train->r_111400 = 0x0;
+}
+
+/*
+ * Link training for (at least) DDR3
+ */
+int
+gt215_link_train(struct nvkm_fb *pfb)
+{
+ struct nvkm_bios *bios = nvkm_bios(pfb);
+ struct gt215_ram *ram = (void *)pfb->ram;
+ struct nvkm_clk *clk = nvkm_clk(pfb);
+ struct gt215_ltrain *train = &ram->ltrain;
+ struct nvkm_device *device = nv_device(pfb);
+ struct gt215_ramfuc *fuc = &ram->fuc;
+ u32 *result, r1700;
+ int ret, i;
+ struct nvbios_M0205T M0205T = { 0 };
+ u8 ver, hdr, cnt, len, snr, ssz;
+ unsigned int clk_current;
+ unsigned long flags;
+ unsigned long *f = &flags;
+
+ if (nvkm_boolopt(device->cfgopt, "NvMemExec", true) != true)
+ return -ENOSYS;
+
+ /* XXX: Multiple partitions? */
+ result = kmalloc(64 * sizeof(u32), GFP_KERNEL);
+ if (!result)
+ return -ENOMEM;
+
+ train->state = NVA3_TRAIN_EXEC;
+
+ /* Clock speeds for training and back */
+ nvbios_M0205Tp(bios, &ver, &hdr, &cnt, &len, &snr, &ssz, &M0205T);
+ if (M0205T.freq == 0)
+ return -ENOENT;
+
+ clk_current = clk->read(clk, nv_clk_src_mem);
+
+ ret = gt215_clk_pre(clk, f);
+ if (ret)
+ goto out;
+
+ /* First: clock up/down */
+ ret = ram->base.calc(pfb, (u32) M0205T.freq * 1000);
+ if (ret)
+ goto out;
+
+ /* Do this *after* calc, eliminates write in script */
+ nv_wr32(pfb, 0x111400, 0x00000000);
+ /* XXX: Magic writes that improve train reliability? */
+ nv_mask(pfb, 0x100674, 0x0000ffff, 0x00000000);
+ nv_mask(pfb, 0x1005e4, 0x0000ffff, 0x00000000);
+ nv_mask(pfb, 0x100b0c, 0x000000ff, 0x00000000);
+ nv_wr32(pfb, 0x100c04, 0x00000400);
+
+ /* Now the training script */
+ r1700 = ram_rd32(fuc, 0x001700);
+
+ ram_mask(fuc, 0x100200, 0x00000800, 0x00000000);
+ ram_wr32(fuc, 0x611200, 0x3300);
+ ram_wait_vblank(fuc);
+ ram_wait(fuc, 0x611200, 0x00000003, 0x00000000, 500000);
+ ram_mask(fuc, 0x001610, 0x00000083, 0x00000003);
+ ram_mask(fuc, 0x100080, 0x00000020, 0x00000000);
+ ram_mask(fuc, 0x10f804, 0x80000000, 0x00000000);
+ ram_wr32(fuc, 0x001700, 0x00000000);
+
+ ram_train(fuc);
+
+ /* Reset */
+ ram_mask(fuc, 0x10f804, 0x80000000, 0x80000000);
+ ram_wr32(fuc, 0x10053c, 0x0);
+ ram_wr32(fuc, 0x100720, train->r_100720);
+ ram_wr32(fuc, 0x1111e0, train->r_1111e0);
+ ram_wr32(fuc, 0x111400, train->r_111400);
+ ram_nuke(fuc, 0x100080);
+ ram_mask(fuc, 0x100080, 0x00000020, 0x00000020);
+ ram_nsec(fuc, 1000);
+
+ ram_wr32(fuc, 0x001700, r1700);
+ ram_mask(fuc, 0x001610, 0x00000083, 0x00000080);
+ ram_wr32(fuc, 0x611200, 0x3330);
+ ram_mask(fuc, 0x100200, 0x00000800, 0x00000800);
+
+ ram_exec(fuc, true);
+
+ ram->base.calc(pfb, clk_current);
+ ram_exec(fuc, true);
+
+ /* Post-processing, avoids flicker */
+ nv_mask(pfb, 0x616308, 0x10, 0x10);
+ nv_mask(pfb, 0x616b08, 0x10, 0x10);
+
+ gt215_clk_post(clk, f);
+
+ ram_train_result(pfb, result, 64);
+ for (i = 0; i < 64; i++)
+ nv_debug(pfb, "Train: %08x", result[i]);
+ gt215_link_train_calc(result, train);
+
+ nv_debug(pfb, "Train: %08x %08x %08x", train->r_100720,
+ train->r_1111e0, train->r_111400);
+
+ kfree(result);
+
+ train->state = NVA3_TRAIN_DONE;
+
+ return ret;
+
+out:
+ if(ret == -EBUSY)
+ f = NULL;
+
+ train->state = NVA3_TRAIN_UNSUPPORTED;
+
+ gt215_clk_post(clk, f);
+ return ret;
+}
+
+int
+gt215_link_train_init(struct nvkm_fb *pfb)
+{
+ static const u32 pattern[16] = {
+ 0xaaaaaaaa, 0xcccccccc, 0xdddddddd, 0xeeeeeeee,
+ 0x00000000, 0x11111111, 0x44444444, 0xdddddddd,
+ 0x33333333, 0x55555555, 0x77777777, 0x66666666,
+ 0x99999999, 0x88888888, 0xeeeeeeee, 0xbbbbbbbb,
+ };
+ struct nvkm_bios *bios = nvkm_bios(pfb);
+ struct gt215_ram *ram = (void *)pfb->ram;
+ struct gt215_ltrain *train = &ram->ltrain;
+ struct nvkm_mem *mem;
+ struct nvbios_M0205E M0205E;
+ u8 ver, hdr, cnt, len;
+ u32 r001700;
+ int ret, i = 0;
+
+ train->state = NVA3_TRAIN_UNSUPPORTED;
+
+ /* We support type "5"
+ * XXX: training pattern table appears to be unused for this routine */
+ if (!nvbios_M0205Ep(bios, i, &ver, &hdr, &cnt, &len, &M0205E))
+ return -ENOENT;
+
+ if (M0205E.type != 5)
+ return 0;
+
+ train->state = NVA3_TRAIN_ONCE;
+
+ ret = pfb->ram->get(pfb, 0x8000, 0x10000, 0, 0x800, &ram->ltrain.mem);
+ if (ret)
+ return ret;
+
+ mem = ram->ltrain.mem;
+
+ nv_wr32(pfb, 0x100538, 0x10000000 | (mem->offset >> 16));
+ nv_wr32(pfb, 0x1005a8, 0x0000ffff);
+ nv_mask(pfb, 0x10f800, 0x00000001, 0x00000001);
+
+ for (i = 0; i < 0x30; i++) {
+ nv_wr32(pfb, 0x10f8c0, (i << 8) | i);
+ nv_wr32(pfb, 0x10f900, pattern[i % 16]);
+ }
+
+ for (i = 0; i < 0x30; i++) {
+ nv_wr32(pfb, 0x10f8e0, (i << 8) | i);
+ nv_wr32(pfb, 0x10f920, pattern[i % 16]);
+ }
+
+ /* And upload the pattern */
+ r001700 = nv_rd32(pfb, 0x1700);
+ nv_wr32(pfb, 0x1700, mem->offset >> 16);
+ for (i = 0; i < 16; i++)
+ nv_wr32(pfb, 0x700000 + (i << 2), pattern[i]);
+ for (i = 0; i < 16; i++)
+ nv_wr32(pfb, 0x700100 + (i << 2), pattern[i]);
+ nv_wr32(pfb, 0x1700, r001700);
+
+ train->r_100720 = nv_rd32(pfb, 0x100720);
+ train->r_1111e0 = nv_rd32(pfb, 0x1111e0);
+ train->r_111400 = nv_rd32(pfb, 0x111400);
+ return 0;
+}
+
+void
+gt215_link_train_fini(struct nvkm_fb *pfb)
+{
+ struct gt215_ram *ram = (void *)pfb->ram;
+
+ if (ram->ltrain.mem)
+ pfb->ram->put(pfb, &ram->ltrain.mem);
+}
+
+/*
+ * RAM reclocking
+ */
+#define T(t) cfg->timing_10_##t
+static int
+gt215_ram_timing_calc(struct nvkm_fb *pfb, u32 *timing)
+{
+ struct gt215_ram *ram = (void *)pfb->ram;
+ struct nvbios_ramcfg *cfg = &ram->base.target.bios;
+ int tUNK_base, tUNK_40_0, prevCL;
+ u32 cur2, cur3, cur7, cur8;
+
+ cur2 = nv_rd32(pfb, 0x100228);
+ cur3 = nv_rd32(pfb, 0x10022c);
+ cur7 = nv_rd32(pfb, 0x10023c);
+ cur8 = nv_rd32(pfb, 0x100240);
+
+
+ switch ((!T(CWL)) * ram->base.type) {
+ case NV_MEM_TYPE_DDR2:
+ T(CWL) = T(CL) - 1;
+ break;
+ case NV_MEM_TYPE_GDDR3:
+ T(CWL) = ((cur2 & 0xff000000) >> 24) + 1;
+ break;
+ }
+
+ prevCL = (cur3 & 0x000000ff) + 1;
+ tUNK_base = ((cur7 & 0x00ff0000) >> 16) - prevCL;
+
+ timing[0] = (T(RP) << 24 | T(RAS) << 16 | T(RFC) << 8 | T(RC));
+ timing[1] = (T(WR) + 1 + T(CWL)) << 24 |
+ max_t(u8,T(18), 1) << 16 |
+ (T(WTR) + 1 + T(CWL)) << 8 |
+ (5 + T(CL) - T(CWL));
+ timing[2] = (T(CWL) - 1) << 24 |
+ (T(RRD) << 16) |
+ (T(RCDWR) << 8) |
+ T(RCDRD);
+ timing[3] = (cur3 & 0x00ff0000) |
+ (0x30 + T(CL)) << 24 |
+ (0xb + T(CL)) << 8 |
+ (T(CL) - 1);
+ timing[4] = T(20) << 24 |
+ T(21) << 16 |
+ T(13) << 8 |
+ T(13);
+ timing[5] = T(RFC) << 24 |
+ max_t(u8,T(RCDRD), T(RCDWR)) << 16 |
+ max_t(u8, (T(CWL) + 6), (T(CL) + 2)) << 8 |
+ T(RP);
+ timing[6] = (0x5a + T(CL)) << 16 |
+ max_t(u8, 1, (6 - T(CL) + T(CWL))) << 8 |
+ (0x50 + T(CL) - T(CWL));
+ timing[7] = (cur7 & 0xff000000) |
+ ((tUNK_base + T(CL)) << 16) |
+ 0x202;
+ timing[8] = cur8 & 0xffffff00;
+
+ switch (ram->base.type) {
+ case NV_MEM_TYPE_DDR2:
+ case NV_MEM_TYPE_GDDR3:
+ tUNK_40_0 = prevCL - (cur8 & 0xff);
+ if (tUNK_40_0 > 0)
+ timing[8] |= T(CL);
+ break;
+ default:
+ break;
+ }
+
+ nv_debug(pfb, "Entry: 220: %08x %08x %08x %08x\n",
+ timing[0], timing[1], timing[2], timing[3]);
+ nv_debug(pfb, " 230: %08x %08x %08x %08x\n",
+ timing[4], timing[5], timing[6], timing[7]);
+ nv_debug(pfb, " 240: %08x\n", timing[8]);
+ return 0;
+}
+#undef T
+
+static void
+nvkm_sddr2_dll_reset(struct gt215_ramfuc *fuc)
+{
+ ram_mask(fuc, mr[0], 0x100, 0x100);
+ ram_nsec(fuc, 1000);
+ ram_mask(fuc, mr[0], 0x100, 0x000);
+ ram_nsec(fuc, 1000);
+}
+
+static void
+nvkm_sddr3_dll_disable(struct gt215_ramfuc *fuc, u32 *mr)
+{
+ u32 mr1_old = ram_rd32(fuc, mr[1]);
+
+ if (!(mr1_old & 0x1)) {
+ ram_wr32(fuc, 0x1002d4, 0x00000001);
+ ram_wr32(fuc, mr[1], mr[1]);
+ ram_nsec(fuc, 1000);
+ }
+}
+
+static void
+nvkm_gddr3_dll_disable(struct gt215_ramfuc *fuc, u32 *mr)
+{
+ u32 mr1_old = ram_rd32(fuc, mr[1]);
+
+ if (!(mr1_old & 0x40)) {
+ ram_wr32(fuc, mr[1], mr[1]);
+ ram_nsec(fuc, 1000);
+ }
+}
+
+static void
+gt215_ram_lock_pll(struct gt215_ramfuc *fuc, struct gt215_clk_info *mclk)
+{
+ ram_wr32(fuc, 0x004004, mclk->pll);
+ ram_mask(fuc, 0x004000, 0x00000001, 0x00000001);
+ ram_mask(fuc, 0x004000, 0x00000010, 0x00000000);
+ ram_wait(fuc, 0x004000, 0x00020000, 0x00020000, 64000);
+ ram_mask(fuc, 0x004000, 0x00000010, 0x00000010);
+}
+
+static void
+gt215_ram_fbvref(struct gt215_ramfuc *fuc, u32 val)
+{
+ struct nvkm_gpio *gpio = nvkm_gpio(fuc->base.pfb);
+ struct dcb_gpio_func func;
+ u32 reg, sh, gpio_val;
+ int ret;
+
+ if (gpio->get(gpio, 0, 0x2e, DCB_GPIO_UNUSED) != val) {
+ ret = gpio->find(gpio, 0, 0x2e, DCB_GPIO_UNUSED, &func);
+ if (ret)
+ return;
+
+ nv50_gpio_location(func.line, &reg, &sh);
+ gpio_val = ram_rd32(fuc, gpioFBVREF);
+ if (gpio_val & (8 << sh))
+ val = !val;
+
+ ram_mask(fuc, gpioFBVREF, (0x3 << sh), ((val | 0x2) << sh));
+ ram_nsec(fuc, 20000);
+ }
+}
+
+static int
+gt215_ram_calc(struct nvkm_fb *pfb, u32 freq)
+{
+ struct nvkm_bios *bios = nvkm_bios(pfb);
+ struct gt215_ram *ram = (void *)pfb->ram;
+ struct gt215_ramfuc *fuc = &ram->fuc;
+ struct gt215_ltrain *train = &ram->ltrain;
+ struct gt215_clk_info mclk;
+ struct nvkm_ram_data *next;
+ u8 ver, hdr, cnt, len, strap;
+ u32 data;
+ u32 r004018, r100760, r100da0, r111100, ctrl;
+ u32 unk714, unk718, unk71c;
+ int ret, i;
+ u32 timing[9];
+ bool pll2pll;
+
+ next = &ram->base.target;
+ next->freq = freq;
+ ram->base.next = next;
+
+ if (ram->ltrain.state == NVA3_TRAIN_ONCE)
+ gt215_link_train(pfb);
+
+ /* lookup memory config data relevant to the target frequency */
+ i = 0;
+ data = nvbios_rammapEm(bios, freq / 1000, &ver, &hdr, &cnt, &len,
+ &next->bios);
+ if (!data || ver != 0x10 || hdr < 0x05) {
+ nv_error(pfb, "invalid/missing rammap entry\n");
+ return -EINVAL;
+ }
+
+ /* locate specific data set for the attached memory */
+ strap = nvbios_ramcfg_index(nv_subdev(pfb));
+ if (strap >= cnt) {
+ nv_error(pfb, "invalid ramcfg strap\n");
+ return -EINVAL;
+ }
+
+ data = nvbios_rammapSp(bios, data, ver, hdr, cnt, len, strap,
+ &ver, &hdr, &next->bios);
+ if (!data || ver != 0x10 || hdr < 0x09) {
+ nv_error(pfb, "invalid/missing ramcfg entry\n");
+ return -EINVAL;
+ }
+
+ /* lookup memory timings, if bios says they're present */
+ if (next->bios.ramcfg_timing != 0xff) {
+ data = nvbios_timingEp(bios, next->bios.ramcfg_timing,
+ &ver, &hdr, &cnt, &len,
+ &next->bios);
+ if (!data || ver != 0x10 || hdr < 0x17) {
+ nv_error(pfb, "invalid/missing timing entry\n");
+ return -EINVAL;
+ }
+ }
+
+ ret = gt215_pll_info(nvkm_clk(pfb), 0x12, 0x4000, freq, &mclk);
+ if (ret < 0) {
+ nv_error(pfb, "failed mclk calculation\n");
+ return ret;
+ }
+
+ gt215_ram_timing_calc(pfb, timing);
+
+ ret = ram_init(fuc, pfb);
+ if (ret)
+ return ret;
+
+ /* Determine ram-specific MR values */
+ ram->base.mr[0] = ram_rd32(fuc, mr[0]);
+ ram->base.mr[1] = ram_rd32(fuc, mr[1]);
+ ram->base.mr[2] = ram_rd32(fuc, mr[2]);
+
+ switch (ram->base.type) {
+ case NV_MEM_TYPE_DDR2:
+ ret = nvkm_sddr2_calc(&ram->base);
+ break;
+ case NV_MEM_TYPE_DDR3:
+ ret = nvkm_sddr3_calc(&ram->base);
+ break;
+ case NV_MEM_TYPE_GDDR3:
+ ret = nvkm_gddr3_calc(&ram->base);
+ break;
+ default:
+ ret = -ENOSYS;
+ break;
+ }
+
+ if (ret)
+ return ret;
+
+ /* XXX: where the fuck does 750MHz come from? */
+ if (freq <= 750000) {
+ r004018 = 0x10000000;
+ r100760 = 0x22222222;
+ r100da0 = 0x00000010;
+ } else {
+ r004018 = 0x00000000;
+ r100760 = 0x00000000;
+ r100da0 = 0x00000000;
+ }
+
+ if (!next->bios.ramcfg_10_DLLoff)
+ r004018 |= 0x00004000;
+
+ /* pll2pll requires to switch to a safe clock first */
+ ctrl = ram_rd32(fuc, 0x004000);
+ pll2pll = (!(ctrl & 0x00000008)) && mclk.pll;
+
+ /* Pre, NVIDIA does this outside the script */
+ if (next->bios.ramcfg_10_02_10) {
+ ram_mask(fuc, 0x111104, 0x00000600, 0x00000000);
+ } else {
+ ram_mask(fuc, 0x111100, 0x40000000, 0x40000000);
+ ram_mask(fuc, 0x111104, 0x00000180, 0x00000000);
+ }
+ /* Always disable this bit during reclock */
+ ram_mask(fuc, 0x100200, 0x00000800, 0x00000000);
+
+ /* If switching from non-pll to pll, lock before disabling FB */
+ if (mclk.pll && !pll2pll) {
+ ram_mask(fuc, 0x004128, 0x003f3141, mclk.clk | 0x00000101);
+ gt215_ram_lock_pll(fuc, &mclk);
+ }
+
+ /* Start with disabling some CRTCs and PFIFO? */
+ ram_wait_vblank(fuc);
+ ram_wr32(fuc, 0x611200, 0x3300);
+ ram_mask(fuc, 0x002504, 0x1, 0x1);
+ ram_nsec(fuc, 10000);
+ ram_wait(fuc, 0x002504, 0x10, 0x10, 20000); /* XXX: or longer? */
+ ram_block(fuc);
+ ram_nsec(fuc, 2000);
+
+ if (!next->bios.ramcfg_10_02_10) {
+ if (ram->base.type == NV_MEM_TYPE_GDDR3)
+ ram_mask(fuc, 0x111100, 0x04020000, 0x00020000);
+ else
+ ram_mask(fuc, 0x111100, 0x04020000, 0x04020000);
+ }
+
+ /* If we're disabling the DLL, do it now */
+ switch (next->bios.ramcfg_10_DLLoff * ram->base.type) {
+ case NV_MEM_TYPE_DDR3:
+ nvkm_sddr3_dll_disable(fuc, ram->base.mr);
+ break;
+ case NV_MEM_TYPE_GDDR3:
+ nvkm_gddr3_dll_disable(fuc, ram->base.mr);
+ break;
+ }
+
+ if (fuc->r_gpioFBVREF.addr && next->bios.timing_10_ODT)
+ gt215_ram_fbvref(fuc, 0);
+
+ /* Brace RAM for impact */
+ ram_wr32(fuc, 0x1002d4, 0x00000001);
+ ram_wr32(fuc, 0x1002d0, 0x00000001);
+ ram_wr32(fuc, 0x1002d0, 0x00000001);
+ ram_wr32(fuc, 0x100210, 0x00000000);
+ ram_wr32(fuc, 0x1002dc, 0x00000001);
+ ram_nsec(fuc, 2000);
+
+ if (nv_device(pfb)->chipset == 0xa3 && freq <= 500000)
+ ram_mask(fuc, 0x100700, 0x00000006, 0x00000006);
+
+ /* Fiddle with clocks */
+ /* There's 4 scenario's
+ * pll->pll: first switch to a 324MHz clock, set up new PLL, switch
+ * clk->pll: Set up new PLL, switch
+ * pll->clk: Set up clock, switch
+ * clk->clk: Overwrite ctrl and other bits, switch */
+
+ /* Switch to regular clock - 324MHz */
+ if (pll2pll) {
+ ram_mask(fuc, 0x004000, 0x00000004, 0x00000004);
+ ram_mask(fuc, 0x004168, 0x003f3141, 0x00083101);
+ ram_mask(fuc, 0x004000, 0x00000008, 0x00000008);
+ ram_mask(fuc, 0x1110e0, 0x00088000, 0x00088000);
+ ram_wr32(fuc, 0x004018, 0x00001000);
+ gt215_ram_lock_pll(fuc, &mclk);
+ }
+
+ if (mclk.pll) {
+ ram_mask(fuc, 0x004000, 0x00000105, 0x00000105);
+ ram_wr32(fuc, 0x004018, 0x00001000 | r004018);
+ ram_wr32(fuc, 0x100da0, r100da0);
+ } else {
+ ram_mask(fuc, 0x004168, 0x003f3141, mclk.clk | 0x00000101);
+ ram_mask(fuc, 0x004000, 0x00000108, 0x00000008);
+ ram_mask(fuc, 0x1110e0, 0x00088000, 0x00088000);
+ ram_wr32(fuc, 0x004018, 0x00009000 | r004018);
+ ram_wr32(fuc, 0x100da0, r100da0);
+ }
+ ram_nsec(fuc, 20000);
+
+ if (next->bios.rammap_10_04_08) {
+ ram_wr32(fuc, 0x1005a0, next->bios.ramcfg_10_06 << 16 |
+ next->bios.ramcfg_10_05 << 8 |
+ next->bios.ramcfg_10_05);
+ ram_wr32(fuc, 0x1005a4, next->bios.ramcfg_10_08 << 8 |
+ next->bios.ramcfg_10_07);
+ ram_wr32(fuc, 0x10f804, next->bios.ramcfg_10_09_f0 << 20 |
+ next->bios.ramcfg_10_03_0f << 16 |
+ next->bios.ramcfg_10_09_0f |
+ 0x80000000);
+ ram_mask(fuc, 0x10053c, 0x00001000, 0x00000000);
+ } else {
+ if (train->state == NVA3_TRAIN_DONE) {
+ ram_wr32(fuc, 0x100080, 0x1020);
+ ram_mask(fuc, 0x111400, 0xffffffff, train->r_111400);
+ ram_mask(fuc, 0x1111e0, 0xffffffff, train->r_1111e0);
+ ram_mask(fuc, 0x100720, 0xffffffff, train->r_100720);
+ }
+ ram_mask(fuc, 0x10053c, 0x00001000, 0x00001000);
+ ram_mask(fuc, 0x10f804, 0x80000000, 0x00000000);
+ ram_mask(fuc, 0x100760, 0x22222222, r100760);
+ ram_mask(fuc, 0x1007a0, 0x22222222, r100760);
+ ram_mask(fuc, 0x1007e0, 0x22222222, r100760);
+ }
+
+ if (nv_device(pfb)->chipset == 0xa3 && freq > 500000) {
+ ram_mask(fuc, 0x100700, 0x00000006, 0x00000000);
+ }
+
+ /* Final switch */
+ if (mclk.pll) {
+ ram_mask(fuc, 0x1110e0, 0x00088000, 0x00011000);
+ ram_mask(fuc, 0x004000, 0x00000008, 0x00000000);
+ }
+
+ ram_wr32(fuc, 0x1002dc, 0x00000000);
+ ram_wr32(fuc, 0x1002d4, 0x00000001);
+ ram_wr32(fuc, 0x100210, 0x80000000);
+ ram_nsec(fuc, 2000);
+
+ /* Set RAM MR parameters and timings */
+ for (i = 2; i >= 0; i--) {
+ if (ram_rd32(fuc, mr[i]) != ram->base.mr[i]) {
+ ram_wr32(fuc, mr[i], ram->base.mr[i]);
+ ram_nsec(fuc, 1000);
+ }
+ }
+
+ ram_wr32(fuc, 0x100220[3], timing[3]);
+ ram_wr32(fuc, 0x100220[1], timing[1]);
+ ram_wr32(fuc, 0x100220[6], timing[6]);
+ ram_wr32(fuc, 0x100220[7], timing[7]);
+ ram_wr32(fuc, 0x100220[2], timing[2]);
+ ram_wr32(fuc, 0x100220[4], timing[4]);
+ ram_wr32(fuc, 0x100220[5], timing[5]);
+ ram_wr32(fuc, 0x100220[0], timing[0]);
+ ram_wr32(fuc, 0x100220[8], timing[8]);
+
+ /* Misc */
+ ram_mask(fuc, 0x100200, 0x00001000, !next->bios.ramcfg_10_02_08 << 12);
+
+ /* XXX: A lot of "chipset"/"ram type" specific stuff...? */
+ unk714 = ram_rd32(fuc, 0x100714) & ~0xf0000130;
+ unk718 = ram_rd32(fuc, 0x100718) & ~0x00000100;
+ unk71c = ram_rd32(fuc, 0x10071c) & ~0x00000100;
+ r111100 = ram_rd32(fuc, 0x111100) & ~0x3a800000;
+
+ if (next->bios.ramcfg_10_02_04) {
+ switch (ram->base.type) {
+ case NV_MEM_TYPE_DDR3:
+ if (nv_device(pfb)->chipset != 0xa8)
+ r111100 |= 0x00000004;
+ /* no break */
+ case NV_MEM_TYPE_DDR2:
+ r111100 |= 0x08000000;
+ break;
+ default:
+ break;
+ }
+ } else {
+ switch (ram->base.type) {
+ case NV_MEM_TYPE_DDR2:
+ r111100 |= 0x1a800000;
+ unk714 |= 0x00000010;
+ break;
+ case NV_MEM_TYPE_DDR3:
+ if (nv_device(pfb)->chipset == 0xa8) {
+ r111100 |= 0x08000000;
+ } else {
+ r111100 &= ~0x00000004;
+ r111100 |= 0x12800000;
+ }
+ unk714 |= 0x00000010;
+ break;
+ case NV_MEM_TYPE_GDDR3:
+ r111100 |= 0x30000000;
+ unk714 |= 0x00000020;
+ break;
+ default:
+ break;
+ }
+ }
+
+ unk714 |= (next->bios.ramcfg_10_04_01) << 8;
+
+ if (next->bios.ramcfg_10_02_20)
+ unk714 |= 0xf0000000;
+ if (next->bios.ramcfg_10_02_02)
+ unk718 |= 0x00000100;
+ if (next->bios.ramcfg_10_02_01)
+ unk71c |= 0x00000100;
+ if (next->bios.timing_10_24 != 0xff) {
+ unk718 &= ~0xf0000000;
+ unk718 |= next->bios.timing_10_24 << 28;
+ }
+ if (next->bios.ramcfg_10_02_10)
+ r111100 &= ~0x04020000;
+
+ ram_mask(fuc, 0x100714, 0xffffffff, unk714);
+ ram_mask(fuc, 0x10071c, 0xffffffff, unk71c);
+ ram_mask(fuc, 0x100718, 0xffffffff, unk718);
+ ram_mask(fuc, 0x111100, 0xffffffff, r111100);
+
+ if (fuc->r_gpioFBVREF.addr && !next->bios.timing_10_ODT)
+ gt215_ram_fbvref(fuc, 1);
+
+ /* Reset DLL */
+ if (!next->bios.ramcfg_10_DLLoff)
+ nvkm_sddr2_dll_reset(fuc);
+
+ if (ram->base.type == NV_MEM_TYPE_GDDR3) {
+ ram_nsec(fuc, 31000);
+ } else {
+ ram_nsec(fuc, 14000);
+ }
+
+ if (ram->base.type == NV_MEM_TYPE_DDR3) {
+ ram_wr32(fuc, 0x100264, 0x1);
+ ram_nsec(fuc, 2000);
+ }
+
+ ram_nuke(fuc, 0x100700);
+ ram_mask(fuc, 0x100700, 0x01000000, 0x01000000);
+ ram_mask(fuc, 0x100700, 0x01000000, 0x00000000);
+
+ /* Re-enable FB */
+ ram_unblock(fuc);
+ ram_wr32(fuc, 0x611200, 0x3330);
+
+ /* Post fiddlings */
+ if (next->bios.rammap_10_04_02)
+ ram_mask(fuc, 0x100200, 0x00000800, 0x00000800);
+ if (next->bios.ramcfg_10_02_10) {
+ ram_mask(fuc, 0x111104, 0x00000180, 0x00000180);
+ ram_mask(fuc, 0x111100, 0x40000000, 0x00000000);
+ } else {
+ ram_mask(fuc, 0x111104, 0x00000600, 0x00000600);
+ }
+
+ if (mclk.pll) {
+ ram_mask(fuc, 0x004168, 0x00000001, 0x00000000);
+ ram_mask(fuc, 0x004168, 0x00000100, 0x00000000);
+ } else {
+ ram_mask(fuc, 0x004000, 0x00000001, 0x00000000);
+ ram_mask(fuc, 0x004128, 0x00000001, 0x00000000);
+ ram_mask(fuc, 0x004128, 0x00000100, 0x00000000);
+ }
+
+ return 0;
+}
+
+static int
+gt215_ram_prog(struct nvkm_fb *pfb)
+{
+ struct nvkm_device *device = nv_device(pfb);
+ struct gt215_ram *ram = (void *)pfb->ram;
+ struct gt215_ramfuc *fuc = &ram->fuc;
+ bool exec = nvkm_boolopt(device->cfgopt, "NvMemExec", true);
+
+ if (exec) {
+ nv_mask(pfb, 0x001534, 0x2, 0x2);
+
+ ram_exec(fuc, true);
+
+ /* Post-processing, avoids flicker */
+ nv_mask(pfb, 0x002504, 0x1, 0x0);
+ nv_mask(pfb, 0x001534, 0x2, 0x0);
+
+ nv_mask(pfb, 0x616308, 0x10, 0x10);
+ nv_mask(pfb, 0x616b08, 0x10, 0x10);
+ } else {
+ ram_exec(fuc, false);
+ }
+ return 0;
+}
+
+static void
+gt215_ram_tidy(struct nvkm_fb *pfb)
+{
+ struct gt215_ram *ram = (void *)pfb->ram;
+ struct gt215_ramfuc *fuc = &ram->fuc;
+ ram_exec(fuc, false);
+}
+
+static int
+gt215_ram_init(struct nvkm_object *object)
+{
+ struct nvkm_fb *pfb = (void *)object->parent;
+ struct gt215_ram *ram = (void *)object;
+ int ret;
+
+ ret = nvkm_ram_init(&ram->base);
+ if (ret)
+ return ret;
+
+ gt215_link_train_init(pfb);
+ return 0;
+}
+
+static int
+gt215_ram_fini(struct nvkm_object *object, bool suspend)
+{
+ struct nvkm_fb *pfb = (void *)object->parent;
+
+ if (!suspend)
+ gt215_link_train_fini(pfb);
+
+ return 0;
+}
+
+static int
+gt215_ram_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 datasize,
+ struct nvkm_object **pobject)
+{
+ struct nvkm_fb *pfb = nvkm_fb(parent);
+ struct nvkm_gpio *gpio = nvkm_gpio(pfb);
+ struct dcb_gpio_func func;
+ struct gt215_ram *ram;
+ int ret, i;
+ u32 reg, shift;
+
+ ret = nv50_ram_create(parent, engine, oclass, &ram);
+ *pobject = nv_object(ram);
+ if (ret)
+ return ret;
+
+ switch (ram->base.type) {
+ case NV_MEM_TYPE_DDR2:
+ case NV_MEM_TYPE_DDR3:
+ case NV_MEM_TYPE_GDDR3:
+ ram->base.calc = gt215_ram_calc;
+ ram->base.prog = gt215_ram_prog;
+ ram->base.tidy = gt215_ram_tidy;
+ break;
+ default:
+ nv_warn(ram, "reclocking of this ram type unsupported\n");
+ return 0;
+ }
+
+ ram->fuc.r_0x001610 = ramfuc_reg(0x001610);
+ ram->fuc.r_0x001700 = ramfuc_reg(0x001700);
+ ram->fuc.r_0x002504 = ramfuc_reg(0x002504);
+ ram->fuc.r_0x004000 = ramfuc_reg(0x004000);
+ ram->fuc.r_0x004004 = ramfuc_reg(0x004004);
+ ram->fuc.r_0x004018 = ramfuc_reg(0x004018);
+ ram->fuc.r_0x004128 = ramfuc_reg(0x004128);
+ ram->fuc.r_0x004168 = ramfuc_reg(0x004168);
+ ram->fuc.r_0x100080 = ramfuc_reg(0x100080);
+ ram->fuc.r_0x100200 = ramfuc_reg(0x100200);
+ ram->fuc.r_0x100210 = ramfuc_reg(0x100210);
+ for (i = 0; i < 9; i++)
+ ram->fuc.r_0x100220[i] = ramfuc_reg(0x100220 + (i * 4));
+ ram->fuc.r_0x100264 = ramfuc_reg(0x100264);
+ ram->fuc.r_0x1002d0 = ramfuc_reg(0x1002d0);
+ ram->fuc.r_0x1002d4 = ramfuc_reg(0x1002d4);
+ ram->fuc.r_0x1002dc = ramfuc_reg(0x1002dc);
+ ram->fuc.r_0x10053c = ramfuc_reg(0x10053c);
+ ram->fuc.r_0x1005a0 = ramfuc_reg(0x1005a0);
+ ram->fuc.r_0x1005a4 = ramfuc_reg(0x1005a4);
+ ram->fuc.r_0x100700 = ramfuc_reg(0x100700);
+ ram->fuc.r_0x100714 = ramfuc_reg(0x100714);
+ ram->fuc.r_0x100718 = ramfuc_reg(0x100718);
+ ram->fuc.r_0x10071c = ramfuc_reg(0x10071c);
+ ram->fuc.r_0x100720 = ramfuc_reg(0x100720);
+ ram->fuc.r_0x100760 = ramfuc_stride(0x100760, 4, ram->base.part_mask);
+ ram->fuc.r_0x1007a0 = ramfuc_stride(0x1007a0, 4, ram->base.part_mask);
+ ram->fuc.r_0x1007e0 = ramfuc_stride(0x1007e0, 4, ram->base.part_mask);
+ ram->fuc.r_0x100da0 = ramfuc_stride(0x100da0, 4, ram->base.part_mask);
+ ram->fuc.r_0x10f804 = ramfuc_reg(0x10f804);
+ ram->fuc.r_0x1110e0 = ramfuc_stride(0x1110e0, 4, ram->base.part_mask);
+ ram->fuc.r_0x111100 = ramfuc_reg(0x111100);
+ ram->fuc.r_0x111104 = ramfuc_reg(0x111104);
+ ram->fuc.r_0x1111e0 = ramfuc_reg(0x1111e0);
+ ram->fuc.r_0x111400 = ramfuc_reg(0x111400);
+ ram->fuc.r_0x611200 = ramfuc_reg(0x611200);
+
+ if (ram->base.ranks > 1) {
+ ram->fuc.r_mr[0] = ramfuc_reg2(0x1002c0, 0x1002c8);
+ ram->fuc.r_mr[1] = ramfuc_reg2(0x1002c4, 0x1002cc);
+ ram->fuc.r_mr[2] = ramfuc_reg2(0x1002e0, 0x1002e8);
+ ram->fuc.r_mr[3] = ramfuc_reg2(0x1002e4, 0x1002ec);
+ } else {
+ ram->fuc.r_mr[0] = ramfuc_reg(0x1002c0);
+ ram->fuc.r_mr[1] = ramfuc_reg(0x1002c4);
+ ram->fuc.r_mr[2] = ramfuc_reg(0x1002e0);
+ ram->fuc.r_mr[3] = ramfuc_reg(0x1002e4);
+ }
+
+ ret = gpio->find(gpio, 0, 0x2e, DCB_GPIO_UNUSED, &func);
+ if (ret == 0) {
+ nv50_gpio_location(func.line, &reg, &shift);
+ ram->fuc.r_gpioFBVREF = ramfuc_reg(reg);
+ }
+
+ return 0;
+}
+
+struct nvkm_oclass
+gt215_ram_oclass = {
+ .ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = gt215_ram_ctor,
+ .dtor = _nvkm_ram_dtor,
+ .init = gt215_ram_init,
+ .fini = gt215_ram_fini,
+ },
+};
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/rammcp77.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/rammcp77.c
new file mode 100644
index 000000000000..abc18e89a97c
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/rammcp77.c
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "nv50.h"
+
+struct mcp77_ram_priv {
+ struct nvkm_ram base;
+ u64 poller_base;
+};
+
+static int
+mcp77_ram_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 datasize,
+ struct nvkm_object **pobject)
+{
+ u32 rsvd_head = ( 256 * 1024); /* vga memory */
+ u32 rsvd_tail = (1024 * 1024); /* vbios etc */
+ struct nvkm_fb *pfb = nvkm_fb(parent);
+ struct mcp77_ram_priv *priv;
+ int ret;
+
+ ret = nvkm_ram_create(parent, engine, oclass, &priv);
+ *pobject = nv_object(priv);
+ if (ret)
+ return ret;
+
+ priv->base.type = NV_MEM_TYPE_STOLEN;
+ priv->base.stolen = (u64)nv_rd32(pfb, 0x100e10) << 12;
+ priv->base.size = (u64)nv_rd32(pfb, 0x100e14) << 12;
+
+ rsvd_tail += 0x1000;
+ priv->poller_base = priv->base.size - rsvd_tail;
+
+ ret = nvkm_mm_init(&pfb->vram, rsvd_head >> 12,
+ (priv->base.size - (rsvd_head + rsvd_tail)) >> 12,
+ 1);
+ if (ret)
+ return ret;
+
+ priv->base.get = nv50_ram_get;
+ priv->base.put = nv50_ram_put;
+ return 0;
+}
+
+static int
+mcp77_ram_init(struct nvkm_object *object)
+{
+ struct nvkm_fb *pfb = nvkm_fb(object);
+ struct mcp77_ram_priv *priv = (void *)object;
+ int ret;
+ u64 dniso, hostnb, flush;
+
+ ret = nvkm_ram_init(&priv->base);
+ if (ret)
+ return ret;
+
+ dniso = ((priv->base.size - (priv->poller_base + 0x00)) >> 5) - 1;
+ hostnb = ((priv->base.size - (priv->poller_base + 0x20)) >> 5) - 1;
+ flush = ((priv->base.size - (priv->poller_base + 0x40)) >> 5) - 1;
+
+ /* Enable NISO poller for various clients and set their associated
+ * read address, only for MCP77/78 and MCP79/7A. (fd#25701)
+ */
+ nv_wr32(pfb, 0x100c18, dniso);
+ nv_mask(pfb, 0x100c14, 0x00000000, 0x00000001);
+ nv_wr32(pfb, 0x100c1c, hostnb);
+ nv_mask(pfb, 0x100c14, 0x00000000, 0x00000002);
+ nv_wr32(pfb, 0x100c24, flush);
+ nv_mask(pfb, 0x100c14, 0x00000000, 0x00010000);
+ return 0;
+}
+
+struct nvkm_oclass
+mcp77_ram_oclass = {
+ .ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = mcp77_ram_ctor,
+ .dtor = _nvkm_ram_dtor,
+ .init = mcp77_ram_init,
+ .fini = _nvkm_ram_fini,
+ },
+};
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv04.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv04.c
new file mode 100644
index 000000000000..855de1617229
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv04.c
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "priv.h"
+#include "regsnv04.h"
+
+static int
+nv04_ram_create(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 size,
+ struct nvkm_object **pobject)
+{
+ struct nvkm_fb *pfb = nvkm_fb(parent);
+ struct nvkm_ram *ram;
+ u32 boot0 = nv_rd32(pfb, NV04_PFB_BOOT_0);
+ int ret;
+
+ ret = nvkm_ram_create(parent, engine, oclass, &ram);
+ *pobject = nv_object(ram);
+ if (ret)
+ return ret;
+
+ if (boot0 & 0x00000100) {
+ ram->size = ((boot0 >> 12) & 0xf) * 2 + 2;
+ ram->size *= 1024 * 1024;
+ } else {
+ switch (boot0 & NV04_PFB_BOOT_0_RAM_AMOUNT) {
+ case NV04_PFB_BOOT_0_RAM_AMOUNT_32MB:
+ ram->size = 32 * 1024 * 1024;
+ break;
+ case NV04_PFB_BOOT_0_RAM_AMOUNT_16MB:
+ ram->size = 16 * 1024 * 1024;
+ break;
+ case NV04_PFB_BOOT_0_RAM_AMOUNT_8MB:
+ ram->size = 8 * 1024 * 1024;
+ break;
+ case NV04_PFB_BOOT_0_RAM_AMOUNT_4MB:
+ ram->size = 4 * 1024 * 1024;
+ break;
+ }
+ }
+
+ if ((boot0 & 0x00000038) <= 0x10)
+ ram->type = NV_MEM_TYPE_SGRAM;
+ else
+ ram->type = NV_MEM_TYPE_SDRAM;
+
+ return 0;
+}
+
+struct nvkm_oclass
+nv04_ram_oclass = {
+ .handle = 0,
+ .ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv04_ram_create,
+ .dtor = _nvkm_ram_dtor,
+ .init = _nvkm_ram_init,
+ .fini = _nvkm_ram_fini,
+ }
+};
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv10.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv10.c
new file mode 100644
index 000000000000..3b8a1eda5b64
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv10.c
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "priv.h"
+
+static int
+nv10_ram_create(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 size,
+ struct nvkm_object **pobject)
+{
+ struct nvkm_fb *pfb = nvkm_fb(parent);
+ struct nvkm_ram *ram;
+ u32 cfg0 = nv_rd32(pfb, 0x100200);
+ int ret;
+
+ ret = nvkm_ram_create(parent, engine, oclass, &ram);
+ *pobject = nv_object(ram);
+ if (ret)
+ return ret;
+
+ if (cfg0 & 0x00000001)
+ ram->type = NV_MEM_TYPE_DDR1;
+ else
+ ram->type = NV_MEM_TYPE_SDRAM;
+
+ ram->size = nv_rd32(pfb, 0x10020c) & 0xff000000;
+ return 0;
+}
+
+struct nvkm_oclass
+nv10_ram_oclass = {
+ .handle = 0,
+ .ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv10_ram_create,
+ .dtor = _nvkm_ram_dtor,
+ .init = _nvkm_ram_init,
+ .fini = _nvkm_ram_fini,
+ }
+};
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv1a.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv1a.c
new file mode 100644
index 000000000000..fbae05db4ffd
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv1a.c
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "priv.h"
+
+#include <core/device.h>
+
+static int
+nv1a_ram_create(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 size,
+ struct nvkm_object **pobject)
+{
+ struct nvkm_fb *pfb = nvkm_fb(parent);
+ struct nvkm_ram *ram;
+ struct pci_dev *bridge;
+ u32 mem, mib;
+ int ret;
+
+ bridge = pci_get_bus_and_slot(0, PCI_DEVFN(0, 1));
+ if (!bridge) {
+ nv_fatal(pfb, "no bridge device\n");
+ return -ENODEV;
+ }
+
+ ret = nvkm_ram_create(parent, engine, oclass, &ram);
+ *pobject = nv_object(ram);
+ if (ret)
+ return ret;
+
+ if (nv_device(pfb)->chipset == 0x1a) {
+ pci_read_config_dword(bridge, 0x7c, &mem);
+ mib = ((mem >> 6) & 31) + 1;
+ } else {
+ pci_read_config_dword(bridge, 0x84, &mem);
+ mib = ((mem >> 4) & 127) + 1;
+ }
+
+ ram->type = NV_MEM_TYPE_STOLEN;
+ ram->size = mib * 1024 * 1024;
+ return 0;
+}
+
+struct nvkm_oclass
+nv1a_ram_oclass = {
+ .handle = 0,
+ .ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv1a_ram_create,
+ .dtor = _nvkm_ram_dtor,
+ .init = _nvkm_ram_init,
+ .fini = _nvkm_ram_fini,
+ }
+};
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv20.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv20.c
new file mode 100644
index 000000000000..d9e7187bd235
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv20.c
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "priv.h"
+
+static int
+nv20_ram_create(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 size,
+ struct nvkm_object **pobject)
+{
+ struct nvkm_fb *pfb = nvkm_fb(parent);
+ struct nvkm_ram *ram;
+ u32 pbus1218 = nv_rd32(pfb, 0x001218);
+ int ret;
+
+ ret = nvkm_ram_create(parent, engine, oclass, &ram);
+ *pobject = nv_object(ram);
+ if (ret)
+ return ret;
+
+ switch (pbus1218 & 0x00000300) {
+ case 0x00000000: ram->type = NV_MEM_TYPE_SDRAM; break;
+ case 0x00000100: ram->type = NV_MEM_TYPE_DDR1; break;
+ case 0x00000200: ram->type = NV_MEM_TYPE_GDDR3; break;
+ case 0x00000300: ram->type = NV_MEM_TYPE_GDDR2; break;
+ }
+ ram->size = (nv_rd32(pfb, 0x10020c) & 0xff000000);
+ ram->parts = (nv_rd32(pfb, 0x100200) & 0x00000003) + 1;
+ ram->tags = nv_rd32(pfb, 0x100320);
+ return 0;
+}
+
+struct nvkm_oclass
+nv20_ram_oclass = {
+ .handle = 0,
+ .ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv20_ram_create,
+ .dtor = _nvkm_ram_dtor,
+ .init = _nvkm_ram_init,
+ .fini = _nvkm_ram_fini,
+ }
+};
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv40.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv40.c
new file mode 100644
index 000000000000..3d31fa45c1a6
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv40.c
@@ -0,0 +1,212 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "nv40.h"
+
+#include <core/device.h>
+#include <subdev/bios.h>
+#include <subdev/bios/bit.h>
+#include <subdev/bios/init.h>
+#include <subdev/bios/pll.h>
+#include <subdev/clk/pll.h>
+#include <subdev/timer.h>
+
+int
+nv40_ram_calc(struct nvkm_fb *pfb, u32 freq)
+{
+ struct nvkm_bios *bios = nvkm_bios(pfb);
+ struct nv40_ram *ram = (void *)pfb->ram;
+ struct nvbios_pll pll;
+ int N1, M1, N2, M2;
+ int log2P, ret;
+
+ ret = nvbios_pll_parse(bios, 0x04, &pll);
+ if (ret) {
+ nv_error(pfb, "mclk pll data not found\n");
+ return ret;
+ }
+
+ ret = nv04_pll_calc(nv_subdev(pfb), &pll, freq,
+ &N1, &M1, &N2, &M2, &log2P);
+ if (ret < 0)
+ return ret;
+
+ ram->ctrl = 0x80000000 | (log2P << 16);
+ ram->ctrl |= min(pll.bias_p + log2P, (int)pll.max_p) << 20;
+ if (N2 == M2) {
+ ram->ctrl |= 0x00000100;
+ ram->coef = (N1 << 8) | M1;
+ } else {
+ ram->ctrl |= 0x40000000;
+ ram->coef = (N2 << 24) | (M2 << 16) | (N1 << 8) | M1;
+ }
+
+ return 0;
+}
+
+int
+nv40_ram_prog(struct nvkm_fb *pfb)
+{
+ struct nvkm_bios *bios = nvkm_bios(pfb);
+ struct nv40_ram *ram = (void *)pfb->ram;
+ struct bit_entry M;
+ u32 crtc_mask = 0;
+ u8 sr1[2];
+ int i;
+
+ /* determine which CRTCs are active, fetch VGA_SR1 for each */
+ for (i = 0; i < 2; i++) {
+ u32 vbl = nv_rd32(pfb, 0x600808 + (i * 0x2000));
+ u32 cnt = 0;
+ do {
+ if (vbl != nv_rd32(pfb, 0x600808 + (i * 0x2000))) {
+ nv_wr08(pfb, 0x0c03c4 + (i * 0x2000), 0x01);
+ sr1[i] = nv_rd08(pfb, 0x0c03c5 + (i * 0x2000));
+ if (!(sr1[i] & 0x20))
+ crtc_mask |= (1 << i);
+ break;
+ }
+ udelay(1);
+ } while (cnt++ < 32);
+ }
+
+ /* wait for vblank start on active crtcs, disable memory access */
+ for (i = 0; i < 2; i++) {
+ if (!(crtc_mask & (1 << i)))
+ continue;
+ nv_wait(pfb, 0x600808 + (i * 0x2000), 0x00010000, 0x00000000);
+ nv_wait(pfb, 0x600808 + (i * 0x2000), 0x00010000, 0x00010000);
+ nv_wr08(pfb, 0x0c03c4 + (i * 0x2000), 0x01);
+ nv_wr08(pfb, 0x0c03c5 + (i * 0x2000), sr1[i] | 0x20);
+ }
+
+ /* prepare ram for reclocking */
+ nv_wr32(pfb, 0x1002d4, 0x00000001); /* precharge */
+ nv_wr32(pfb, 0x1002d0, 0x00000001); /* refresh */
+ nv_wr32(pfb, 0x1002d0, 0x00000001); /* refresh */
+ nv_mask(pfb, 0x100210, 0x80000000, 0x00000000); /* no auto refresh */
+ nv_wr32(pfb, 0x1002dc, 0x00000001); /* enable self-refresh */
+
+ /* change the PLL of each memory partition */
+ nv_mask(pfb, 0x00c040, 0x0000c000, 0x00000000);
+ switch (nv_device(pfb)->chipset) {
+ case 0x40:
+ case 0x45:
+ case 0x41:
+ case 0x42:
+ case 0x47:
+ nv_mask(pfb, 0x004044, 0xc0771100, ram->ctrl);
+ nv_mask(pfb, 0x00402c, 0xc0771100, ram->ctrl);
+ nv_wr32(pfb, 0x004048, ram->coef);
+ nv_wr32(pfb, 0x004030, ram->coef);
+ case 0x43:
+ case 0x49:
+ case 0x4b:
+ nv_mask(pfb, 0x004038, 0xc0771100, ram->ctrl);
+ nv_wr32(pfb, 0x00403c, ram->coef);
+ default:
+ nv_mask(pfb, 0x004020, 0xc0771100, ram->ctrl);
+ nv_wr32(pfb, 0x004024, ram->coef);
+ break;
+ }
+ udelay(100);
+ nv_mask(pfb, 0x00c040, 0x0000c000, 0x0000c000);
+
+ /* re-enable normal operation of memory controller */
+ nv_wr32(pfb, 0x1002dc, 0x00000000);
+ nv_mask(pfb, 0x100210, 0x80000000, 0x80000000);
+ udelay(100);
+
+ /* execute memory reset script from vbios */
+ if (!bit_entry(bios, 'M', &M)) {
+ struct nvbios_init init = {
+ .subdev = nv_subdev(pfb),
+ .bios = bios,
+ .offset = nv_ro16(bios, M.offset + 0x00),
+ .execute = 1,
+ };
+
+ nvbios_exec(&init);
+ }
+
+ /* make sure we're in vblank (hopefully the same one as before), and
+ * then re-enable crtc memory access
+ */
+ for (i = 0; i < 2; i++) {
+ if (!(crtc_mask & (1 << i)))
+ continue;
+ nv_wait(pfb, 0x600808 + (i * 0x2000), 0x00010000, 0x00010000);
+ nv_wr08(pfb, 0x0c03c4 + (i * 0x2000), 0x01);
+ nv_wr08(pfb, 0x0c03c5 + (i * 0x2000), sr1[i]);
+ }
+
+ return 0;
+}
+
+void
+nv40_ram_tidy(struct nvkm_fb *pfb)
+{
+}
+
+static int
+nv40_ram_create(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 size,
+ struct nvkm_object **pobject)
+{
+ struct nvkm_fb *pfb = nvkm_fb(parent);
+ struct nv40_ram *ram;
+ u32 pbus1218 = nv_rd32(pfb, 0x001218);
+ int ret;
+
+ ret = nvkm_ram_create(parent, engine, oclass, &ram);
+ *pobject = nv_object(ram);
+ if (ret)
+ return ret;
+
+ switch (pbus1218 & 0x00000300) {
+ case 0x00000000: ram->base.type = NV_MEM_TYPE_SDRAM; break;
+ case 0x00000100: ram->base.type = NV_MEM_TYPE_DDR1; break;
+ case 0x00000200: ram->base.type = NV_MEM_TYPE_GDDR3; break;
+ case 0x00000300: ram->base.type = NV_MEM_TYPE_DDR2; break;
+ }
+
+ ram->base.size = nv_rd32(pfb, 0x10020c) & 0xff000000;
+ ram->base.parts = (nv_rd32(pfb, 0x100200) & 0x00000003) + 1;
+ ram->base.tags = nv_rd32(pfb, 0x100320);
+ ram->base.calc = nv40_ram_calc;
+ ram->base.prog = nv40_ram_prog;
+ ram->base.tidy = nv40_ram_tidy;
+ return 0;
+}
+
+
+struct nvkm_oclass
+nv40_ram_oclass = {
+ .handle = 0,
+ .ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv40_ram_create,
+ .dtor = _nvkm_ram_dtor,
+ .init = _nvkm_ram_init,
+ .fini = _nvkm_ram_fini,
+ }
+};
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv41.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv41.c
new file mode 100644
index 000000000000..33c612b1355f
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv41.c
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "nv40.h"
+
+static int
+nv41_ram_create(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 size,
+ struct nvkm_object **pobject)
+{
+ struct nvkm_fb *pfb = nvkm_fb(parent);
+ struct nv40_ram *ram;
+ u32 pfb474 = nv_rd32(pfb, 0x100474);
+ int ret;
+
+ ret = nvkm_ram_create(parent, engine, oclass, &ram);
+ *pobject = nv_object(ram);
+ if (ret)
+ return ret;
+
+ if (pfb474 & 0x00000004)
+ ram->base.type = NV_MEM_TYPE_GDDR3;
+ if (pfb474 & 0x00000002)
+ ram->base.type = NV_MEM_TYPE_DDR2;
+ if (pfb474 & 0x00000001)
+ ram->base.type = NV_MEM_TYPE_DDR1;
+
+ ram->base.size = nv_rd32(pfb, 0x10020c) & 0xff000000;
+ ram->base.parts = (nv_rd32(pfb, 0x100200) & 0x00000003) + 1;
+ ram->base.tags = nv_rd32(pfb, 0x100320);
+ ram->base.calc = nv40_ram_calc;
+ ram->base.prog = nv40_ram_prog;
+ ram->base.tidy = nv40_ram_tidy;
+ return 0;
+}
+
+struct nvkm_oclass
+nv41_ram_oclass = {
+ .handle = 0,
+ .ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv41_ram_create,
+ .dtor = _nvkm_ram_dtor,
+ .init = _nvkm_ram_init,
+ .fini = _nvkm_ram_fini,
+ }
+};
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv44.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv44.c
new file mode 100644
index 000000000000..f575a7246403
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv44.c
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "nv40.h"
+
+static int
+nv44_ram_create(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 size,
+ struct nvkm_object **pobject)
+{
+ struct nvkm_fb *pfb = nvkm_fb(parent);
+ struct nv40_ram *ram;
+ u32 pfb474 = nv_rd32(pfb, 0x100474);
+ int ret;
+
+ ret = nvkm_ram_create(parent, engine, oclass, &ram);
+ *pobject = nv_object(ram);
+ if (ret)
+ return ret;
+
+ if (pfb474 & 0x00000004)
+ ram->base.type = NV_MEM_TYPE_GDDR3;
+ if (pfb474 & 0x00000002)
+ ram->base.type = NV_MEM_TYPE_DDR2;
+ if (pfb474 & 0x00000001)
+ ram->base.type = NV_MEM_TYPE_DDR1;
+
+ ram->base.size = nv_rd32(pfb, 0x10020c) & 0xff000000;
+ ram->base.calc = nv40_ram_calc;
+ ram->base.prog = nv40_ram_prog;
+ ram->base.tidy = nv40_ram_tidy;
+ return 0;
+}
+
+struct nvkm_oclass
+nv44_ram_oclass = {
+ .handle = 0,
+ .ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv44_ram_create,
+ .dtor = _nvkm_ram_dtor,
+ .init = _nvkm_ram_init,
+ .fini = _nvkm_ram_fini,
+ }
+};
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv49.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv49.c
new file mode 100644
index 000000000000..51b44cdb2732
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv49.c
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "nv40.h"
+
+static int
+nv49_ram_create(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 size,
+ struct nvkm_object **pobject)
+{
+ struct nvkm_fb *pfb = nvkm_fb(parent);
+ struct nv40_ram *ram;
+ u32 pfb914 = nv_rd32(pfb, 0x100914);
+ int ret;
+
+ ret = nvkm_ram_create(parent, engine, oclass, &ram);
+ *pobject = nv_object(ram);
+ if (ret)
+ return ret;
+
+ switch (pfb914 & 0x00000003) {
+ case 0x00000000: ram->base.type = NV_MEM_TYPE_DDR1; break;
+ case 0x00000001: ram->base.type = NV_MEM_TYPE_DDR2; break;
+ case 0x00000002: ram->base.type = NV_MEM_TYPE_GDDR3; break;
+ case 0x00000003: break;
+ }
+
+ ram->base.size = nv_rd32(pfb, 0x10020c) & 0xff000000;
+ ram->base.parts = (nv_rd32(pfb, 0x100200) & 0x00000003) + 1;
+ ram->base.tags = nv_rd32(pfb, 0x100320);
+ ram->base.calc = nv40_ram_calc;
+ ram->base.prog = nv40_ram_prog;
+ ram->base.tidy = nv40_ram_tidy;
+ return 0;
+}
+
+struct nvkm_oclass
+nv49_ram_oclass = {
+ .handle = 0,
+ .ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv49_ram_create,
+ .dtor = _nvkm_ram_dtor,
+ .init = _nvkm_ram_init,
+ .fini = _nvkm_ram_fini,
+ }
+};
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv4e.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv4e.c
new file mode 100644
index 000000000000..f3ed1c60d730
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv4e.c
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "priv.h"
+
+static int
+nv4e_ram_create(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 size,
+ struct nvkm_object **pobject)
+{
+ struct nvkm_fb *pfb = nvkm_fb(parent);
+ struct nvkm_ram *ram;
+ int ret;
+
+ ret = nvkm_ram_create(parent, engine, oclass, &ram);
+ *pobject = nv_object(ram);
+ if (ret)
+ return ret;
+
+ ram->size = nv_rd32(pfb, 0x10020c) & 0xff000000;
+ ram->type = NV_MEM_TYPE_STOLEN;
+ return 0;
+}
+
+struct nvkm_oclass
+nv4e_ram_oclass = {
+ .handle = 0,
+ .ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv4e_ram_create,
+ .dtor = _nvkm_ram_dtor,
+ .init = _nvkm_ram_init,
+ .fini = _nvkm_ram_fini,
+ }
+};
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv50.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv50.c
new file mode 100644
index 000000000000..d2c81dd635dc
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv50.c
@@ -0,0 +1,465 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "nv50.h"
+#include "ramseq.h"
+
+#include <core/device.h>
+#include <core/option.h>
+#include <subdev/bios.h>
+#include <subdev/bios/perf.h>
+#include <subdev/bios/pll.h>
+#include <subdev/bios/timing.h>
+#include <subdev/clk/pll.h>
+
+struct nv50_ramseq {
+ struct hwsq base;
+ struct hwsq_reg r_0x002504;
+ struct hwsq_reg r_0x004008;
+ struct hwsq_reg r_0x00400c;
+ struct hwsq_reg r_0x00c040;
+ struct hwsq_reg r_0x100210;
+ struct hwsq_reg r_0x1002d0;
+ struct hwsq_reg r_0x1002d4;
+ struct hwsq_reg r_0x1002dc;
+ struct hwsq_reg r_0x100da0[8];
+ struct hwsq_reg r_0x100e20;
+ struct hwsq_reg r_0x100e24;
+ struct hwsq_reg r_0x611200;
+ struct hwsq_reg r_timing[9];
+ struct hwsq_reg r_mr[4];
+};
+
+struct nv50_ram {
+ struct nvkm_ram base;
+ struct nv50_ramseq hwsq;
+};
+
+#define QFX5800NVA0 1
+
+static int
+nv50_ram_calc(struct nvkm_fb *pfb, u32 freq)
+{
+ struct nvkm_bios *bios = nvkm_bios(pfb);
+ struct nv50_ram *ram = (void *)pfb->ram;
+ struct nv50_ramseq *hwsq = &ram->hwsq;
+ struct nvbios_perfE perfE;
+ struct nvbios_pll mpll;
+ struct {
+ u32 data;
+ u8 size;
+ } ramcfg, timing;
+ u8 ver, hdr, cnt, len, strap;
+ int N1, M1, N2, M2, P;
+ int ret, i;
+
+ /* lookup closest matching performance table entry for frequency */
+ i = 0;
+ do {
+ ramcfg.data = nvbios_perfEp(bios, i++, &ver, &hdr, &cnt,
+ &ramcfg.size, &perfE);
+ if (!ramcfg.data || (ver < 0x25 || ver >= 0x40) ||
+ (ramcfg.size < 2)) {
+ nv_error(pfb, "invalid/missing perftab entry\n");
+ return -EINVAL;
+ }
+ } while (perfE.memory < freq);
+
+ /* locate specific data set for the attached memory */
+ strap = nvbios_ramcfg_index(nv_subdev(pfb));
+ if (strap >= cnt) {
+ nv_error(pfb, "invalid ramcfg strap\n");
+ return -EINVAL;
+ }
+
+ ramcfg.data += hdr + (strap * ramcfg.size);
+
+ /* lookup memory timings, if bios says they're present */
+ strap = nv_ro08(bios, ramcfg.data + 0x01);
+ if (strap != 0xff) {
+ timing.data = nvbios_timingEe(bios, strap, &ver, &hdr,
+ &cnt, &len);
+ if (!timing.data || ver != 0x10 || hdr < 0x12) {
+ nv_error(pfb, "invalid/missing timing entry "
+ "%02x %04x %02x %02x\n",
+ strap, timing.data, ver, hdr);
+ return -EINVAL;
+ }
+ } else {
+ timing.data = 0;
+ }
+
+ ret = ram_init(hwsq, nv_subdev(pfb));
+ if (ret)
+ return ret;
+
+ ram_wait(hwsq, 0x01, 0x00); /* wait for !vblank */
+ ram_wait(hwsq, 0x01, 0x01); /* wait for vblank */
+ ram_wr32(hwsq, 0x611200, 0x00003300);
+ ram_wr32(hwsq, 0x002504, 0x00000001); /* block fifo */
+ ram_nsec(hwsq, 8000);
+ ram_setf(hwsq, 0x10, 0x00); /* disable fb */
+ ram_wait(hwsq, 0x00, 0x01); /* wait for fb disabled */
+
+ ram_wr32(hwsq, 0x1002d4, 0x00000001); /* precharge */
+ ram_wr32(hwsq, 0x1002d0, 0x00000001); /* refresh */
+ ram_wr32(hwsq, 0x1002d0, 0x00000001); /* refresh */
+ ram_wr32(hwsq, 0x100210, 0x00000000); /* disable auto-refresh */
+ ram_wr32(hwsq, 0x1002dc, 0x00000001); /* enable self-refresh */
+
+ ret = nvbios_pll_parse(bios, 0x004008, &mpll);
+ mpll.vco2.max_freq = 0;
+ if (ret == 0) {
+ ret = nv04_pll_calc(nv_subdev(pfb), &mpll, freq,
+ &N1, &M1, &N2, &M2, &P);
+ if (ret == 0)
+ ret = -EINVAL;
+ }
+
+ if (ret < 0)
+ return ret;
+
+ ram_mask(hwsq, 0x00c040, 0xc000c000, 0x0000c000);
+ ram_mask(hwsq, 0x004008, 0x00000200, 0x00000200);
+ ram_mask(hwsq, 0x00400c, 0x0000ffff, (N1 << 8) | M1);
+ ram_mask(hwsq, 0x004008, 0x81ff0000, 0x80000000 | (mpll.bias_p << 19) |
+ (P << 22) | (P << 16));
+#if QFX5800NVA0
+ for (i = 0; i < 8; i++)
+ ram_mask(hwsq, 0x100da0[i], 0x00000000, 0x00000000); /*XXX*/
+#endif
+ ram_nsec(hwsq, 96000); /*XXX*/
+ ram_mask(hwsq, 0x004008, 0x00002200, 0x00002000);
+
+ ram_wr32(hwsq, 0x1002dc, 0x00000000); /* disable self-refresh */
+ ram_wr32(hwsq, 0x100210, 0x80000000); /* enable auto-refresh */
+
+ ram_nsec(hwsq, 12000);
+
+ switch (ram->base.type) {
+ case NV_MEM_TYPE_DDR2:
+ ram_nuke(hwsq, mr[0]); /* force update */
+ ram_mask(hwsq, mr[0], 0x000, 0x000);
+ break;
+ case NV_MEM_TYPE_GDDR3:
+ ram_mask(hwsq, mr[2], 0x000, 0x000);
+ ram_nuke(hwsq, mr[0]); /* force update */
+ ram_mask(hwsq, mr[0], 0x000, 0x000);
+ break;
+ default:
+ break;
+ }
+
+ ram_mask(hwsq, timing[3], 0x00000000, 0x00000000); /*XXX*/
+ ram_mask(hwsq, timing[1], 0x00000000, 0x00000000); /*XXX*/
+ ram_mask(hwsq, timing[6], 0x00000000, 0x00000000); /*XXX*/
+ ram_mask(hwsq, timing[7], 0x00000000, 0x00000000); /*XXX*/
+ ram_mask(hwsq, timing[8], 0x00000000, 0x00000000); /*XXX*/
+ ram_mask(hwsq, timing[0], 0x00000000, 0x00000000); /*XXX*/
+ ram_mask(hwsq, timing[2], 0x00000000, 0x00000000); /*XXX*/
+ ram_mask(hwsq, timing[4], 0x00000000, 0x00000000); /*XXX*/
+ ram_mask(hwsq, timing[5], 0x00000000, 0x00000000); /*XXX*/
+
+ ram_mask(hwsq, timing[0], 0x00000000, 0x00000000); /*XXX*/
+
+#if QFX5800NVA0
+ ram_nuke(hwsq, 0x100e24);
+ ram_mask(hwsq, 0x100e24, 0x00000000, 0x00000000);
+ ram_nuke(hwsq, 0x100e20);
+ ram_mask(hwsq, 0x100e20, 0x00000000, 0x00000000);
+#endif
+
+ ram_mask(hwsq, mr[0], 0x100, 0x100);
+ ram_mask(hwsq, mr[0], 0x100, 0x000);
+
+ ram_setf(hwsq, 0x10, 0x01); /* enable fb */
+ ram_wait(hwsq, 0x00, 0x00); /* wait for fb enabled */
+ ram_wr32(hwsq, 0x611200, 0x00003330);
+ ram_wr32(hwsq, 0x002504, 0x00000000); /* un-block fifo */
+ return 0;
+}
+
+static int
+nv50_ram_prog(struct nvkm_fb *pfb)
+{
+ struct nvkm_device *device = nv_device(pfb);
+ struct nv50_ram *ram = (void *)pfb->ram;
+ struct nv50_ramseq *hwsq = &ram->hwsq;
+
+ ram_exec(hwsq, nvkm_boolopt(device->cfgopt, "NvMemExec", true));
+ return 0;
+}
+
+static void
+nv50_ram_tidy(struct nvkm_fb *pfb)
+{
+ struct nv50_ram *ram = (void *)pfb->ram;
+ struct nv50_ramseq *hwsq = &ram->hwsq;
+ ram_exec(hwsq, false);
+}
+
+void
+__nv50_ram_put(struct nvkm_fb *pfb, struct nvkm_mem *mem)
+{
+ struct nvkm_mm_node *this;
+
+ while (!list_empty(&mem->regions)) {
+ this = list_first_entry(&mem->regions, typeof(*this), rl_entry);
+
+ list_del(&this->rl_entry);
+ nvkm_mm_free(&pfb->vram, &this);
+ }
+
+ nvkm_mm_free(&pfb->tags, &mem->tag);
+}
+
+void
+nv50_ram_put(struct nvkm_fb *pfb, struct nvkm_mem **pmem)
+{
+ struct nvkm_mem *mem = *pmem;
+
+ *pmem = NULL;
+ if (unlikely(mem == NULL))
+ return;
+
+ mutex_lock(&pfb->base.mutex);
+ __nv50_ram_put(pfb, mem);
+ mutex_unlock(&pfb->base.mutex);
+
+ kfree(mem);
+}
+
+int
+nv50_ram_get(struct nvkm_fb *pfb, u64 size, u32 align, u32 ncmin,
+ u32 memtype, struct nvkm_mem **pmem)
+{
+ struct nvkm_mm *heap = &pfb->vram;
+ struct nvkm_mm *tags = &pfb->tags;
+ struct nvkm_mm_node *r;
+ struct nvkm_mem *mem;
+ int comp = (memtype & 0x300) >> 8;
+ int type = (memtype & 0x07f);
+ int back = (memtype & 0x800);
+ int min, max, ret;
+
+ max = (size >> 12);
+ min = ncmin ? (ncmin >> 12) : max;
+ align >>= 12;
+
+ mem = kzalloc(sizeof(*mem), GFP_KERNEL);
+ if (!mem)
+ return -ENOMEM;
+
+ mutex_lock(&pfb->base.mutex);
+ if (comp) {
+ if (align == 16) {
+ int n = (max >> 4) * comp;
+
+ ret = nvkm_mm_head(tags, 0, 1, n, n, 1, &mem->tag);
+ if (ret)
+ mem->tag = NULL;
+ }
+
+ if (unlikely(!mem->tag))
+ comp = 0;
+ }
+
+ INIT_LIST_HEAD(&mem->regions);
+ mem->memtype = (comp << 7) | type;
+ mem->size = max;
+
+ type = nv50_fb_memtype[type];
+ do {
+ if (back)
+ ret = nvkm_mm_tail(heap, 0, type, max, min, align, &r);
+ else
+ ret = nvkm_mm_head(heap, 0, type, max, min, align, &r);
+ if (ret) {
+ mutex_unlock(&pfb->base.mutex);
+ pfb->ram->put(pfb, &mem);
+ return ret;
+ }
+
+ list_add_tail(&r->rl_entry, &mem->regions);
+ max -= r->length;
+ } while (max);
+ mutex_unlock(&pfb->base.mutex);
+
+ r = list_first_entry(&mem->regions, struct nvkm_mm_node, rl_entry);
+ mem->offset = (u64)r->offset << 12;
+ *pmem = mem;
+ return 0;
+}
+
+static u32
+nv50_fb_vram_rblock(struct nvkm_fb *pfb, struct nvkm_ram *ram)
+{
+ int colbits, rowbitsa, rowbitsb, banks;
+ u64 rowsize, predicted;
+ u32 r0, r4, rt, rblock_size;
+
+ r0 = nv_rd32(pfb, 0x100200);
+ r4 = nv_rd32(pfb, 0x100204);
+ rt = nv_rd32(pfb, 0x100250);
+ nv_debug(pfb, "memcfg 0x%08x 0x%08x 0x%08x 0x%08x\n",
+ r0, r4, rt, nv_rd32(pfb, 0x001540));
+
+ colbits = (r4 & 0x0000f000) >> 12;
+ rowbitsa = ((r4 & 0x000f0000) >> 16) + 8;
+ rowbitsb = ((r4 & 0x00f00000) >> 20) + 8;
+ banks = 1 << (((r4 & 0x03000000) >> 24) + 2);
+
+ rowsize = ram->parts * banks * (1 << colbits) * 8;
+ predicted = rowsize << rowbitsa;
+ if (r0 & 0x00000004)
+ predicted += rowsize << rowbitsb;
+
+ if (predicted != ram->size) {
+ nv_warn(pfb, "memory controller reports %d MiB VRAM\n",
+ (u32)(ram->size >> 20));
+ }
+
+ rblock_size = rowsize;
+ if (rt & 1)
+ rblock_size *= 3;
+
+ nv_debug(pfb, "rblock %d bytes\n", rblock_size);
+ return rblock_size;
+}
+
+int
+nv50_ram_create_(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, int length, void **pobject)
+{
+ const u32 rsvd_head = ( 256 * 1024) >> 12; /* vga memory */
+ const u32 rsvd_tail = (1024 * 1024) >> 12; /* vbios etc */
+ struct nvkm_bios *bios = nvkm_bios(parent);
+ struct nvkm_fb *pfb = nvkm_fb(parent);
+ struct nvkm_ram *ram;
+ int ret;
+
+ ret = nvkm_ram_create_(parent, engine, oclass, length, pobject);
+ ram = *pobject;
+ if (ret)
+ return ret;
+
+ ram->size = nv_rd32(pfb, 0x10020c);
+ ram->size = (ram->size & 0xffffff00) | ((ram->size & 0x000000ff) << 32);
+
+ ram->part_mask = (nv_rd32(pfb, 0x001540) & 0x00ff0000) >> 16;
+ ram->parts = hweight8(ram->part_mask);
+
+ switch (nv_rd32(pfb, 0x100714) & 0x00000007) {
+ case 0: ram->type = NV_MEM_TYPE_DDR1; break;
+ case 1:
+ if (nvkm_fb_bios_memtype(bios) == NV_MEM_TYPE_DDR3)
+ ram->type = NV_MEM_TYPE_DDR3;
+ else
+ ram->type = NV_MEM_TYPE_DDR2;
+ break;
+ case 2: ram->type = NV_MEM_TYPE_GDDR3; break;
+ case 3: ram->type = NV_MEM_TYPE_GDDR4; break;
+ case 4: ram->type = NV_MEM_TYPE_GDDR5; break;
+ default:
+ break;
+ }
+
+ ret = nvkm_mm_init(&pfb->vram, rsvd_head, (ram->size >> 12) -
+ (rsvd_head + rsvd_tail),
+ nv50_fb_vram_rblock(pfb, ram) >> 12);
+ if (ret)
+ return ret;
+
+ ram->ranks = (nv_rd32(pfb, 0x100200) & 0x4) ? 2 : 1;
+ ram->tags = nv_rd32(pfb, 0x100320);
+ ram->get = nv50_ram_get;
+ ram->put = nv50_ram_put;
+ return 0;
+}
+
+static int
+nv50_ram_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 datasize,
+ struct nvkm_object **pobject)
+{
+ struct nv50_ram *ram;
+ int ret, i;
+
+ ret = nv50_ram_create(parent, engine, oclass, &ram);
+ *pobject = nv_object(ram);
+ if (ret)
+ return ret;
+
+ switch (ram->base.type) {
+ case NV_MEM_TYPE_DDR2:
+ case NV_MEM_TYPE_GDDR3:
+ ram->base.calc = nv50_ram_calc;
+ ram->base.prog = nv50_ram_prog;
+ ram->base.tidy = nv50_ram_tidy;
+ break;
+ default:
+ nv_warn(ram, "reclocking of this ram type unsupported\n");
+ return 0;
+ }
+
+ ram->hwsq.r_0x002504 = hwsq_reg(0x002504);
+ ram->hwsq.r_0x00c040 = hwsq_reg(0x00c040);
+ ram->hwsq.r_0x004008 = hwsq_reg(0x004008);
+ ram->hwsq.r_0x00400c = hwsq_reg(0x00400c);
+ ram->hwsq.r_0x100210 = hwsq_reg(0x100210);
+ ram->hwsq.r_0x1002d0 = hwsq_reg(0x1002d0);
+ ram->hwsq.r_0x1002d4 = hwsq_reg(0x1002d4);
+ ram->hwsq.r_0x1002dc = hwsq_reg(0x1002dc);
+ for (i = 0; i < 8; i++)
+ ram->hwsq.r_0x100da0[i] = hwsq_reg(0x100da0 + (i * 0x04));
+ ram->hwsq.r_0x100e20 = hwsq_reg(0x100e20);
+ ram->hwsq.r_0x100e24 = hwsq_reg(0x100e24);
+ ram->hwsq.r_0x611200 = hwsq_reg(0x611200);
+
+ for (i = 0; i < 9; i++)
+ ram->hwsq.r_timing[i] = hwsq_reg(0x100220 + (i * 0x04));
+
+ if (ram->base.ranks > 1) {
+ ram->hwsq.r_mr[0] = hwsq_reg2(0x1002c0, 0x1002c8);
+ ram->hwsq.r_mr[1] = hwsq_reg2(0x1002c4, 0x1002cc);
+ ram->hwsq.r_mr[2] = hwsq_reg2(0x1002e0, 0x1002e8);
+ ram->hwsq.r_mr[3] = hwsq_reg2(0x1002e4, 0x1002ec);
+ } else {
+ ram->hwsq.r_mr[0] = hwsq_reg(0x1002c0);
+ ram->hwsq.r_mr[1] = hwsq_reg(0x1002c4);
+ ram->hwsq.r_mr[2] = hwsq_reg(0x1002e0);
+ ram->hwsq.r_mr[3] = hwsq_reg(0x1002e4);
+ }
+
+ return 0;
+}
+
+struct nvkm_oclass
+nv50_ram_oclass = {
+ .ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv50_ram_ctor,
+ .dtor = _nvkm_ram_dtor,
+ .init = _nvkm_ram_init,
+ .fini = _nvkm_ram_fini,
+ }
+};
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramseq.h b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramseq.h
new file mode 100644
index 000000000000..0f1f97ccd5f6
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramseq.h
@@ -0,0 +1,15 @@
+#ifndef __NVKM_FBRAM_SEQ_H__
+#define __NVKM_FBRAM_SEQ_H__
+#include <subdev/bus/hwsq.h>
+
+#define ram_init(s,p) hwsq_init(&(s)->base, (p))
+#define ram_exec(s,e) hwsq_exec(&(s)->base, (e))
+#define ram_have(s,r) ((s)->r_##r.addr != 0x000000)
+#define ram_rd32(s,r) hwsq_rd32(&(s)->base, &(s)->r_##r)
+#define ram_wr32(s,r,d) hwsq_wr32(&(s)->base, &(s)->r_##r, (d))
+#define ram_nuke(s,r) hwsq_nuke(&(s)->base, &(s)->r_##r)
+#define ram_mask(s,r,m,d) hwsq_mask(&(s)->base, &(s)->r_##r, (m), (d))
+#define ram_setf(s,f,d) hwsq_setf(&(s)->base, (f), (d))
+#define ram_wait(s,f,d) hwsq_wait(&(s)->base, (f), (d))
+#define ram_nsec(s,n) hwsq_nsec(&(s)->base, (n))
+#endif
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/regsnv04.h b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/regsnv04.h
new file mode 100644
index 000000000000..1f865f61504e
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/regsnv04.h
@@ -0,0 +1,22 @@
+#ifndef __NVKM_FB_REGS_04_H__
+#define __NVKM_FB_REGS_04_H__
+
+#define NV04_PFB_BOOT_0 0x00100000
+# define NV04_PFB_BOOT_0_RAM_AMOUNT 0x00000003
+# define NV04_PFB_BOOT_0_RAM_AMOUNT_32MB 0x00000000
+# define NV04_PFB_BOOT_0_RAM_AMOUNT_4MB 0x00000001
+# define NV04_PFB_BOOT_0_RAM_AMOUNT_8MB 0x00000002
+# define NV04_PFB_BOOT_0_RAM_AMOUNT_16MB 0x00000003
+# define NV04_PFB_BOOT_0_RAM_WIDTH_128 0x00000004
+# define NV04_PFB_BOOT_0_RAM_TYPE 0x00000028
+# define NV04_PFB_BOOT_0_RAM_TYPE_SGRAM_8MBIT 0x00000000
+# define NV04_PFB_BOOT_0_RAM_TYPE_SGRAM_16MBIT 0x00000008
+# define NV04_PFB_BOOT_0_RAM_TYPE_SGRAM_16MBIT_4BANK 0x00000010
+# define NV04_PFB_BOOT_0_RAM_TYPE_SDRAM_16MBIT 0x00000018
+# define NV04_PFB_BOOT_0_RAM_TYPE_SDRAM_64MBIT 0x00000020
+# define NV04_PFB_BOOT_0_RAM_TYPE_SDRAM_64MBITX16 0x00000028
+# define NV04_PFB_BOOT_0_UMA_ENABLE 0x00000100
+# define NV04_PFB_BOOT_0_UMA_SIZE 0x0000f000
+#define NV04_PFB_CFG0 0x00100200
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/sddr2.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/sddr2.c
new file mode 100644
index 000000000000..afab42df28d4
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/sddr2.c
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2014 Roy Spliet
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Roy Spliet <rspliet@eclipso.eu>
+ * Ben Skeggs
+ */
+#include "priv.h"
+
+struct ramxlat {
+ int id;
+ u8 enc;
+};
+
+static inline int
+ramxlat(const struct ramxlat *xlat, int id)
+{
+ while (xlat->id >= 0) {
+ if (xlat->id == id)
+ return xlat->enc;
+ xlat++;
+ }
+ return -EINVAL;
+}
+
+static const struct ramxlat
+ramddr2_cl[] = {
+ { 2, 2 }, { 3, 3 }, { 4, 4 }, { 5, 5 }, { 6, 6 },
+ /* The following are available in some, but not all DDR2 docs */
+ { 7, 7 },
+ { -1 }
+};
+
+static const struct ramxlat
+ramddr2_wr[] = {
+ { 2, 1 }, { 3, 2 }, { 4, 3 }, { 5, 4 }, { 6, 5 },
+ /* The following are available in some, but not all DDR2 docs */
+ { 7, 6 },
+ { -1 }
+};
+
+int
+nvkm_sddr2_calc(struct nvkm_ram *ram)
+{
+ int CL, WR, DLL = 0, ODT = 0;
+
+ switch (ram->next->bios.timing_ver) {
+ case 0x10:
+ CL = ram->next->bios.timing_10_CL;
+ WR = ram->next->bios.timing_10_WR;
+ DLL = !ram->next->bios.ramcfg_10_DLLoff;
+ ODT = ram->next->bios.timing_10_ODT & 3;
+ break;
+ case 0x20:
+ CL = (ram->next->bios.timing[1] & 0x0000001f);
+ WR = (ram->next->bios.timing[2] & 0x007f0000) >> 16;
+ break;
+ default:
+ return -ENOSYS;
+ }
+
+ CL = ramxlat(ramddr2_cl, CL);
+ WR = ramxlat(ramddr2_wr, WR);
+ if (CL < 0 || WR < 0)
+ return -EINVAL;
+
+ ram->mr[0] &= ~0xf70;
+ ram->mr[0] |= (WR & 0x07) << 9;
+ ram->mr[0] |= (CL & 0x07) << 4;
+
+ ram->mr[1] &= ~0x045;
+ ram->mr[1] |= (ODT & 0x1) << 2;
+ ram->mr[1] |= (ODT & 0x2) << 5;
+ ram->mr[1] |= !DLL;
+ return 0;
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/sddr3.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/sddr3.c
new file mode 100644
index 000000000000..10844355c3f3
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/sddr3.c
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs <bskeggs@redhat.com>
+ * Roy Spliet <rspliet@eclipso.eu>
+ */
+#include "priv.h"
+
+struct ramxlat {
+ int id;
+ u8 enc;
+};
+
+static inline int
+ramxlat(const struct ramxlat *xlat, int id)
+{
+ while (xlat->id >= 0) {
+ if (xlat->id == id)
+ return xlat->enc;
+ xlat++;
+ }
+ return -EINVAL;
+}
+
+static const struct ramxlat
+ramddr3_cl[] = {
+ { 5, 2 }, { 6, 4 }, { 7, 6 }, { 8, 8 }, { 9, 10 }, { 10, 12 },
+ { 11, 14 },
+ /* the below are mentioned in some, but not all, ddr3 docs */
+ { 12, 1 }, { 13, 3 }, { 14, 5 },
+ { -1 }
+};
+
+static const struct ramxlat
+ramddr3_wr[] = {
+ { 5, 1 }, { 6, 2 }, { 7, 3 }, { 8, 4 }, { 10, 5 }, { 12, 6 },
+ /* the below are mentioned in some, but not all, ddr3 docs */
+ { 14, 7 }, { 16, 0 },
+ { -1 }
+};
+
+static const struct ramxlat
+ramddr3_cwl[] = {
+ { 5, 0 }, { 6, 1 }, { 7, 2 }, { 8, 3 },
+ /* the below are mentioned in some, but not all, ddr3 docs */
+ { 9, 4 },
+ { -1 }
+};
+
+int
+nvkm_sddr3_calc(struct nvkm_ram *ram)
+{
+ int CWL, CL, WR, DLL = 0, ODT = 0;
+
+ switch (ram->next->bios.timing_ver) {
+ case 0x10:
+ if (ram->next->bios.timing_hdr < 0x17) {
+ /* XXX: NV50: Get CWL from the timing register */
+ return -ENOSYS;
+ }
+ CWL = ram->next->bios.timing_10_CWL;
+ CL = ram->next->bios.timing_10_CL;
+ WR = ram->next->bios.timing_10_WR;
+ DLL = !ram->next->bios.ramcfg_10_DLLoff;
+ ODT = ram->next->bios.timing_10_ODT;
+ break;
+ case 0x20:
+ CWL = (ram->next->bios.timing[1] & 0x00000f80) >> 7;
+ CL = (ram->next->bios.timing[1] & 0x0000001f) >> 0;
+ WR = (ram->next->bios.timing[2] & 0x007f0000) >> 16;
+ /* XXX: Get these values from the VBIOS instead */
+ DLL = !(ram->mr[1] & 0x1);
+ ODT = (ram->mr[1] & 0x004) >> 2 |
+ (ram->mr[1] & 0x040) >> 5 |
+ (ram->mr[1] & 0x200) >> 7;
+ break;
+ default:
+ return -ENOSYS;
+ }
+
+ CWL = ramxlat(ramddr3_cwl, CWL);
+ CL = ramxlat(ramddr3_cl, CL);
+ WR = ramxlat(ramddr3_wr, WR);
+ if (CL < 0 || CWL < 0 || WR < 0)
+ return -EINVAL;
+
+ ram->mr[0] &= ~0xf74;
+ ram->mr[0] |= (WR & 0x07) << 9;
+ ram->mr[0] |= (CL & 0x0e) << 3;
+ ram->mr[0] |= (CL & 0x01) << 2;
+
+ ram->mr[1] &= ~0x245;
+ ram->mr[1] |= (ODT & 0x1) << 2;
+ ram->mr[1] |= (ODT & 0x2) << 5;
+ ram->mr[1] |= (ODT & 0x4) << 7;
+ ram->mr[1] |= !DLL;
+
+ ram->mr[2] &= ~0x038;
+ ram->mr[2] |= (CWL & 0x07) << 3;
+ return 0;
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fuse/Kbuild b/drivers/gpu/drm/nouveau/nvkm/subdev/fuse/Kbuild
new file mode 100644
index 000000000000..f3d4e6e131b6
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fuse/Kbuild
@@ -0,0 +1,4 @@
+nvkm-y += nvkm/subdev/fuse/base.o
+nvkm-y += nvkm/subdev/fuse/nv50.o
+nvkm-y += nvkm/subdev/fuse/gf100.o
+nvkm-y += nvkm/subdev/fuse/gm107.o
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fuse/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fuse/base.c
new file mode 100644
index 000000000000..b7b7193bbce7
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fuse/base.c
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2014 Martin Peres
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Martin Peres
+ */
+#include <subdev/fuse.h>
+
+int
+_nvkm_fuse_init(struct nvkm_object *object)
+{
+ struct nvkm_fuse *fuse = (void *)object;
+ return nvkm_subdev_init(&fuse->base);
+}
+
+void
+_nvkm_fuse_dtor(struct nvkm_object *object)
+{
+ struct nvkm_fuse *fuse = (void *)object;
+ nvkm_subdev_destroy(&fuse->base);
+}
+
+int
+nvkm_fuse_create_(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, int length, void **pobject)
+{
+ struct nvkm_fuse *fuse;
+ int ret;
+
+ ret = nvkm_subdev_create_(parent, engine, oclass, 0, "FUSE",
+ "fuse", length, pobject);
+ fuse = *pobject;
+ return ret;
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fuse/gf100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fuse/gf100.c
new file mode 100644
index 000000000000..393ef3a0faaf
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fuse/gf100.c
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2014 Martin Peres
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Martin Peres
+ */
+#include "priv.h"
+
+struct gf100_fuse_priv {
+ struct nvkm_fuse base;
+
+ spinlock_t fuse_enable_lock;
+};
+
+static u32
+gf100_fuse_rd32(struct nvkm_object *object, u64 addr)
+{
+ struct gf100_fuse_priv *priv = (void *)object;
+ unsigned long flags;
+ u32 fuse_enable, unk, val;
+
+ /* racy if another part of nvkm start writing to these regs */
+ spin_lock_irqsave(&priv->fuse_enable_lock, flags);
+ fuse_enable = nv_mask(priv, 0x22400, 0x800, 0x800);
+ unk = nv_mask(priv, 0x21000, 0x1, 0x1);
+ val = nv_rd32(priv, 0x21100 + addr);
+ nv_wr32(priv, 0x21000, unk);
+ nv_wr32(priv, 0x22400, fuse_enable);
+ spin_unlock_irqrestore(&priv->fuse_enable_lock, flags);
+ return val;
+}
+
+
+static int
+gf100_fuse_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 size,
+ struct nvkm_object **pobject)
+{
+ struct gf100_fuse_priv *priv;
+ int ret;
+
+ ret = nvkm_fuse_create(parent, engine, oclass, &priv);
+ *pobject = nv_object(priv);
+ if (ret)
+ return ret;
+
+ spin_lock_init(&priv->fuse_enable_lock);
+ return 0;
+}
+
+struct nvkm_oclass
+gf100_fuse_oclass = {
+ .handle = NV_SUBDEV(FUSE, 0xC0),
+ .ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = gf100_fuse_ctor,
+ .dtor = _nvkm_fuse_dtor,
+ .init = _nvkm_fuse_init,
+ .fini = _nvkm_fuse_fini,
+ .rd32 = gf100_fuse_rd32,
+ },
+};
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fuse/gm107.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fuse/gm107.c
new file mode 100644
index 000000000000..ba19158a5912
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fuse/gm107.c
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2014 Martin Peres
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Martin Peres
+ */
+#include "priv.h"
+
+struct gm107_fuse_priv {
+ struct nvkm_fuse base;
+};
+
+static u32
+gm107_fuse_rd32(struct nvkm_object *object, u64 addr)
+{
+ struct gf100_fuse_priv *priv = (void *)object;
+ return nv_rd32(priv, 0x21100 + addr);
+}
+
+
+static int
+gm107_fuse_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 size,
+ struct nvkm_object **pobject)
+{
+ struct gm107_fuse_priv *priv;
+ int ret;
+
+ ret = nvkm_fuse_create(parent, engine, oclass, &priv);
+ *pobject = nv_object(priv);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+struct nvkm_oclass
+gm107_fuse_oclass = {
+ .handle = NV_SUBDEV(FUSE, 0x117),
+ .ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = gm107_fuse_ctor,
+ .dtor = _nvkm_fuse_dtor,
+ .init = _nvkm_fuse_init,
+ .fini = _nvkm_fuse_fini,
+ .rd32 = gm107_fuse_rd32,
+ },
+};
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fuse/nv50.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fuse/nv50.c
new file mode 100644
index 000000000000..0d2afc426100
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fuse/nv50.c
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2014 Martin Peres
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Martin Peres
+ */
+#include "priv.h"
+
+struct nv50_fuse_priv {
+ struct nvkm_fuse base;
+
+ spinlock_t fuse_enable_lock;
+};
+
+static u32
+nv50_fuse_rd32(struct nvkm_object *object, u64 addr)
+{
+ struct nv50_fuse_priv *priv = (void *)object;
+ unsigned long flags;
+ u32 fuse_enable, val;
+
+ /* racy if another part of nvkm start writing to this reg */
+ spin_lock_irqsave(&priv->fuse_enable_lock, flags);
+ fuse_enable = nv_mask(priv, 0x1084, 0x800, 0x800);
+ val = nv_rd32(priv, 0x21000 + addr);
+ nv_wr32(priv, 0x1084, fuse_enable);
+ spin_unlock_irqrestore(&priv->fuse_enable_lock, flags);
+ return val;
+}
+
+
+static int
+nv50_fuse_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 size,
+ struct nvkm_object **pobject)
+{
+ struct nv50_fuse_priv *priv;
+ int ret;
+
+ ret = nvkm_fuse_create(parent, engine, oclass, &priv);
+ *pobject = nv_object(priv);
+ if (ret)
+ return ret;
+
+ spin_lock_init(&priv->fuse_enable_lock);
+ return 0;
+}
+
+struct nvkm_oclass
+nv50_fuse_oclass = {
+ .handle = NV_SUBDEV(FUSE, 0x50),
+ .ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv50_fuse_ctor,
+ .dtor = _nvkm_fuse_dtor,
+ .init = _nvkm_fuse_init,
+ .fini = _nvkm_fuse_fini,
+ .rd32 = nv50_fuse_rd32,
+ },
+};
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fuse/priv.h b/drivers/gpu/drm/nouveau/nvkm/subdev/fuse/priv.h
new file mode 100644
index 000000000000..7e050f789384
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fuse/priv.h
@@ -0,0 +1,7 @@
+#ifndef __NVKM_FUSE_PRIV_H__
+#define __NVKM_FUSE_PRIV_H__
+#include <subdev/fuse.h>
+
+int _nvkm_fuse_init(struct nvkm_object *object);
+void _nvkm_fuse_dtor(struct nvkm_object *object);
+#endif
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/Kbuild b/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/Kbuild
new file mode 100644
index 000000000000..ea42a9ed1821
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/Kbuild
@@ -0,0 +1,6 @@
+nvkm-y += nvkm/subdev/gpio/base.o
+nvkm-y += nvkm/subdev/gpio/nv10.o
+nvkm-y += nvkm/subdev/gpio/nv50.o
+nvkm-y += nvkm/subdev/gpio/g94.o
+nvkm-y += nvkm/subdev/gpio/gf110.o
+nvkm-y += nvkm/subdev/gpio/gk104.o
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/base.c
new file mode 100644
index 000000000000..dea58161ba46
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/base.c
@@ -0,0 +1,251 @@
+/*
+ * Copyright 2011 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "priv.h"
+
+#include <core/device.h>
+#include <core/notify.h>
+
+static int
+nvkm_gpio_drive(struct nvkm_gpio *gpio, int idx, int line, int dir, int out)
+{
+ const struct nvkm_gpio_impl *impl = (void *)nv_object(gpio)->oclass;
+ return impl->drive ? impl->drive(gpio, line, dir, out) : -ENODEV;
+}
+
+static int
+nvkm_gpio_sense(struct nvkm_gpio *gpio, int idx, int line)
+{
+ const struct nvkm_gpio_impl *impl = (void *)nv_object(gpio)->oclass;
+ return impl->sense ? impl->sense(gpio, line) : -ENODEV;
+}
+
+static int
+nvkm_gpio_find(struct nvkm_gpio *gpio, int idx, u8 tag, u8 line,
+ struct dcb_gpio_func *func)
+{
+ struct nvkm_bios *bios = nvkm_bios(gpio);
+ u8 ver, len;
+ u16 data;
+
+ if (line == 0xff && tag == 0xff)
+ return -EINVAL;
+
+ data = dcb_gpio_match(bios, idx, tag, line, &ver, &len, func);
+ if (data)
+ return 0;
+
+ /* Apple iMac G4 NV18 */
+ if (nv_device_match(nv_object(gpio), 0x0189, 0x10de, 0x0010)) {
+ if (tag == DCB_GPIO_TVDAC0) {
+ *func = (struct dcb_gpio_func) {
+ .func = DCB_GPIO_TVDAC0,
+ .line = 4,
+ .log[0] = 0,
+ .log[1] = 1,
+ };
+ return 0;
+ }
+ }
+
+ return -ENOENT;
+}
+
+static int
+nvkm_gpio_set(struct nvkm_gpio *gpio, int idx, u8 tag, u8 line, int state)
+{
+ struct dcb_gpio_func func;
+ int ret;
+
+ ret = nvkm_gpio_find(gpio, idx, tag, line, &func);
+ if (ret == 0) {
+ int dir = !!(func.log[state] & 0x02);
+ int out = !!(func.log[state] & 0x01);
+ ret = nvkm_gpio_drive(gpio, idx, func.line, dir, out);
+ }
+
+ return ret;
+}
+
+static int
+nvkm_gpio_get(struct nvkm_gpio *gpio, int idx, u8 tag, u8 line)
+{
+ struct dcb_gpio_func func;
+ int ret;
+
+ ret = nvkm_gpio_find(gpio, idx, tag, line, &func);
+ if (ret == 0) {
+ ret = nvkm_gpio_sense(gpio, idx, func.line);
+ if (ret >= 0)
+ ret = (ret == (func.log[1] & 1));
+ }
+
+ return ret;
+}
+
+static void
+nvkm_gpio_intr_fini(struct nvkm_event *event, int type, int index)
+{
+ struct nvkm_gpio *gpio = container_of(event, typeof(*gpio), event);
+ const struct nvkm_gpio_impl *impl = (void *)nv_object(gpio)->oclass;
+ impl->intr_mask(gpio, type, 1 << index, 0);
+}
+
+static void
+nvkm_gpio_intr_init(struct nvkm_event *event, int type, int index)
+{
+ struct nvkm_gpio *gpio = container_of(event, typeof(*gpio), event);
+ const struct nvkm_gpio_impl *impl = (void *)nv_object(gpio)->oclass;
+ impl->intr_mask(gpio, type, 1 << index, 1 << index);
+}
+
+static int
+nvkm_gpio_intr_ctor(struct nvkm_object *object, void *data, u32 size,
+ struct nvkm_notify *notify)
+{
+ struct nvkm_gpio_ntfy_req *req = data;
+ if (!WARN_ON(size != sizeof(*req))) {
+ notify->size = sizeof(struct nvkm_gpio_ntfy_rep);
+ notify->types = req->mask;
+ notify->index = req->line;
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static void
+nvkm_gpio_intr(struct nvkm_subdev *subdev)
+{
+ struct nvkm_gpio *gpio = nvkm_gpio(subdev);
+ const struct nvkm_gpio_impl *impl = (void *)nv_object(gpio)->oclass;
+ u32 hi, lo, i;
+
+ impl->intr_stat(gpio, &hi, &lo);
+
+ for (i = 0; (hi | lo) && i < impl->lines; i++) {
+ struct nvkm_gpio_ntfy_rep rep = {
+ .mask = (NVKM_GPIO_HI * !!(hi & (1 << i))) |
+ (NVKM_GPIO_LO * !!(lo & (1 << i))),
+ };
+ nvkm_event_send(&gpio->event, rep.mask, i, &rep, sizeof(rep));
+ }
+}
+
+static const struct nvkm_event_func
+nvkm_gpio_intr_func = {
+ .ctor = nvkm_gpio_intr_ctor,
+ .init = nvkm_gpio_intr_init,
+ .fini = nvkm_gpio_intr_fini,
+};
+
+int
+_nvkm_gpio_fini(struct nvkm_object *object, bool suspend)
+{
+ const struct nvkm_gpio_impl *impl = (void *)object->oclass;
+ struct nvkm_gpio *gpio = nvkm_gpio(object);
+ u32 mask = (1 << impl->lines) - 1;
+
+ impl->intr_mask(gpio, NVKM_GPIO_TOGGLED, mask, 0);
+ impl->intr_stat(gpio, &mask, &mask);
+
+ return nvkm_subdev_fini(&gpio->base, suspend);
+}
+
+static struct dmi_system_id gpio_reset_ids[] = {
+ {
+ .ident = "Apple Macbook 10,1",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro10,1"),
+ }
+ },
+ { }
+};
+
+int
+_nvkm_gpio_init(struct nvkm_object *object)
+{
+ struct nvkm_gpio *gpio = nvkm_gpio(object);
+ int ret;
+
+ ret = nvkm_subdev_init(&gpio->base);
+ if (ret)
+ return ret;
+
+ if (gpio->reset && dmi_check_system(gpio_reset_ids))
+ gpio->reset(gpio, DCB_GPIO_UNUSED);
+
+ return ret;
+}
+
+void
+_nvkm_gpio_dtor(struct nvkm_object *object)
+{
+ struct nvkm_gpio *gpio = (void *)object;
+ nvkm_event_fini(&gpio->event);
+ nvkm_subdev_destroy(&gpio->base);
+}
+
+int
+nvkm_gpio_create_(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, int length, void **pobject)
+{
+ const struct nvkm_gpio_impl *impl = (void *)oclass;
+ struct nvkm_gpio *gpio;
+ int ret;
+
+ ret = nvkm_subdev_create_(parent, engine, oclass, 0, "GPIO",
+ "gpio", length, pobject);
+ gpio = *pobject;
+ if (ret)
+ return ret;
+
+ gpio->find = nvkm_gpio_find;
+ gpio->set = nvkm_gpio_set;
+ gpio->get = nvkm_gpio_get;
+ gpio->reset = impl->reset;
+
+ ret = nvkm_event_init(&nvkm_gpio_intr_func, 2, impl->lines,
+ &gpio->event);
+ if (ret)
+ return ret;
+
+ nv_subdev(gpio)->intr = nvkm_gpio_intr;
+ return 0;
+}
+
+int
+_nvkm_gpio_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 size,
+ struct nvkm_object **pobject)
+{
+ struct nvkm_gpio *gpio;
+ int ret;
+
+ ret = nvkm_gpio_create(parent, engine, oclass, &gpio);
+ *pobject = nv_object(gpio);
+ if (ret)
+ return ret;
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/g94.c b/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/g94.c
new file mode 100644
index 000000000000..12b3e01fca8e
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/g94.c
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "priv.h"
+
+void
+g94_gpio_intr_stat(struct nvkm_gpio *gpio, u32 *hi, u32 *lo)
+{
+ u32 intr0 = nv_rd32(gpio, 0x00e054);
+ u32 intr1 = nv_rd32(gpio, 0x00e074);
+ u32 stat0 = nv_rd32(gpio, 0x00e050) & intr0;
+ u32 stat1 = nv_rd32(gpio, 0x00e070) & intr1;
+ *lo = (stat1 & 0xffff0000) | (stat0 >> 16);
+ *hi = (stat1 << 16) | (stat0 & 0x0000ffff);
+ nv_wr32(gpio, 0x00e054, intr0);
+ nv_wr32(gpio, 0x00e074, intr1);
+}
+
+void
+g94_gpio_intr_mask(struct nvkm_gpio *gpio, u32 type, u32 mask, u32 data)
+{
+ u32 inte0 = nv_rd32(gpio, 0x00e050);
+ u32 inte1 = nv_rd32(gpio, 0x00e070);
+ if (type & NVKM_GPIO_LO)
+ inte0 = (inte0 & ~(mask << 16)) | (data << 16);
+ if (type & NVKM_GPIO_HI)
+ inte0 = (inte0 & ~(mask & 0xffff)) | (data & 0xffff);
+ mask >>= 16;
+ data >>= 16;
+ if (type & NVKM_GPIO_LO)
+ inte1 = (inte1 & ~(mask << 16)) | (data << 16);
+ if (type & NVKM_GPIO_HI)
+ inte1 = (inte1 & ~mask) | data;
+ nv_wr32(gpio, 0x00e050, inte0);
+ nv_wr32(gpio, 0x00e070, inte1);
+}
+
+struct nvkm_oclass *
+g94_gpio_oclass = &(struct nvkm_gpio_impl) {
+ .base.handle = NV_SUBDEV(GPIO, 0x94),
+ .base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = _nvkm_gpio_ctor,
+ .dtor = _nvkm_gpio_dtor,
+ .init = _nvkm_gpio_init,
+ .fini = _nvkm_gpio_fini,
+ },
+ .lines = 32,
+ .intr_stat = g94_gpio_intr_stat,
+ .intr_mask = g94_gpio_intr_mask,
+ .drive = nv50_gpio_drive,
+ .sense = nv50_gpio_sense,
+ .reset = nv50_gpio_reset,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/gf110.c b/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/gf110.c
new file mode 100644
index 000000000000..2c3bb255d1f8
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/gf110.c
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "priv.h"
+
+void
+gf110_gpio_reset(struct nvkm_gpio *gpio, u8 match)
+{
+ struct nvkm_bios *bios = nvkm_bios(gpio);
+ u8 ver, len;
+ u16 entry;
+ int ent = -1;
+
+ while ((entry = dcb_gpio_entry(bios, 0, ++ent, &ver, &len))) {
+ u32 data = nv_ro32(bios, entry);
+ u8 line = (data & 0x0000003f);
+ u8 defs = !!(data & 0x00000080);
+ u8 func = (data & 0x0000ff00) >> 8;
+ u8 unk0 = (data & 0x00ff0000) >> 16;
+ u8 unk1 = (data & 0x1f000000) >> 24;
+
+ if ( func == DCB_GPIO_UNUSED ||
+ (match != DCB_GPIO_UNUSED && match != func))
+ continue;
+
+ gpio->set(gpio, 0, func, line, defs);
+
+ nv_mask(gpio, 0x00d610 + (line * 4), 0xff, unk0);
+ if (unk1--)
+ nv_mask(gpio, 0x00d740 + (unk1 * 4), 0xff, line);
+ }
+}
+
+int
+gf110_gpio_drive(struct nvkm_gpio *gpio, int line, int dir, int out)
+{
+ u32 data = ((dir ^ 1) << 13) | (out << 12);
+ nv_mask(gpio, 0x00d610 + (line * 4), 0x00003000, data);
+ nv_mask(gpio, 0x00d604, 0x00000001, 0x00000001); /* update? */
+ return 0;
+}
+
+int
+gf110_gpio_sense(struct nvkm_gpio *gpio, int line)
+{
+ return !!(nv_rd32(gpio, 0x00d610 + (line * 4)) & 0x00004000);
+}
+
+struct nvkm_oclass *
+gf110_gpio_oclass = &(struct nvkm_gpio_impl) {
+ .base.handle = NV_SUBDEV(GPIO, 0xd0),
+ .base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = _nvkm_gpio_ctor,
+ .dtor = _nvkm_gpio_dtor,
+ .init = _nvkm_gpio_init,
+ .fini = _nvkm_gpio_fini,
+ },
+ .lines = 32,
+ .intr_stat = g94_gpio_intr_stat,
+ .intr_mask = g94_gpio_intr_mask,
+ .drive = gf110_gpio_drive,
+ .sense = gf110_gpio_sense,
+ .reset = gf110_gpio_reset,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/gk104.c b/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/gk104.c
new file mode 100644
index 000000000000..42fd2faaaa4f
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/gk104.c
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "priv.h"
+
+static void
+gk104_gpio_intr_stat(struct nvkm_gpio *gpio, u32 *hi, u32 *lo)
+{
+ u32 intr0 = nv_rd32(gpio, 0x00dc00);
+ u32 intr1 = nv_rd32(gpio, 0x00dc80);
+ u32 stat0 = nv_rd32(gpio, 0x00dc08) & intr0;
+ u32 stat1 = nv_rd32(gpio, 0x00dc88) & intr1;
+ *lo = (stat1 & 0xffff0000) | (stat0 >> 16);
+ *hi = (stat1 << 16) | (stat0 & 0x0000ffff);
+ nv_wr32(gpio, 0x00dc00, intr0);
+ nv_wr32(gpio, 0x00dc80, intr1);
+}
+
+void
+gk104_gpio_intr_mask(struct nvkm_gpio *gpio, u32 type, u32 mask, u32 data)
+{
+ u32 inte0 = nv_rd32(gpio, 0x00dc08);
+ u32 inte1 = nv_rd32(gpio, 0x00dc88);
+ if (type & NVKM_GPIO_LO)
+ inte0 = (inte0 & ~(mask << 16)) | (data << 16);
+ if (type & NVKM_GPIO_HI)
+ inte0 = (inte0 & ~(mask & 0xffff)) | (data & 0xffff);
+ mask >>= 16;
+ data >>= 16;
+ if (type & NVKM_GPIO_LO)
+ inte1 = (inte1 & ~(mask << 16)) | (data << 16);
+ if (type & NVKM_GPIO_HI)
+ inte1 = (inte1 & ~mask) | data;
+ nv_wr32(gpio, 0x00dc08, inte0);
+ nv_wr32(gpio, 0x00dc88, inte1);
+}
+
+struct nvkm_oclass *
+gk104_gpio_oclass = &(struct nvkm_gpio_impl) {
+ .base.handle = NV_SUBDEV(GPIO, 0xe0),
+ .base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = _nvkm_gpio_ctor,
+ .dtor = _nvkm_gpio_dtor,
+ .init = _nvkm_gpio_init,
+ .fini = _nvkm_gpio_fini,
+ },
+ .lines = 32,
+ .intr_stat = gk104_gpio_intr_stat,
+ .intr_mask = gk104_gpio_intr_mask,
+ .drive = gf110_gpio_drive,
+ .sense = gf110_gpio_sense,
+ .reset = gf110_gpio_reset,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/nv10.c b/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/nv10.c
new file mode 100644
index 000000000000..2b295154247e
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/nv10.c
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2009 Francisco Jerez.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#include "priv.h"
+
+static int
+nv10_gpio_sense(struct nvkm_gpio *gpio, int line)
+{
+ if (line < 2) {
+ line = line * 16;
+ line = nv_rd32(gpio, 0x600818) >> line;
+ return !!(line & 0x0100);
+ } else
+ if (line < 10) {
+ line = (line - 2) * 4;
+ line = nv_rd32(gpio, 0x60081c) >> line;
+ return !!(line & 0x04);
+ } else
+ if (line < 14) {
+ line = (line - 10) * 4;
+ line = nv_rd32(gpio, 0x600850) >> line;
+ return !!(line & 0x04);
+ }
+
+ return -EINVAL;
+}
+
+static int
+nv10_gpio_drive(struct nvkm_gpio *gpio, int line, int dir, int out)
+{
+ u32 reg, mask, data;
+
+ if (line < 2) {
+ line = line * 16;
+ reg = 0x600818;
+ mask = 0x00000011;
+ data = (dir << 4) | out;
+ } else
+ if (line < 10) {
+ line = (line - 2) * 4;
+ reg = 0x60081c;
+ mask = 0x00000003;
+ data = (dir << 1) | out;
+ } else
+ if (line < 14) {
+ line = (line - 10) * 4;
+ reg = 0x600850;
+ mask = 0x00000003;
+ data = (dir << 1) | out;
+ } else {
+ return -EINVAL;
+ }
+
+ nv_mask(gpio, reg, mask << line, data << line);
+ return 0;
+}
+
+static void
+nv10_gpio_intr_stat(struct nvkm_gpio *gpio, u32 *hi, u32 *lo)
+{
+ u32 intr = nv_rd32(gpio, 0x001104);
+ u32 stat = nv_rd32(gpio, 0x001144) & intr;
+ *lo = (stat & 0xffff0000) >> 16;
+ *hi = (stat & 0x0000ffff);
+ nv_wr32(gpio, 0x001104, intr);
+}
+
+static void
+nv10_gpio_intr_mask(struct nvkm_gpio *gpio, u32 type, u32 mask, u32 data)
+{
+ u32 inte = nv_rd32(gpio, 0x001144);
+ if (type & NVKM_GPIO_LO)
+ inte = (inte & ~(mask << 16)) | (data << 16);
+ if (type & NVKM_GPIO_HI)
+ inte = (inte & ~mask) | data;
+ nv_wr32(gpio, 0x001144, inte);
+}
+
+struct nvkm_oclass *
+nv10_gpio_oclass = &(struct nvkm_gpio_impl) {
+ .base.handle = NV_SUBDEV(GPIO, 0x10),
+ .base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = _nvkm_gpio_ctor,
+ .dtor = _nvkm_gpio_dtor,
+ .init = _nvkm_gpio_init,
+ .fini = _nvkm_gpio_fini,
+ },
+ .lines = 16,
+ .intr_stat = nv10_gpio_intr_stat,
+ .intr_mask = nv10_gpio_intr_mask,
+ .drive = nv10_gpio_drive,
+ .sense = nv10_gpio_sense,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/nv50.c b/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/nv50.c
new file mode 100644
index 000000000000..6a031035bd27
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/nv50.c
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "priv.h"
+
+void
+nv50_gpio_reset(struct nvkm_gpio *gpio, u8 match)
+{
+ struct nvkm_bios *bios = nvkm_bios(gpio);
+ u8 ver, len;
+ u16 entry;
+ int ent = -1;
+
+ while ((entry = dcb_gpio_entry(bios, 0, ++ent, &ver, &len))) {
+ static const u32 regs[] = { 0xe100, 0xe28c };
+ u32 data = nv_ro32(bios, entry);
+ u8 line = (data & 0x0000001f);
+ u8 func = (data & 0x0000ff00) >> 8;
+ u8 defs = !!(data & 0x01000000);
+ u8 unk0 = !!(data & 0x02000000);
+ u8 unk1 = !!(data & 0x04000000);
+ u32 val = (unk1 << 16) | unk0;
+ u32 reg = regs[line >> 4];
+ u32 lsh = line & 0x0f;
+
+ if ( func == DCB_GPIO_UNUSED ||
+ (match != DCB_GPIO_UNUSED && match != func))
+ continue;
+
+ gpio->set(gpio, 0, func, line, defs);
+
+ nv_mask(gpio, reg, 0x00010001 << lsh, val << lsh);
+ }
+}
+
+int
+nv50_gpio_location(int line, u32 *reg, u32 *shift)
+{
+ const u32 nv50_gpio_reg[4] = { 0xe104, 0xe108, 0xe280, 0xe284 };
+
+ if (line >= 32)
+ return -EINVAL;
+
+ *reg = nv50_gpio_reg[line >> 3];
+ *shift = (line & 7) << 2;
+ return 0;
+}
+
+int
+nv50_gpio_drive(struct nvkm_gpio *gpio, int line, int dir, int out)
+{
+ u32 reg, shift;
+
+ if (nv50_gpio_location(line, &reg, &shift))
+ return -EINVAL;
+
+ nv_mask(gpio, reg, 3 << shift, (((dir ^ 1) << 1) | out) << shift);
+ return 0;
+}
+
+int
+nv50_gpio_sense(struct nvkm_gpio *gpio, int line)
+{
+ u32 reg, shift;
+
+ if (nv50_gpio_location(line, &reg, &shift))
+ return -EINVAL;
+
+ return !!(nv_rd32(gpio, reg) & (4 << shift));
+}
+
+static void
+nv50_gpio_intr_stat(struct nvkm_gpio *gpio, u32 *hi, u32 *lo)
+{
+ u32 intr = nv_rd32(gpio, 0x00e054);
+ u32 stat = nv_rd32(gpio, 0x00e050) & intr;
+ *lo = (stat & 0xffff0000) >> 16;
+ *hi = (stat & 0x0000ffff);
+ nv_wr32(gpio, 0x00e054, intr);
+}
+
+static void
+nv50_gpio_intr_mask(struct nvkm_gpio *gpio, u32 type, u32 mask, u32 data)
+{
+ u32 inte = nv_rd32(gpio, 0x00e050);
+ if (type & NVKM_GPIO_LO)
+ inte = (inte & ~(mask << 16)) | (data << 16);
+ if (type & NVKM_GPIO_HI)
+ inte = (inte & ~mask) | data;
+ nv_wr32(gpio, 0x00e050, inte);
+}
+
+struct nvkm_oclass *
+nv50_gpio_oclass = &(struct nvkm_gpio_impl) {
+ .base.handle = NV_SUBDEV(GPIO, 0x50),
+ .base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = _nvkm_gpio_ctor,
+ .dtor = _nvkm_gpio_dtor,
+ .init = _nvkm_gpio_init,
+ .fini = _nvkm_gpio_fini,
+ },
+ .lines = 16,
+ .intr_stat = nv50_gpio_intr_stat,
+ .intr_mask = nv50_gpio_intr_mask,
+ .drive = nv50_gpio_drive,
+ .sense = nv50_gpio_sense,
+ .reset = nv50_gpio_reset,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/priv.h b/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/priv.h
new file mode 100644
index 000000000000..382f8d44e140
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/priv.h
@@ -0,0 +1,64 @@
+#ifndef __NVKM_GPIO_PRIV_H__
+#define __NVKM_GPIO_PRIV_H__
+#include <subdev/gpio.h>
+
+#define nvkm_gpio_create(p,e,o,d) \
+ nvkm_gpio_create_((p), (e), (o), sizeof(**d), (void **)d)
+#define nvkm_gpio_destroy(p) ({ \
+ struct nvkm_gpio *gpio = (p); \
+ _nvkm_gpio_dtor(nv_object(gpio)); \
+})
+#define nvkm_gpio_init(p) ({ \
+ struct nvkm_gpio *gpio = (p); \
+ _nvkm_gpio_init(nv_object(gpio)); \
+})
+#define nvkm_gpio_fini(p,s) ({ \
+ struct nvkm_gpio *gpio = (p); \
+ _nvkm_gpio_fini(nv_object(gpio), (s)); \
+})
+
+int nvkm_gpio_create_(struct nvkm_object *, struct nvkm_object *,
+ struct nvkm_oclass *, int, void **);
+int _nvkm_gpio_ctor(struct nvkm_object *, struct nvkm_object *,
+ struct nvkm_oclass *, void *, u32,
+ struct nvkm_object **);
+void _nvkm_gpio_dtor(struct nvkm_object *);
+int _nvkm_gpio_init(struct nvkm_object *);
+int _nvkm_gpio_fini(struct nvkm_object *, bool);
+
+struct nvkm_gpio_impl {
+ struct nvkm_oclass base;
+ int lines;
+
+ /* read and ack pending interrupts, returning only data
+ * for lines that have not been masked off, while still
+ * performing the ack for anything that was pending.
+ */
+ void (*intr_stat)(struct nvkm_gpio *, u32 *, u32 *);
+
+ /* mask on/off interrupts for hi/lo transitions on a
+ * given set of gpio lines
+ */
+ void (*intr_mask)(struct nvkm_gpio *, u32, u32, u32);
+
+ /* configure gpio direction and output value */
+ int (*drive)(struct nvkm_gpio *, int line, int dir, int out);
+
+ /* sense current state of given gpio line */
+ int (*sense)(struct nvkm_gpio *, int line);
+
+ /*XXX*/
+ void (*reset)(struct nvkm_gpio *, u8);
+};
+
+void nv50_gpio_reset(struct nvkm_gpio *, u8);
+int nv50_gpio_drive(struct nvkm_gpio *, int, int, int);
+int nv50_gpio_sense(struct nvkm_gpio *, int);
+
+void g94_gpio_intr_stat(struct nvkm_gpio *, u32 *, u32 *);
+void g94_gpio_intr_mask(struct nvkm_gpio *, u32, u32, u32);
+
+void gf110_gpio_reset(struct nvkm_gpio *, u8);
+int gf110_gpio_drive(struct nvkm_gpio *, int, int, int);
+int gf110_gpio_sense(struct nvkm_gpio *, int);
+#endif
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/Kbuild b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/Kbuild
new file mode 100644
index 000000000000..d68307409980
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/Kbuild
@@ -0,0 +1,16 @@
+nvkm-y += nvkm/subdev/i2c/base.o
+nvkm-y += nvkm/subdev/i2c/anx9805.o
+nvkm-y += nvkm/subdev/i2c/aux.o
+nvkm-y += nvkm/subdev/i2c/bit.o
+nvkm-y += nvkm/subdev/i2c/pad.o
+nvkm-y += nvkm/subdev/i2c/padnv04.o
+nvkm-y += nvkm/subdev/i2c/padg94.o
+nvkm-y += nvkm/subdev/i2c/padgm204.o
+nvkm-y += nvkm/subdev/i2c/nv04.o
+nvkm-y += nvkm/subdev/i2c/nv4e.o
+nvkm-y += nvkm/subdev/i2c/nv50.o
+nvkm-y += nvkm/subdev/i2c/g94.o
+nvkm-y += nvkm/subdev/i2c/gf110.o
+nvkm-y += nvkm/subdev/i2c/gf117.o
+nvkm-y += nvkm/subdev/i2c/gk104.o
+nvkm-y += nvkm/subdev/i2c/gm204.o
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/anx9805.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/anx9805.c
new file mode 100644
index 000000000000..d17dd1cf3c34
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/anx9805.c
@@ -0,0 +1,292 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs <bskeggs@redhat.com>
+ */
+#include "port.h"
+
+struct anx9805_i2c_port {
+ struct nvkm_i2c_port base;
+ u32 addr;
+ u32 ctrl;
+};
+
+static int
+anx9805_train(struct nvkm_i2c_port *port, int link_nr, int link_bw, bool enh)
+{
+ struct anx9805_i2c_port *chan = (void *)port;
+ struct nvkm_i2c_port *mast = (void *)nv_object(chan)->parent;
+ u8 tmp, i;
+
+ DBG("ANX9805 train %d 0x%02x %d\n", link_nr, link_bw, enh);
+
+ nv_wri2cr(mast, chan->addr, 0xa0, link_bw);
+ nv_wri2cr(mast, chan->addr, 0xa1, link_nr | (enh ? 0x80 : 0x00));
+ nv_wri2cr(mast, chan->addr, 0xa2, 0x01);
+ nv_wri2cr(mast, chan->addr, 0xa8, 0x01);
+
+ i = 0;
+ while ((tmp = nv_rdi2cr(mast, chan->addr, 0xa8)) & 0x01) {
+ mdelay(5);
+ if (i++ == 100) {
+ nv_error(port, "link training timed out\n");
+ return -ETIMEDOUT;
+ }
+ }
+
+ if (tmp & 0x70) {
+ nv_error(port, "link training failed: 0x%02x\n", tmp);
+ return -EIO;
+ }
+
+ return 1;
+}
+
+static int
+anx9805_aux(struct nvkm_i2c_port *port, bool retry,
+ u8 type, u32 addr, u8 *data, u8 size)
+{
+ struct anx9805_i2c_port *chan = (void *)port;
+ struct nvkm_i2c_port *mast = (void *)nv_object(chan)->parent;
+ int i, ret = -ETIMEDOUT;
+ u8 buf[16] = {};
+ u8 tmp;
+
+ DBG("%02x %05x %d\n", type, addr, size);
+
+ tmp = nv_rdi2cr(mast, chan->ctrl, 0x07) & ~0x04;
+ nv_wri2cr(mast, chan->ctrl, 0x07, tmp | 0x04);
+ nv_wri2cr(mast, chan->ctrl, 0x07, tmp);
+ nv_wri2cr(mast, chan->ctrl, 0xf7, 0x01);
+
+ nv_wri2cr(mast, chan->addr, 0xe4, 0x80);
+ if (!(type & 1)) {
+ memcpy(buf, data, size);
+ DBG("%16ph", buf);
+ for (i = 0; i < size; i++)
+ nv_wri2cr(mast, chan->addr, 0xf0 + i, buf[i]);
+ }
+ nv_wri2cr(mast, chan->addr, 0xe5, ((size - 1) << 4) | type);
+ nv_wri2cr(mast, chan->addr, 0xe6, (addr & 0x000ff) >> 0);
+ nv_wri2cr(mast, chan->addr, 0xe7, (addr & 0x0ff00) >> 8);
+ nv_wri2cr(mast, chan->addr, 0xe8, (addr & 0xf0000) >> 16);
+ nv_wri2cr(mast, chan->addr, 0xe9, 0x01);
+
+ i = 0;
+ while ((tmp = nv_rdi2cr(mast, chan->addr, 0xe9)) & 0x01) {
+ mdelay(5);
+ if (i++ == 32)
+ goto done;
+ }
+
+ if ((tmp = nv_rdi2cr(mast, chan->ctrl, 0xf7)) & 0x01) {
+ ret = -EIO;
+ goto done;
+ }
+
+ if (type & 1) {
+ for (i = 0; i < size; i++)
+ buf[i] = nv_rdi2cr(mast, chan->addr, 0xf0 + i);
+ DBG("%16ph", buf);
+ memcpy(data, buf, size);
+ }
+
+ ret = 0;
+done:
+ nv_wri2cr(mast, chan->ctrl, 0xf7, 0x01);
+ return ret;
+}
+
+static const struct nvkm_i2c_func
+anx9805_aux_func = {
+ .aux = anx9805_aux,
+ .lnk_ctl = anx9805_train,
+};
+
+static int
+anx9805_aux_chan_ctor(struct nvkm_object *parent,
+ struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 index,
+ struct nvkm_object **pobject)
+{
+ struct nvkm_i2c_port *mast = (void *)parent;
+ struct anx9805_i2c_port *chan;
+ int ret;
+
+ ret = nvkm_i2c_port_create(parent, engine, oclass, index,
+ &nvkm_i2c_aux_algo, &anx9805_aux_func,
+ &chan);
+ *pobject = nv_object(chan);
+ if (ret)
+ return ret;
+
+ switch ((oclass->handle & 0xff00) >> 8) {
+ case 0x0d:
+ chan->addr = 0x38;
+ chan->ctrl = 0x39;
+ break;
+ case 0x0e:
+ chan->addr = 0x3c;
+ chan->ctrl = 0x3b;
+ break;
+ default:
+ BUG_ON(1);
+ }
+
+ if (mast->adapter.algo == &i2c_bit_algo) {
+ struct i2c_algo_bit_data *algo = mast->adapter.algo_data;
+ algo->udelay = max(algo->udelay, 40);
+ }
+
+ return 0;
+}
+
+static struct nvkm_ofuncs
+anx9805_aux_ofuncs = {
+ .ctor = anx9805_aux_chan_ctor,
+ .dtor = _nvkm_i2c_port_dtor,
+ .init = _nvkm_i2c_port_init,
+ .fini = _nvkm_i2c_port_fini,
+};
+
+static int
+anx9805_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
+{
+ struct anx9805_i2c_port *port = adap->algo_data;
+ struct nvkm_i2c_port *mast = (void *)nv_object(port)->parent;
+ struct i2c_msg *msg = msgs;
+ int ret = -ETIMEDOUT;
+ int i, j, cnt = num;
+ u8 seg = 0x00, off = 0x00, tmp;
+
+ tmp = nv_rdi2cr(mast, port->ctrl, 0x07) & ~0x10;
+ nv_wri2cr(mast, port->ctrl, 0x07, tmp | 0x10);
+ nv_wri2cr(mast, port->ctrl, 0x07, tmp);
+ nv_wri2cr(mast, port->addr, 0x43, 0x05);
+ mdelay(5);
+
+ while (cnt--) {
+ if ( (msg->flags & I2C_M_RD) && msg->addr == 0x50) {
+ nv_wri2cr(mast, port->addr, 0x40, msg->addr << 1);
+ nv_wri2cr(mast, port->addr, 0x41, seg);
+ nv_wri2cr(mast, port->addr, 0x42, off);
+ nv_wri2cr(mast, port->addr, 0x44, msg->len);
+ nv_wri2cr(mast, port->addr, 0x45, 0x00);
+ nv_wri2cr(mast, port->addr, 0x43, 0x01);
+ for (i = 0; i < msg->len; i++) {
+ j = 0;
+ while (nv_rdi2cr(mast, port->addr, 0x46) & 0x10) {
+ mdelay(5);
+ if (j++ == 32)
+ goto done;
+ }
+ msg->buf[i] = nv_rdi2cr(mast, port->addr, 0x47);
+ }
+ } else
+ if (!(msg->flags & I2C_M_RD)) {
+ if (msg->addr == 0x50 && msg->len == 0x01) {
+ off = msg->buf[0];
+ } else
+ if (msg->addr == 0x30 && msg->len == 0x01) {
+ seg = msg->buf[0];
+ } else
+ goto done;
+ } else {
+ goto done;
+ }
+ msg++;
+ }
+
+ ret = num;
+done:
+ nv_wri2cr(mast, port->addr, 0x43, 0x00);
+ return ret;
+}
+
+static u32
+anx9805_func(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm
+anx9805_i2c_algo = {
+ .master_xfer = anx9805_xfer,
+ .functionality = anx9805_func
+};
+
+static const struct nvkm_i2c_func
+anx9805_i2c_func = {
+};
+
+static int
+anx9805_ddc_port_ctor(struct nvkm_object *parent,
+ struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 index,
+ struct nvkm_object **pobject)
+{
+ struct nvkm_i2c_port *mast = (void *)parent;
+ struct anx9805_i2c_port *port;
+ int ret;
+
+ ret = nvkm_i2c_port_create(parent, engine, oclass, index,
+ &anx9805_i2c_algo, &anx9805_i2c_func, &port);
+ *pobject = nv_object(port);
+ if (ret)
+ return ret;
+
+ switch ((oclass->handle & 0xff00) >> 8) {
+ case 0x0d:
+ port->addr = 0x3d;
+ port->ctrl = 0x39;
+ break;
+ case 0x0e:
+ port->addr = 0x3f;
+ port->ctrl = 0x3b;
+ break;
+ default:
+ BUG_ON(1);
+ }
+
+ if (mast->adapter.algo == &i2c_bit_algo) {
+ struct i2c_algo_bit_data *algo = mast->adapter.algo_data;
+ algo->udelay = max(algo->udelay, 40);
+ }
+
+ return 0;
+}
+
+static struct nvkm_ofuncs
+anx9805_ddc_ofuncs = {
+ .ctor = anx9805_ddc_port_ctor,
+ .dtor = _nvkm_i2c_port_dtor,
+ .init = _nvkm_i2c_port_init,
+ .fini = _nvkm_i2c_port_fini,
+};
+
+struct nvkm_oclass
+nvkm_anx9805_sclass[] = {
+ { .handle = NV_I2C_TYPE_EXTDDC(0x0d), .ofuncs = &anx9805_ddc_ofuncs },
+ { .handle = NV_I2C_TYPE_EXTAUX(0x0d), .ofuncs = &anx9805_aux_ofuncs },
+ { .handle = NV_I2C_TYPE_EXTDDC(0x0e), .ofuncs = &anx9805_ddc_ofuncs },
+ { .handle = NV_I2C_TYPE_EXTAUX(0x0e), .ofuncs = &anx9805_aux_ofuncs },
+ {}
+};
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/aux.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/aux.c
new file mode 100644
index 000000000000..1c18860f80d1
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/aux.c
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2009 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "priv.h"
+
+int
+nv_rdaux(struct nvkm_i2c_port *port, u32 addr, u8 *data, u8 size)
+{
+ struct nvkm_i2c *i2c = nvkm_i2c(port);
+ if (port->func->aux) {
+ int ret = i2c->acquire(port, 0);
+ if (ret == 0) {
+ ret = port->func->aux(port, true, 9, addr, data, size);
+ i2c->release(port);
+ }
+ return ret;
+ }
+ return -ENODEV;
+}
+
+int
+nv_wraux(struct nvkm_i2c_port *port, u32 addr, u8 *data, u8 size)
+{
+ struct nvkm_i2c *i2c = nvkm_i2c(port);
+ if (port->func->aux) {
+ int ret = i2c->acquire(port, 0);
+ if (ret == 0) {
+ ret = port->func->aux(port, true, 8, addr, data, size);
+ i2c->release(port);
+ }
+ return ret;
+ }
+ return -ENODEV;
+}
+
+static int
+aux_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
+{
+ struct nvkm_i2c_port *port = adap->algo_data;
+ struct nvkm_i2c *i2c = nvkm_i2c(port);
+ struct i2c_msg *msg = msgs;
+ int ret, mcnt = num;
+
+ if (!port->func->aux)
+ return -ENODEV;
+
+ ret = i2c->acquire(port, 0);
+ if (ret)
+ return ret;
+
+ while (mcnt--) {
+ u8 remaining = msg->len;
+ u8 *ptr = msg->buf;
+
+ while (remaining) {
+ u8 cnt = (remaining > 16) ? 16 : remaining;
+ u8 cmd;
+
+ if (msg->flags & I2C_M_RD)
+ cmd = 1;
+ else
+ cmd = 0;
+
+ if (mcnt || remaining > 16)
+ cmd |= 4; /* MOT */
+
+ ret = port->func->aux(port, true, cmd, msg->addr, ptr, cnt);
+ if (ret < 0) {
+ i2c->release(port);
+ return ret;
+ }
+
+ ptr += cnt;
+ remaining -= cnt;
+ }
+
+ msg++;
+ }
+
+ i2c->release(port);
+ return num;
+}
+
+static u32
+aux_func(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+const struct i2c_algorithm nvkm_i2c_aux_algo = {
+ .master_xfer = aux_xfer,
+ .functionality = aux_func
+};
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/base.c
new file mode 100644
index 000000000000..9200f122c02c
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/base.c
@@ -0,0 +1,622 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "priv.h"
+#include "pad.h"
+
+#include <core/device.h>
+#include <core/notify.h>
+#include <core/option.h>
+#include <subdev/bios.h>
+#include <subdev/bios/dcb.h>
+
+/******************************************************************************
+ * interface to linux i2c bit-banging algorithm
+ *****************************************************************************/
+
+#ifdef CONFIG_NOUVEAU_I2C_INTERNAL_DEFAULT
+#define CSTMSEL true
+#else
+#define CSTMSEL false
+#endif
+
+static int
+nvkm_i2c_pre_xfer(struct i2c_adapter *adap)
+{
+ struct i2c_algo_bit_data *bit = adap->algo_data;
+ struct nvkm_i2c_port *port = bit->data;
+ return nvkm_i2c(port)->acquire(port, bit->timeout);
+}
+
+static void
+nvkm_i2c_post_xfer(struct i2c_adapter *adap)
+{
+ struct i2c_algo_bit_data *bit = adap->algo_data;
+ struct nvkm_i2c_port *port = bit->data;
+ return nvkm_i2c(port)->release(port);
+}
+
+static void
+nvkm_i2c_setscl(void *data, int state)
+{
+ struct nvkm_i2c_port *port = data;
+ port->func->drive_scl(port, state);
+}
+
+static void
+nvkm_i2c_setsda(void *data, int state)
+{
+ struct nvkm_i2c_port *port = data;
+ port->func->drive_sda(port, state);
+}
+
+static int
+nvkm_i2c_getscl(void *data)
+{
+ struct nvkm_i2c_port *port = data;
+ return port->func->sense_scl(port);
+}
+
+static int
+nvkm_i2c_getsda(void *data)
+{
+ struct nvkm_i2c_port *port = data;
+ return port->func->sense_sda(port);
+}
+
+/******************************************************************************
+ * base i2c "port" class implementation
+ *****************************************************************************/
+
+int
+_nvkm_i2c_port_fini(struct nvkm_object *object, bool suspend)
+{
+ struct nvkm_i2c_port *port = (void *)object;
+ struct nvkm_i2c_pad *pad = nvkm_i2c_pad(port);
+ nv_ofuncs(pad)->fini(nv_object(pad), suspend);
+ return nvkm_object_fini(&port->base, suspend);
+}
+
+void
+_nvkm_i2c_port_dtor(struct nvkm_object *object)
+{
+ struct nvkm_i2c_port *port = (void *)object;
+ i2c_del_adapter(&port->adapter);
+ nvkm_object_destroy(&port->base);
+}
+
+int
+nvkm_i2c_port_create_(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, u8 index,
+ const struct i2c_algorithm *algo,
+ const struct nvkm_i2c_func *func,
+ int size, void **pobject)
+{
+ struct nvkm_device *device = nv_device(parent);
+ struct nvkm_i2c *i2c = nvkm_i2c(parent);
+ struct nvkm_i2c_port *port;
+ int ret;
+
+ ret = nvkm_object_create_(parent, engine, oclass, 0, size, pobject);
+ port = *pobject;
+ if (ret)
+ return ret;
+
+ snprintf(port->adapter.name, sizeof(port->adapter.name),
+ "nvkm-%s-%d", device->name, index);
+ port->adapter.owner = THIS_MODULE;
+ port->adapter.dev.parent = nv_device_base(device);
+ port->index = index;
+ port->aux = -1;
+ port->func = func;
+ mutex_init(&port->mutex);
+
+ if ( algo == &nvkm_i2c_bit_algo &&
+ !nvkm_boolopt(device->cfgopt, "NvI2C", CSTMSEL)) {
+ struct i2c_algo_bit_data *bit;
+
+ bit = kzalloc(sizeof(*bit), GFP_KERNEL);
+ if (!bit)
+ return -ENOMEM;
+
+ bit->udelay = 10;
+ bit->timeout = usecs_to_jiffies(2200);
+ bit->data = port;
+ bit->pre_xfer = nvkm_i2c_pre_xfer;
+ bit->post_xfer = nvkm_i2c_post_xfer;
+ bit->setsda = nvkm_i2c_setsda;
+ bit->setscl = nvkm_i2c_setscl;
+ bit->getsda = nvkm_i2c_getsda;
+ bit->getscl = nvkm_i2c_getscl;
+
+ port->adapter.algo_data = bit;
+ ret = i2c_bit_add_bus(&port->adapter);
+ } else {
+ port->adapter.algo_data = port;
+ port->adapter.algo = algo;
+ ret = i2c_add_adapter(&port->adapter);
+ }
+
+ if (ret == 0)
+ list_add_tail(&port->head, &i2c->ports);
+ return ret;
+}
+
+/******************************************************************************
+ * base i2c subdev class implementation
+ *****************************************************************************/
+
+static struct nvkm_i2c_port *
+nvkm_i2c_find(struct nvkm_i2c *i2c, u8 index)
+{
+ struct nvkm_bios *bios = nvkm_bios(i2c);
+ struct nvkm_i2c_port *port;
+
+ if (index == NV_I2C_DEFAULT(0) ||
+ index == NV_I2C_DEFAULT(1)) {
+ u8 ver, hdr, cnt, len;
+ u16 i2c = dcb_i2c_table(bios, &ver, &hdr, &cnt, &len);
+ if (i2c && ver >= 0x30) {
+ u8 auxidx = nv_ro08(bios, i2c + 4);
+ if (index == NV_I2C_DEFAULT(0))
+ index = (auxidx & 0x0f) >> 0;
+ else
+ index = (auxidx & 0xf0) >> 4;
+ } else {
+ index = 2;
+ }
+ }
+
+ list_for_each_entry(port, &i2c->ports, head) {
+ if (port->index == index)
+ return port;
+ }
+
+ return NULL;
+}
+
+static struct nvkm_i2c_port *
+nvkm_i2c_find_type(struct nvkm_i2c *i2c, u16 type)
+{
+ struct nvkm_i2c_port *port;
+
+ list_for_each_entry(port, &i2c->ports, head) {
+ if (nv_hclass(port) == type)
+ return port;
+ }
+
+ return NULL;
+}
+
+static void
+nvkm_i2c_release_pad(struct nvkm_i2c_port *port)
+{
+ struct nvkm_i2c_pad *pad = nvkm_i2c_pad(port);
+ struct nvkm_i2c *i2c = nvkm_i2c(port);
+
+ if (atomic_dec_and_test(&nv_object(pad)->usecount)) {
+ nv_ofuncs(pad)->fini(nv_object(pad), false);
+ wake_up_all(&i2c->wait);
+ }
+}
+
+static int
+nvkm_i2c_try_acquire_pad(struct nvkm_i2c_port *port)
+{
+ struct nvkm_i2c_pad *pad = nvkm_i2c_pad(port);
+
+ if (atomic_add_return(1, &nv_object(pad)->usecount) != 1) {
+ struct nvkm_object *owner = (void *)pad->port;
+ do {
+ if (owner == (void *)port)
+ return 0;
+ owner = owner->parent;
+ } while(owner);
+ nvkm_i2c_release_pad(port);
+ return -EBUSY;
+ }
+
+ pad->next = port;
+ nv_ofuncs(pad)->init(nv_object(pad));
+ return 0;
+}
+
+static int
+nvkm_i2c_acquire_pad(struct nvkm_i2c_port *port, unsigned long timeout)
+{
+ struct nvkm_i2c *i2c = nvkm_i2c(port);
+
+ if (timeout) {
+ if (wait_event_timeout(i2c->wait,
+ nvkm_i2c_try_acquire_pad(port) == 0,
+ timeout) == 0)
+ return -EBUSY;
+ } else {
+ wait_event(i2c->wait, nvkm_i2c_try_acquire_pad(port) == 0);
+ }
+
+ return 0;
+}
+
+static void
+nvkm_i2c_release(struct nvkm_i2c_port *port)
+__releases(pad->mutex)
+{
+ nvkm_i2c(port)->release_pad(port);
+ mutex_unlock(&port->mutex);
+}
+
+static int
+nvkm_i2c_acquire(struct nvkm_i2c_port *port, unsigned long timeout)
+__acquires(pad->mutex)
+{
+ int ret;
+ mutex_lock(&port->mutex);
+ if ((ret = nvkm_i2c(port)->acquire_pad(port, timeout)))
+ mutex_unlock(&port->mutex);
+ return ret;
+}
+
+static int
+nvkm_i2c_identify(struct nvkm_i2c *i2c, int index, const char *what,
+ struct nvkm_i2c_board_info *info,
+ bool (*match)(struct nvkm_i2c_port *,
+ struct i2c_board_info *, void *), void *data)
+{
+ struct nvkm_i2c_port *port = nvkm_i2c_find(i2c, index);
+ int i;
+
+ if (!port) {
+ nv_debug(i2c, "no bus when probing %s on %d\n", what, index);
+ return -ENODEV;
+ }
+
+ nv_debug(i2c, "probing %ss on bus: %d\n", what, port->index);
+ for (i = 0; info[i].dev.addr; i++) {
+ u8 orig_udelay = 0;
+
+ if ((port->adapter.algo == &i2c_bit_algo) &&
+ (info[i].udelay != 0)) {
+ struct i2c_algo_bit_data *algo = port->adapter.algo_data;
+ nv_debug(i2c, "using custom udelay %d instead of %d\n",
+ info[i].udelay, algo->udelay);
+ orig_udelay = algo->udelay;
+ algo->udelay = info[i].udelay;
+ }
+
+ if (nv_probe_i2c(port, info[i].dev.addr) &&
+ (!match || match(port, &info[i].dev, data))) {
+ nv_info(i2c, "detected %s: %s\n", what,
+ info[i].dev.type);
+ return i;
+ }
+
+ if (orig_udelay) {
+ struct i2c_algo_bit_data *algo = port->adapter.algo_data;
+ algo->udelay = orig_udelay;
+ }
+ }
+
+ nv_debug(i2c, "no devices found.\n");
+ return -ENODEV;
+}
+
+static void
+nvkm_i2c_intr_fini(struct nvkm_event *event, int type, int index)
+{
+ struct nvkm_i2c *i2c = container_of(event, typeof(*i2c), event);
+ struct nvkm_i2c_port *port = i2c->find(i2c, index);
+ const struct nvkm_i2c_impl *impl = (void *)nv_object(i2c)->oclass;
+ if (port && port->aux >= 0)
+ impl->aux_mask(i2c, type, 1 << port->aux, 0);
+}
+
+static void
+nvkm_i2c_intr_init(struct nvkm_event *event, int type, int index)
+{
+ struct nvkm_i2c *i2c = container_of(event, typeof(*i2c), event);
+ struct nvkm_i2c_port *port = i2c->find(i2c, index);
+ const struct nvkm_i2c_impl *impl = (void *)nv_object(i2c)->oclass;
+ if (port && port->aux >= 0)
+ impl->aux_mask(i2c, type, 1 << port->aux, 1 << port->aux);
+}
+
+static int
+nvkm_i2c_intr_ctor(struct nvkm_object *object, void *data, u32 size,
+ struct nvkm_notify *notify)
+{
+ struct nvkm_i2c_ntfy_req *req = data;
+ if (!WARN_ON(size != sizeof(*req))) {
+ notify->size = sizeof(struct nvkm_i2c_ntfy_rep);
+ notify->types = req->mask;
+ notify->index = req->port;
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static void
+nvkm_i2c_intr(struct nvkm_subdev *subdev)
+{
+ struct nvkm_i2c_impl *impl = (void *)nv_oclass(subdev);
+ struct nvkm_i2c *i2c = nvkm_i2c(subdev);
+ struct nvkm_i2c_port *port;
+ u32 hi, lo, rq, tx, e;
+
+ if (impl->aux_stat) {
+ impl->aux_stat(i2c, &hi, &lo, &rq, &tx);
+ if (hi || lo || rq || tx) {
+ list_for_each_entry(port, &i2c->ports, head) {
+ if (e = 0, port->aux < 0)
+ continue;
+
+ if (hi & (1 << port->aux)) e |= NVKM_I2C_PLUG;
+ if (lo & (1 << port->aux)) e |= NVKM_I2C_UNPLUG;
+ if (rq & (1 << port->aux)) e |= NVKM_I2C_IRQ;
+ if (tx & (1 << port->aux)) e |= NVKM_I2C_DONE;
+ if (e) {
+ struct nvkm_i2c_ntfy_rep rep = {
+ .mask = e,
+ };
+ nvkm_event_send(&i2c->event, rep.mask,
+ port->index, &rep,
+ sizeof(rep));
+ }
+ }
+ }
+ }
+}
+
+static const struct nvkm_event_func
+nvkm_i2c_intr_func = {
+ .ctor = nvkm_i2c_intr_ctor,
+ .init = nvkm_i2c_intr_init,
+ .fini = nvkm_i2c_intr_fini,
+};
+
+int
+_nvkm_i2c_fini(struct nvkm_object *object, bool suspend)
+{
+ struct nvkm_i2c_impl *impl = (void *)nv_oclass(object);
+ struct nvkm_i2c *i2c = (void *)object;
+ struct nvkm_i2c_port *port;
+ u32 mask;
+ int ret;
+
+ list_for_each_entry(port, &i2c->ports, head) {
+ ret = nv_ofuncs(port)->fini(nv_object(port), suspend);
+ if (ret && suspend)
+ goto fail;
+ }
+
+ if ((mask = (1 << impl->aux) - 1), impl->aux_stat) {
+ impl->aux_mask(i2c, NVKM_I2C_ANY, mask, 0);
+ impl->aux_stat(i2c, &mask, &mask, &mask, &mask);
+ }
+
+ return nvkm_subdev_fini(&i2c->base, suspend);
+fail:
+ list_for_each_entry_continue_reverse(port, &i2c->ports, head) {
+ nv_ofuncs(port)->init(nv_object(port));
+ }
+
+ return ret;
+}
+
+int
+_nvkm_i2c_init(struct nvkm_object *object)
+{
+ struct nvkm_i2c *i2c = (void *)object;
+ struct nvkm_i2c_port *port;
+ int ret;
+
+ ret = nvkm_subdev_init(&i2c->base);
+ if (ret == 0) {
+ list_for_each_entry(port, &i2c->ports, head) {
+ ret = nv_ofuncs(port)->init(nv_object(port));
+ if (ret)
+ goto fail;
+ }
+ }
+
+ return ret;
+fail:
+ list_for_each_entry_continue_reverse(port, &i2c->ports, head) {
+ nv_ofuncs(port)->fini(nv_object(port), false);
+ }
+
+ return ret;
+}
+
+void
+_nvkm_i2c_dtor(struct nvkm_object *object)
+{
+ struct nvkm_i2c *i2c = (void *)object;
+ struct nvkm_i2c_port *port, *temp;
+
+ nvkm_event_fini(&i2c->event);
+
+ list_for_each_entry_safe(port, temp, &i2c->ports, head) {
+ nvkm_object_ref(NULL, (struct nvkm_object **)&port);
+ }
+
+ nvkm_subdev_destroy(&i2c->base);
+}
+
+static struct nvkm_oclass *
+nvkm_i2c_extdev_sclass[] = {
+ nvkm_anx9805_sclass,
+};
+
+static void
+nvkm_i2c_create_port(struct nvkm_i2c *i2c, int index, u8 type,
+ struct dcb_i2c_entry *info)
+{
+ const struct nvkm_i2c_impl *impl = (void *)nv_oclass(i2c);
+ struct nvkm_oclass *oclass;
+ struct nvkm_object *parent;
+ struct nvkm_object *object;
+ int ret, pad;
+
+ if (info->share != DCB_I2C_UNUSED) {
+ pad = info->share;
+ oclass = impl->pad_s;
+ } else {
+ if (type != DCB_I2C_NVIO_AUX)
+ pad = 0x100 + info->drive;
+ else
+ pad = 0x100 + info->auxch;
+ oclass = impl->pad_x;
+ }
+
+ ret = nvkm_object_ctor(nv_object(i2c), NULL, oclass,
+ NULL, pad, &parent);
+ if (ret < 0)
+ return;
+
+ oclass = impl->sclass;
+ do {
+ ret = -EINVAL;
+ if (oclass->handle == type) {
+ ret = nvkm_object_ctor(parent, NULL, oclass,
+ info, index, &object);
+ }
+ } while (ret && (++oclass)->handle);
+
+ nvkm_object_ref(NULL, &parent);
+}
+
+int
+nvkm_i2c_create_(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, int length, void **pobject)
+{
+ struct nvkm_bios *bios = nvkm_bios(parent);
+ struct nvkm_i2c *i2c;
+ struct nvkm_object *object;
+ struct dcb_i2c_entry info;
+ int ret, i, j, index = -1;
+ struct dcb_output outp;
+ u8 ver, hdr;
+ u32 data;
+
+ ret = nvkm_subdev_create(parent, engine, oclass, 0, "I2C", "i2c", &i2c);
+ *pobject = nv_object(i2c);
+ if (ret)
+ return ret;
+
+ nv_subdev(i2c)->intr = nvkm_i2c_intr;
+ i2c->find = nvkm_i2c_find;
+ i2c->find_type = nvkm_i2c_find_type;
+ i2c->acquire_pad = nvkm_i2c_acquire_pad;
+ i2c->release_pad = nvkm_i2c_release_pad;
+ i2c->acquire = nvkm_i2c_acquire;
+ i2c->release = nvkm_i2c_release;
+ i2c->identify = nvkm_i2c_identify;
+ init_waitqueue_head(&i2c->wait);
+ INIT_LIST_HEAD(&i2c->ports);
+
+ while (!dcb_i2c_parse(bios, ++index, &info)) {
+ switch (info.type) {
+ case DCB_I2C_NV04_BIT:
+ case DCB_I2C_NV4E_BIT:
+ case DCB_I2C_NVIO_BIT:
+ nvkm_i2c_create_port(i2c, NV_I2C_PORT(index),
+ info.type, &info);
+ break;
+ case DCB_I2C_NVIO_AUX:
+ nvkm_i2c_create_port(i2c, NV_I2C_AUX(index),
+ info.type, &info);
+ break;
+ case DCB_I2C_PMGR:
+ if (info.drive != DCB_I2C_UNUSED) {
+ nvkm_i2c_create_port(i2c, NV_I2C_PORT(index),
+ DCB_I2C_NVIO_BIT, &info);
+ }
+ if (info.auxch != DCB_I2C_UNUSED) {
+ nvkm_i2c_create_port(i2c, NV_I2C_AUX(index),
+ DCB_I2C_NVIO_AUX, &info);
+ }
+ break;
+ case DCB_I2C_UNUSED:
+ default:
+ continue;
+ }
+ }
+
+ /* in addition to the busses specified in the i2c table, there
+ * may be ddc/aux channels hiding behind external tmds/dp/etc
+ * transmitters.
+ */
+ index = NV_I2C_EXT(0);
+ i = -1;
+ while ((data = dcb_outp_parse(bios, ++i, &ver, &hdr, &outp))) {
+ if (!outp.location || !outp.extdev)
+ continue;
+
+ switch (outp.type) {
+ case DCB_OUTPUT_TMDS:
+ info.type = NV_I2C_TYPE_EXTDDC(outp.extdev);
+ break;
+ case DCB_OUTPUT_DP:
+ info.type = NV_I2C_TYPE_EXTAUX(outp.extdev);
+ break;
+ default:
+ continue;
+ }
+
+ ret = -ENODEV;
+ j = -1;
+ while (ret && ++j < ARRAY_SIZE(nvkm_i2c_extdev_sclass)) {
+ parent = nv_object(i2c->find(i2c, outp.i2c_index));
+ oclass = nvkm_i2c_extdev_sclass[j];
+ do {
+ if (oclass->handle != info.type)
+ continue;
+ ret = nvkm_object_ctor(parent, NULL, oclass,
+ NULL, index++, &object);
+ } while (ret && (++oclass)->handle);
+ }
+ }
+
+ ret = nvkm_event_init(&nvkm_i2c_intr_func, 4, index, &i2c->event);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+int
+_nvkm_i2c_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 size,
+ struct nvkm_object **pobject)
+{
+ struct nvkm_i2c *i2c;
+ int ret;
+
+ ret = nvkm_i2c_create(parent, engine, oclass, &i2c);
+ *pobject = nv_object(i2c);
+ if (ret)
+ return ret;
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/bit.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/bit.c
new file mode 100644
index 000000000000..861a453d2a67
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/bit.c
@@ -0,0 +1,233 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "priv.h"
+
+#ifdef CONFIG_NOUVEAU_I2C_INTERNAL
+#define T_TIMEOUT 2200000
+#define T_RISEFALL 1000
+#define T_HOLD 5000
+
+static inline void
+i2c_drive_scl(struct nvkm_i2c_port *port, int state)
+{
+ port->func->drive_scl(port, state);
+}
+
+static inline void
+i2c_drive_sda(struct nvkm_i2c_port *port, int state)
+{
+ port->func->drive_sda(port, state);
+}
+
+static inline int
+i2c_sense_scl(struct nvkm_i2c_port *port)
+{
+ return port->func->sense_scl(port);
+}
+
+static inline int
+i2c_sense_sda(struct nvkm_i2c_port *port)
+{
+ return port->func->sense_sda(port);
+}
+
+static void
+i2c_delay(struct nvkm_i2c_port *port, u32 nsec)
+{
+ udelay((nsec + 500) / 1000);
+}
+
+static bool
+i2c_raise_scl(struct nvkm_i2c_port *port)
+{
+ u32 timeout = T_TIMEOUT / T_RISEFALL;
+
+ i2c_drive_scl(port, 1);
+ do {
+ i2c_delay(port, T_RISEFALL);
+ } while (!i2c_sense_scl(port) && --timeout);
+
+ return timeout != 0;
+}
+
+static int
+i2c_start(struct nvkm_i2c_port *port)
+{
+ int ret = 0;
+
+ if (!i2c_sense_scl(port) ||
+ !i2c_sense_sda(port)) {
+ i2c_drive_scl(port, 0);
+ i2c_drive_sda(port, 1);
+ if (!i2c_raise_scl(port))
+ ret = -EBUSY;
+ }
+
+ i2c_drive_sda(port, 0);
+ i2c_delay(port, T_HOLD);
+ i2c_drive_scl(port, 0);
+ i2c_delay(port, T_HOLD);
+ return ret;
+}
+
+static void
+i2c_stop(struct nvkm_i2c_port *port)
+{
+ i2c_drive_scl(port, 0);
+ i2c_drive_sda(port, 0);
+ i2c_delay(port, T_RISEFALL);
+
+ i2c_drive_scl(port, 1);
+ i2c_delay(port, T_HOLD);
+ i2c_drive_sda(port, 1);
+ i2c_delay(port, T_HOLD);
+}
+
+static int
+i2c_bitw(struct nvkm_i2c_port *port, int sda)
+{
+ i2c_drive_sda(port, sda);
+ i2c_delay(port, T_RISEFALL);
+
+ if (!i2c_raise_scl(port))
+ return -ETIMEDOUT;
+ i2c_delay(port, T_HOLD);
+
+ i2c_drive_scl(port, 0);
+ i2c_delay(port, T_HOLD);
+ return 0;
+}
+
+static int
+i2c_bitr(struct nvkm_i2c_port *port)
+{
+ int sda;
+
+ i2c_drive_sda(port, 1);
+ i2c_delay(port, T_RISEFALL);
+
+ if (!i2c_raise_scl(port))
+ return -ETIMEDOUT;
+ i2c_delay(port, T_HOLD);
+
+ sda = i2c_sense_sda(port);
+
+ i2c_drive_scl(port, 0);
+ i2c_delay(port, T_HOLD);
+ return sda;
+}
+
+static int
+i2c_get_byte(struct nvkm_i2c_port *port, u8 *byte, bool last)
+{
+ int i, bit;
+
+ *byte = 0;
+ for (i = 7; i >= 0; i--) {
+ bit = i2c_bitr(port);
+ if (bit < 0)
+ return bit;
+ *byte |= bit << i;
+ }
+
+ return i2c_bitw(port, last ? 1 : 0);
+}
+
+static int
+i2c_put_byte(struct nvkm_i2c_port *port, u8 byte)
+{
+ int i, ret;
+ for (i = 7; i >= 0; i--) {
+ ret = i2c_bitw(port, !!(byte & (1 << i)));
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = i2c_bitr(port);
+ if (ret == 1) /* nack */
+ ret = -EIO;
+ return ret;
+}
+
+static int
+i2c_addr(struct nvkm_i2c_port *port, struct i2c_msg *msg)
+{
+ u32 addr = msg->addr << 1;
+ if (msg->flags & I2C_M_RD)
+ addr |= 1;
+ return i2c_put_byte(port, addr);
+}
+
+static int
+i2c_bit_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
+{
+ struct nvkm_i2c_port *port = adap->algo_data;
+ struct i2c_msg *msg = msgs;
+ int ret = 0, mcnt = num;
+
+ ret = nvkm_i2c(port)->acquire(port, nsecs_to_jiffies(T_TIMEOUT));
+ if (ret)
+ return ret;
+
+ while (!ret && mcnt--) {
+ u8 remaining = msg->len;
+ u8 *ptr = msg->buf;
+
+ ret = i2c_start(port);
+ if (ret == 0)
+ ret = i2c_addr(port, msg);
+
+ if (msg->flags & I2C_M_RD) {
+ while (!ret && remaining--)
+ ret = i2c_get_byte(port, ptr++, !remaining);
+ } else {
+ while (!ret && remaining--)
+ ret = i2c_put_byte(port, *ptr++);
+ }
+
+ msg++;
+ }
+
+ i2c_stop(port);
+ nvkm_i2c(port)->release(port);
+ return (ret < 0) ? ret : num;
+}
+#else
+static int
+i2c_bit_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
+{
+ return -ENODEV;
+}
+#endif
+
+static u32
+i2c_bit_func(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+const struct i2c_algorithm nvkm_i2c_bit_algo = {
+ .master_xfer = i2c_bit_xfer,
+ .functionality = i2c_bit_func
+};
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/g94.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/g94.c
new file mode 100644
index 000000000000..2a2dd47b9835
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/g94.c
@@ -0,0 +1,279 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "nv50.h"
+
+void
+g94_aux_stat(struct nvkm_i2c *i2c, u32 *hi, u32 *lo, u32 *rq, u32 *tx)
+{
+ u32 intr = nv_rd32(i2c, 0x00e06c);
+ u32 stat = nv_rd32(i2c, 0x00e068) & intr, i;
+ for (i = 0, *hi = *lo = *rq = *tx = 0; i < 8; i++) {
+ if ((stat & (1 << (i * 4)))) *hi |= 1 << i;
+ if ((stat & (2 << (i * 4)))) *lo |= 1 << i;
+ if ((stat & (4 << (i * 4)))) *rq |= 1 << i;
+ if ((stat & (8 << (i * 4)))) *tx |= 1 << i;
+ }
+ nv_wr32(i2c, 0x00e06c, intr);
+}
+
+void
+g94_aux_mask(struct nvkm_i2c *i2c, u32 type, u32 mask, u32 data)
+{
+ u32 temp = nv_rd32(i2c, 0x00e068), i;
+ for (i = 0; i < 8; i++) {
+ if (mask & (1 << i)) {
+ if (!(data & (1 << i))) {
+ temp &= ~(type << (i * 4));
+ continue;
+ }
+ temp |= type << (i * 4);
+ }
+ }
+ nv_wr32(i2c, 0x00e068, temp);
+}
+
+#define AUX_DBG(fmt, args...) nv_debug(aux, "AUXCH(%d): " fmt, ch, ##args)
+#define AUX_ERR(fmt, args...) nv_error(aux, "AUXCH(%d): " fmt, ch, ##args)
+
+static void
+auxch_fini(struct nvkm_i2c *aux, int ch)
+{
+ nv_mask(aux, 0x00e4e4 + (ch * 0x50), 0x00310000, 0x00000000);
+}
+
+static int
+auxch_init(struct nvkm_i2c *aux, int ch)
+{
+ const u32 unksel = 1; /* nfi which to use, or if it matters.. */
+ const u32 ureq = unksel ? 0x00100000 : 0x00200000;
+ const u32 urep = unksel ? 0x01000000 : 0x02000000;
+ u32 ctrl, timeout;
+
+ /* wait up to 1ms for any previous transaction to be done... */
+ timeout = 1000;
+ do {
+ ctrl = nv_rd32(aux, 0x00e4e4 + (ch * 0x50));
+ udelay(1);
+ if (!timeout--) {
+ AUX_ERR("begin idle timeout 0x%08x\n", ctrl);
+ return -EBUSY;
+ }
+ } while (ctrl & 0x03010000);
+
+ /* set some magic, and wait up to 1ms for it to appear */
+ nv_mask(aux, 0x00e4e4 + (ch * 0x50), 0x00300000, ureq);
+ timeout = 1000;
+ do {
+ ctrl = nv_rd32(aux, 0x00e4e4 + (ch * 0x50));
+ udelay(1);
+ if (!timeout--) {
+ AUX_ERR("magic wait 0x%08x\n", ctrl);
+ auxch_fini(aux, ch);
+ return -EBUSY;
+ }
+ } while ((ctrl & 0x03000000) != urep);
+
+ return 0;
+}
+
+int
+g94_aux(struct nvkm_i2c_port *base, bool retry,
+ u8 type, u32 addr, u8 *data, u8 size)
+{
+ struct nvkm_i2c *aux = nvkm_i2c(base);
+ struct nv50_i2c_port *port = (void *)base;
+ u32 ctrl, stat, timeout, retries;
+ u32 xbuf[4] = {};
+ int ch = port->addr;
+ int ret, i;
+
+ AUX_DBG("%d: 0x%08x %d\n", type, addr, size);
+
+ ret = auxch_init(aux, ch);
+ if (ret)
+ goto out;
+
+ stat = nv_rd32(aux, 0x00e4e8 + (ch * 0x50));
+ if (!(stat & 0x10000000)) {
+ AUX_DBG("sink not detected\n");
+ ret = -ENXIO;
+ goto out;
+ }
+
+ if (!(type & 1)) {
+ memcpy(xbuf, data, size);
+ for (i = 0; i < 16; i += 4) {
+ AUX_DBG("wr 0x%08x\n", xbuf[i / 4]);
+ nv_wr32(aux, 0x00e4c0 + (ch * 0x50) + i, xbuf[i / 4]);
+ }
+ }
+
+ ctrl = nv_rd32(aux, 0x00e4e4 + (ch * 0x50));
+ ctrl &= ~0x0001f0ff;
+ ctrl |= type << 12;
+ ctrl |= size - 1;
+ nv_wr32(aux, 0x00e4e0 + (ch * 0x50), addr);
+
+ /* (maybe) retry transaction a number of times on failure... */
+ for (retries = 0; !ret && retries < 32; retries++) {
+ /* reset, and delay a while if this is a retry */
+ nv_wr32(aux, 0x00e4e4 + (ch * 0x50), 0x80000000 | ctrl);
+ nv_wr32(aux, 0x00e4e4 + (ch * 0x50), 0x00000000 | ctrl);
+ if (retries)
+ udelay(400);
+
+ /* transaction request, wait up to 1ms for it to complete */
+ nv_wr32(aux, 0x00e4e4 + (ch * 0x50), 0x00010000 | ctrl);
+
+ timeout = 1000;
+ do {
+ ctrl = nv_rd32(aux, 0x00e4e4 + (ch * 0x50));
+ udelay(1);
+ if (!timeout--) {
+ AUX_ERR("tx req timeout 0x%08x\n", ctrl);
+ ret = -EIO;
+ goto out;
+ }
+ } while (ctrl & 0x00010000);
+ ret = 1;
+
+ /* read status, and check if transaction completed ok */
+ stat = nv_mask(aux, 0x00e4e8 + (ch * 0x50), 0, 0);
+ if ((stat & 0x000f0000) == 0x00080000 ||
+ (stat & 0x000f0000) == 0x00020000)
+ ret = retry ? 0 : 1;
+ if ((stat & 0x00000100))
+ ret = -ETIMEDOUT;
+ if ((stat & 0x00000e00))
+ ret = -EIO;
+
+ AUX_DBG("%02d 0x%08x 0x%08x\n", retries, ctrl, stat);
+ }
+
+ if (type & 1) {
+ for (i = 0; i < 16; i += 4) {
+ xbuf[i / 4] = nv_rd32(aux, 0x00e4d0 + (ch * 0x50) + i);
+ AUX_DBG("rd 0x%08x\n", xbuf[i / 4]);
+ }
+ memcpy(data, xbuf, size);
+ }
+
+out:
+ auxch_fini(aux, ch);
+ return ret < 0 ? ret : (stat & 0x000f0000) >> 16;
+}
+
+static const struct nvkm_i2c_func
+g94_i2c_func = {
+ .drive_scl = nv50_i2c_drive_scl,
+ .drive_sda = nv50_i2c_drive_sda,
+ .sense_scl = nv50_i2c_sense_scl,
+ .sense_sda = nv50_i2c_sense_sda,
+};
+
+static int
+g94_i2c_port_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 index,
+ struct nvkm_object **pobject)
+{
+ struct dcb_i2c_entry *info = data;
+ struct nv50_i2c_port *port;
+ int ret;
+
+ ret = nvkm_i2c_port_create(parent, engine, oclass, index,
+ &nvkm_i2c_bit_algo, &g94_i2c_func, &port);
+ *pobject = nv_object(port);
+ if (ret)
+ return ret;
+
+ if (info->drive >= nv50_i2c_addr_nr)
+ return -EINVAL;
+
+ port->state = 7;
+ port->addr = nv50_i2c_addr[info->drive];
+ return 0;
+}
+
+static const struct nvkm_i2c_func
+g94_aux_func = {
+ .aux = g94_aux,
+};
+
+int
+g94_aux_port_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 index,
+ struct nvkm_object **pobject)
+{
+ struct dcb_i2c_entry *info = data;
+ struct nv50_i2c_port *port;
+ int ret;
+
+ ret = nvkm_i2c_port_create(parent, engine, oclass, index,
+ &nvkm_i2c_aux_algo, &g94_aux_func, &port);
+ *pobject = nv_object(port);
+ if (ret)
+ return ret;
+
+ port->base.aux = info->auxch;
+ port->addr = info->auxch;
+ return 0;
+}
+
+static struct nvkm_oclass
+g94_i2c_sclass[] = {
+ { .handle = NV_I2C_TYPE_DCBI2C(DCB_I2C_NVIO_BIT),
+ .ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = g94_i2c_port_ctor,
+ .dtor = _nvkm_i2c_port_dtor,
+ .init = nv50_i2c_port_init,
+ .fini = _nvkm_i2c_port_fini,
+ },
+ },
+ { .handle = NV_I2C_TYPE_DCBI2C(DCB_I2C_NVIO_AUX),
+ .ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = g94_aux_port_ctor,
+ .dtor = _nvkm_i2c_port_dtor,
+ .init = _nvkm_i2c_port_init,
+ .fini = _nvkm_i2c_port_fini,
+ },
+ },
+ {}
+};
+
+struct nvkm_oclass *
+g94_i2c_oclass = &(struct nvkm_i2c_impl) {
+ .base.handle = NV_SUBDEV(I2C, 0x94),
+ .base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = _nvkm_i2c_ctor,
+ .dtor = _nvkm_i2c_dtor,
+ .init = _nvkm_i2c_init,
+ .fini = _nvkm_i2c_fini,
+ },
+ .sclass = g94_i2c_sclass,
+ .pad_x = &nv04_i2c_pad_oclass,
+ .pad_s = &g94_i2c_pad_oclass,
+ .aux = 4,
+ .aux_stat = g94_aux_stat,
+ .aux_mask = g94_aux_mask,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gf110.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gf110.c
new file mode 100644
index 000000000000..4d4ac6638140
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gf110.c
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "nv50.h"
+
+static int
+gf110_i2c_sense_scl(struct nvkm_i2c_port *base)
+{
+ struct nv50_i2c_priv *priv = (void *)nvkm_i2c(base);
+ struct nv50_i2c_port *port = (void *)base;
+ return !!(nv_rd32(priv, port->addr) & 0x00000010);
+}
+
+static int
+gf110_i2c_sense_sda(struct nvkm_i2c_port *base)
+{
+ struct nv50_i2c_priv *priv = (void *)nvkm_i2c(base);
+ struct nv50_i2c_port *port = (void *)base;
+ return !!(nv_rd32(priv, port->addr) & 0x00000020);
+}
+
+static const struct nvkm_i2c_func
+gf110_i2c_func = {
+ .drive_scl = nv50_i2c_drive_scl,
+ .drive_sda = nv50_i2c_drive_sda,
+ .sense_scl = gf110_i2c_sense_scl,
+ .sense_sda = gf110_i2c_sense_sda,
+};
+
+int
+gf110_i2c_port_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 index,
+ struct nvkm_object **pobject)
+{
+ struct dcb_i2c_entry *info = data;
+ struct nv50_i2c_port *port;
+ int ret;
+
+ ret = nvkm_i2c_port_create(parent, engine, oclass, index,
+ &nvkm_i2c_bit_algo, &gf110_i2c_func, &port);
+ *pobject = nv_object(port);
+ if (ret)
+ return ret;
+
+ port->state = 0x00000007;
+ port->addr = 0x00d014 + (info->drive * 0x20);
+ return 0;
+}
+
+struct nvkm_oclass
+gf110_i2c_sclass[] = {
+ { .handle = NV_I2C_TYPE_DCBI2C(DCB_I2C_NVIO_BIT),
+ .ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = gf110_i2c_port_ctor,
+ .dtor = _nvkm_i2c_port_dtor,
+ .init = nv50_i2c_port_init,
+ .fini = _nvkm_i2c_port_fini,
+ },
+ },
+ { .handle = NV_I2C_TYPE_DCBI2C(DCB_I2C_NVIO_AUX),
+ .ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = g94_aux_port_ctor,
+ .dtor = _nvkm_i2c_port_dtor,
+ .init = _nvkm_i2c_port_init,
+ .fini = _nvkm_i2c_port_fini,
+ },
+ },
+ {}
+};
+
+struct nvkm_oclass *
+gf110_i2c_oclass = &(struct nvkm_i2c_impl) {
+ .base.handle = NV_SUBDEV(I2C, 0xd0),
+ .base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = _nvkm_i2c_ctor,
+ .dtor = _nvkm_i2c_dtor,
+ .init = _nvkm_i2c_init,
+ .fini = _nvkm_i2c_fini,
+ },
+ .sclass = gf110_i2c_sclass,
+ .pad_x = &nv04_i2c_pad_oclass,
+ .pad_s = &g94_i2c_pad_oclass,
+ .aux = 4,
+ .aux_stat = g94_aux_stat,
+ .aux_mask = g94_aux_mask,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gf117.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gf117.c
new file mode 100644
index 000000000000..e290b40f2d13
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gf117.c
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "nv50.h"
+
+struct nvkm_oclass *
+gf117_i2c_oclass = &(struct nvkm_i2c_impl) {
+ .base.handle = NV_SUBDEV(I2C, 0xd7),
+ .base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = _nvkm_i2c_ctor,
+ .dtor = _nvkm_i2c_dtor,
+ .init = _nvkm_i2c_init,
+ .fini = _nvkm_i2c_fini,
+ },
+ .sclass = gf110_i2c_sclass,
+ .pad_x = &nv04_i2c_pad_oclass,
+ .pad_s = &nv04_i2c_pad_oclass,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gk104.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gk104.c
new file mode 100644
index 000000000000..1a464903a992
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gk104.c
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "nv50.h"
+
+void
+gk104_aux_stat(struct nvkm_i2c *i2c, u32 *hi, u32 *lo, u32 *rq, u32 *tx)
+{
+ u32 intr = nv_rd32(i2c, 0x00dc60);
+ u32 stat = nv_rd32(i2c, 0x00dc68) & intr, i;
+ for (i = 0, *hi = *lo = *rq = *tx = 0; i < 8; i++) {
+ if ((stat & (1 << (i * 4)))) *hi |= 1 << i;
+ if ((stat & (2 << (i * 4)))) *lo |= 1 << i;
+ if ((stat & (4 << (i * 4)))) *rq |= 1 << i;
+ if ((stat & (8 << (i * 4)))) *tx |= 1 << i;
+ }
+ nv_wr32(i2c, 0x00dc60, intr);
+}
+
+void
+gk104_aux_mask(struct nvkm_i2c *i2c, u32 type, u32 mask, u32 data)
+{
+ u32 temp = nv_rd32(i2c, 0x00dc68), i;
+ for (i = 0; i < 8; i++) {
+ if (mask & (1 << i)) {
+ if (!(data & (1 << i))) {
+ temp &= ~(type << (i * 4));
+ continue;
+ }
+ temp |= type << (i * 4);
+ }
+ }
+ nv_wr32(i2c, 0x00dc68, temp);
+}
+
+struct nvkm_oclass *
+gk104_i2c_oclass = &(struct nvkm_i2c_impl) {
+ .base.handle = NV_SUBDEV(I2C, 0xe0),
+ .base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = _nvkm_i2c_ctor,
+ .dtor = _nvkm_i2c_dtor,
+ .init = _nvkm_i2c_init,
+ .fini = _nvkm_i2c_fini,
+ },
+ .sclass = gf110_i2c_sclass,
+ .pad_x = &nv04_i2c_pad_oclass,
+ .pad_s = &g94_i2c_pad_oclass,
+ .aux = 4,
+ .aux_stat = gk104_aux_stat,
+ .aux_mask = gk104_aux_mask,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gm204.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gm204.c
new file mode 100644
index 000000000000..ab64237b3842
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gm204.c
@@ -0,0 +1,219 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "nv50.h"
+
+#define AUX_DBG(fmt, args...) nv_debug(aux, "AUXCH(%d): " fmt, ch, ##args)
+#define AUX_ERR(fmt, args...) nv_error(aux, "AUXCH(%d): " fmt, ch, ##args)
+
+static void
+auxch_fini(struct nvkm_i2c *aux, int ch)
+{
+ nv_mask(aux, 0x00d954 + (ch * 0x50), 0x00310000, 0x00000000);
+}
+
+static int
+auxch_init(struct nvkm_i2c *aux, int ch)
+{
+ const u32 unksel = 1; /* nfi which to use, or if it matters.. */
+ const u32 ureq = unksel ? 0x00100000 : 0x00200000;
+ const u32 urep = unksel ? 0x01000000 : 0x02000000;
+ u32 ctrl, timeout;
+
+ /* wait up to 1ms for any previous transaction to be done... */
+ timeout = 1000;
+ do {
+ ctrl = nv_rd32(aux, 0x00d954 + (ch * 0x50));
+ udelay(1);
+ if (!timeout--) {
+ AUX_ERR("begin idle timeout 0x%08x\n", ctrl);
+ return -EBUSY;
+ }
+ } while (ctrl & 0x03010000);
+
+ /* set some magic, and wait up to 1ms for it to appear */
+ nv_mask(aux, 0x00d954 + (ch * 0x50), 0x00300000, ureq);
+ timeout = 1000;
+ do {
+ ctrl = nv_rd32(aux, 0x00d954 + (ch * 0x50));
+ udelay(1);
+ if (!timeout--) {
+ AUX_ERR("magic wait 0x%08x\n", ctrl);
+ auxch_fini(aux, ch);
+ return -EBUSY;
+ }
+ } while ((ctrl & 0x03000000) != urep);
+
+ return 0;
+}
+
+int
+gm204_aux(struct nvkm_i2c_port *base, bool retry,
+ u8 type, u32 addr, u8 *data, u8 size)
+{
+ struct nvkm_i2c *aux = nvkm_i2c(base);
+ struct nv50_i2c_port *port = (void *)base;
+ u32 ctrl, stat, timeout, retries;
+ u32 xbuf[4] = {};
+ int ch = port->addr;
+ int ret, i;
+
+ AUX_DBG("%d: 0x%08x %d\n", type, addr, size);
+
+ ret = auxch_init(aux, ch);
+ if (ret)
+ goto out;
+
+ stat = nv_rd32(aux, 0x00d958 + (ch * 0x50));
+ if (!(stat & 0x10000000)) {
+ AUX_DBG("sink not detected\n");
+ ret = -ENXIO;
+ goto out;
+ }
+
+ if (!(type & 1)) {
+ memcpy(xbuf, data, size);
+ for (i = 0; i < 16; i += 4) {
+ AUX_DBG("wr 0x%08x\n", xbuf[i / 4]);
+ nv_wr32(aux, 0x00d930 + (ch * 0x50) + i, xbuf[i / 4]);
+ }
+ }
+
+ ctrl = nv_rd32(aux, 0x00d954 + (ch * 0x50));
+ ctrl &= ~0x0001f0ff;
+ ctrl |= type << 12;
+ ctrl |= size - 1;
+ nv_wr32(aux, 0x00d950 + (ch * 0x50), addr);
+
+ /* (maybe) retry transaction a number of times on failure... */
+ for (retries = 0; !ret && retries < 32; retries++) {
+ /* reset, and delay a while if this is a retry */
+ nv_wr32(aux, 0x00d954 + (ch * 0x50), 0x80000000 | ctrl);
+ nv_wr32(aux, 0x00d954 + (ch * 0x50), 0x00000000 | ctrl);
+ if (retries)
+ udelay(400);
+
+ /* transaction request, wait up to 1ms for it to complete */
+ nv_wr32(aux, 0x00d954 + (ch * 0x50), 0x00010000 | ctrl);
+
+ timeout = 1000;
+ do {
+ ctrl = nv_rd32(aux, 0x00d954 + (ch * 0x50));
+ udelay(1);
+ if (!timeout--) {
+ AUX_ERR("tx req timeout 0x%08x\n", ctrl);
+ ret = -EIO;
+ goto out;
+ }
+ } while (ctrl & 0x00010000);
+ ret = 1;
+
+ /* read status, and check if transaction completed ok */
+ stat = nv_mask(aux, 0x00d958 + (ch * 0x50), 0, 0);
+ if ((stat & 0x000f0000) == 0x00080000 ||
+ (stat & 0x000f0000) == 0x00020000)
+ ret = retry ? 0 : 1;
+ if ((stat & 0x00000100))
+ ret = -ETIMEDOUT;
+ if ((stat & 0x00000e00))
+ ret = -EIO;
+
+ AUX_DBG("%02d 0x%08x 0x%08x\n", retries, ctrl, stat);
+ }
+
+ if (type & 1) {
+ for (i = 0; i < 16; i += 4) {
+ xbuf[i / 4] = nv_rd32(aux, 0x00d940 + (ch * 0x50) + i);
+ AUX_DBG("rd 0x%08x\n", xbuf[i / 4]);
+ }
+ memcpy(data, xbuf, size);
+ }
+
+out:
+ auxch_fini(aux, ch);
+ return ret < 0 ? ret : (stat & 0x000f0000) >> 16;
+}
+
+static const struct nvkm_i2c_func
+gm204_aux_func = {
+ .aux = gm204_aux,
+};
+
+int
+gm204_aux_port_ctor(struct nvkm_object *parent,
+ struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 index,
+ struct nvkm_object **pobject)
+{
+ struct dcb_i2c_entry *info = data;
+ struct nv50_i2c_port *port;
+ int ret;
+
+ ret = nvkm_i2c_port_create(parent, engine, oclass, index,
+ &nvkm_i2c_aux_algo, &gm204_aux_func, &port);
+ *pobject = nv_object(port);
+ if (ret)
+ return ret;
+
+ port->base.aux = info->auxch;
+ port->addr = info->auxch;
+ return 0;
+}
+
+struct nvkm_oclass
+gm204_i2c_sclass[] = {
+ { .handle = NV_I2C_TYPE_DCBI2C(DCB_I2C_NVIO_BIT),
+ .ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = gf110_i2c_port_ctor,
+ .dtor = _nvkm_i2c_port_dtor,
+ .init = nv50_i2c_port_init,
+ .fini = _nvkm_i2c_port_fini,
+ },
+ },
+ { .handle = NV_I2C_TYPE_DCBI2C(DCB_I2C_NVIO_AUX),
+ .ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = gm204_aux_port_ctor,
+ .dtor = _nvkm_i2c_port_dtor,
+ .init = _nvkm_i2c_port_init,
+ .fini = _nvkm_i2c_port_fini,
+ },
+ },
+ {}
+};
+
+struct nvkm_oclass *
+gm204_i2c_oclass = &(struct nvkm_i2c_impl) {
+ .base.handle = NV_SUBDEV(I2C, 0x24),
+ .base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = _nvkm_i2c_ctor,
+ .dtor = _nvkm_i2c_dtor,
+ .init = _nvkm_i2c_init,
+ .fini = _nvkm_i2c_fini,
+ },
+ .sclass = gm204_i2c_sclass,
+ .pad_x = &nv04_i2c_pad_oclass,
+ .pad_s = &gm204_i2c_pad_oclass,
+ .aux = 8,
+ .aux_stat = gk104_aux_stat,
+ .aux_mask = gk104_aux_mask,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/nv04.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/nv04.c
new file mode 100644
index 000000000000..4cdf1c489353
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/nv04.c
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "priv.h"
+
+#include <subdev/vga.h>
+
+struct nv04_i2c_priv {
+ struct nvkm_i2c base;
+};
+
+struct nv04_i2c_port {
+ struct nvkm_i2c_port base;
+ u8 drive;
+ u8 sense;
+};
+
+static void
+nv04_i2c_drive_scl(struct nvkm_i2c_port *base, int state)
+{
+ struct nv04_i2c_priv *priv = (void *)nvkm_i2c(base);
+ struct nv04_i2c_port *port = (void *)base;
+ u8 val = nv_rdvgac(priv, 0, port->drive);
+ if (state) val |= 0x20;
+ else val &= 0xdf;
+ nv_wrvgac(priv, 0, port->drive, val | 0x01);
+}
+
+static void
+nv04_i2c_drive_sda(struct nvkm_i2c_port *base, int state)
+{
+ struct nv04_i2c_priv *priv = (void *)nvkm_i2c(base);
+ struct nv04_i2c_port *port = (void *)base;
+ u8 val = nv_rdvgac(priv, 0, port->drive);
+ if (state) val |= 0x10;
+ else val &= 0xef;
+ nv_wrvgac(priv, 0, port->drive, val | 0x01);
+}
+
+static int
+nv04_i2c_sense_scl(struct nvkm_i2c_port *base)
+{
+ struct nv04_i2c_priv *priv = (void *)nvkm_i2c(base);
+ struct nv04_i2c_port *port = (void *)base;
+ return !!(nv_rdvgac(priv, 0, port->sense) & 0x04);
+}
+
+static int
+nv04_i2c_sense_sda(struct nvkm_i2c_port *base)
+{
+ struct nv04_i2c_priv *priv = (void *)nvkm_i2c(base);
+ struct nv04_i2c_port *port = (void *)base;
+ return !!(nv_rdvgac(priv, 0, port->sense) & 0x08);
+}
+
+static const struct nvkm_i2c_func
+nv04_i2c_func = {
+ .drive_scl = nv04_i2c_drive_scl,
+ .drive_sda = nv04_i2c_drive_sda,
+ .sense_scl = nv04_i2c_sense_scl,
+ .sense_sda = nv04_i2c_sense_sda,
+};
+
+static int
+nv04_i2c_port_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 index,
+ struct nvkm_object **pobject)
+{
+ struct dcb_i2c_entry *info = data;
+ struct nv04_i2c_port *port;
+ int ret;
+
+ ret = nvkm_i2c_port_create(parent, engine, oclass, index,
+ &nvkm_i2c_bit_algo, &nv04_i2c_func, &port);
+ *pobject = nv_object(port);
+ if (ret)
+ return ret;
+
+ port->drive = info->drive;
+ port->sense = info->sense;
+ return 0;
+}
+
+static struct nvkm_oclass
+nv04_i2c_sclass[] = {
+ { .handle = NV_I2C_TYPE_DCBI2C(DCB_I2C_NV04_BIT),
+ .ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv04_i2c_port_ctor,
+ .dtor = _nvkm_i2c_port_dtor,
+ .init = _nvkm_i2c_port_init,
+ .fini = _nvkm_i2c_port_fini,
+ },
+ },
+ {}
+};
+
+struct nvkm_oclass *
+nv04_i2c_oclass = &(struct nvkm_i2c_impl) {
+ .base.handle = NV_SUBDEV(I2C, 0x04),
+ .base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = _nvkm_i2c_ctor,
+ .dtor = _nvkm_i2c_dtor,
+ .init = _nvkm_i2c_init,
+ .fini = _nvkm_i2c_fini,
+ },
+ .sclass = nv04_i2c_sclass,
+ .pad_x = &nv04_i2c_pad_oclass,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/nv4e.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/nv4e.c
new file mode 100644
index 000000000000..046fe5e2ea19
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/nv4e.c
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "priv.h"
+
+#include <subdev/vga.h>
+
+struct nv4e_i2c_priv {
+ struct nvkm_i2c base;
+};
+
+struct nv4e_i2c_port {
+ struct nvkm_i2c_port base;
+ u32 addr;
+};
+
+static void
+nv4e_i2c_drive_scl(struct nvkm_i2c_port *base, int state)
+{
+ struct nv4e_i2c_priv *priv = (void *)nvkm_i2c(base);
+ struct nv4e_i2c_port *port = (void *)base;
+ nv_mask(priv, port->addr, 0x2f, state ? 0x21 : 0x01);
+}
+
+static void
+nv4e_i2c_drive_sda(struct nvkm_i2c_port *base, int state)
+{
+ struct nv4e_i2c_priv *priv = (void *)nvkm_i2c(base);
+ struct nv4e_i2c_port *port = (void *)base;
+ nv_mask(priv, port->addr, 0x1f, state ? 0x11 : 0x01);
+}
+
+static int
+nv4e_i2c_sense_scl(struct nvkm_i2c_port *base)
+{
+ struct nv4e_i2c_priv *priv = (void *)nvkm_i2c(base);
+ struct nv4e_i2c_port *port = (void *)base;
+ return !!(nv_rd32(priv, port->addr) & 0x00040000);
+}
+
+static int
+nv4e_i2c_sense_sda(struct nvkm_i2c_port *base)
+{
+ struct nv4e_i2c_priv *priv = (void *)nvkm_i2c(base);
+ struct nv4e_i2c_port *port = (void *)base;
+ return !!(nv_rd32(priv, port->addr) & 0x00080000);
+}
+
+static const struct nvkm_i2c_func
+nv4e_i2c_func = {
+ .drive_scl = nv4e_i2c_drive_scl,
+ .drive_sda = nv4e_i2c_drive_sda,
+ .sense_scl = nv4e_i2c_sense_scl,
+ .sense_sda = nv4e_i2c_sense_sda,
+};
+
+static int
+nv4e_i2c_port_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 index,
+ struct nvkm_object **pobject)
+{
+ struct dcb_i2c_entry *info = data;
+ struct nv4e_i2c_port *port;
+ int ret;
+
+ ret = nvkm_i2c_port_create(parent, engine, oclass, index,
+ &nvkm_i2c_bit_algo, &nv4e_i2c_func, &port);
+ *pobject = nv_object(port);
+ if (ret)
+ return ret;
+
+ port->addr = 0x600800 + info->drive;
+ return 0;
+}
+
+static struct nvkm_oclass
+nv4e_i2c_sclass[] = {
+ { .handle = NV_I2C_TYPE_DCBI2C(DCB_I2C_NV4E_BIT),
+ .ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv4e_i2c_port_ctor,
+ .dtor = _nvkm_i2c_port_dtor,
+ .init = _nvkm_i2c_port_init,
+ .fini = _nvkm_i2c_port_fini,
+ },
+ },
+ {}
+};
+
+struct nvkm_oclass *
+nv4e_i2c_oclass = &(struct nvkm_i2c_impl) {
+ .base.handle = NV_SUBDEV(I2C, 0x4e),
+ .base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = _nvkm_i2c_ctor,
+ .dtor = _nvkm_i2c_dtor,
+ .init = _nvkm_i2c_init,
+ .fini = _nvkm_i2c_fini,
+ },
+ .sclass = nv4e_i2c_sclass,
+ .pad_x = &nv04_i2c_pad_oclass,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/nv50.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/nv50.c
new file mode 100644
index 000000000000..fba5b26a5682
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/nv50.c
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "nv50.h"
+
+void
+nv50_i2c_drive_scl(struct nvkm_i2c_port *base, int state)
+{
+ struct nv50_i2c_priv *priv = (void *)nvkm_i2c(base);
+ struct nv50_i2c_port *port = (void *)base;
+ if (state) port->state |= 0x01;
+ else port->state &= 0xfe;
+ nv_wr32(priv, port->addr, port->state);
+}
+
+void
+nv50_i2c_drive_sda(struct nvkm_i2c_port *base, int state)
+{
+ struct nv50_i2c_priv *priv = (void *)nvkm_i2c(base);
+ struct nv50_i2c_port *port = (void *)base;
+ if (state) port->state |= 0x02;
+ else port->state &= 0xfd;
+ nv_wr32(priv, port->addr, port->state);
+}
+
+int
+nv50_i2c_sense_scl(struct nvkm_i2c_port *base)
+{
+ struct nv50_i2c_priv *priv = (void *)nvkm_i2c(base);
+ struct nv50_i2c_port *port = (void *)base;
+ return !!(nv_rd32(priv, port->addr) & 0x00000001);
+}
+
+int
+nv50_i2c_sense_sda(struct nvkm_i2c_port *base)
+{
+ struct nv50_i2c_priv *priv = (void *)nvkm_i2c(base);
+ struct nv50_i2c_port *port = (void *)base;
+ return !!(nv_rd32(priv, port->addr) & 0x00000002);
+}
+
+static const struct nvkm_i2c_func
+nv50_i2c_func = {
+ .drive_scl = nv50_i2c_drive_scl,
+ .drive_sda = nv50_i2c_drive_sda,
+ .sense_scl = nv50_i2c_sense_scl,
+ .sense_sda = nv50_i2c_sense_sda,
+};
+
+const u32 nv50_i2c_addr[] = {
+ 0x00e138, 0x00e150, 0x00e168, 0x00e180,
+ 0x00e254, 0x00e274, 0x00e764, 0x00e780,
+ 0x00e79c, 0x00e7b8
+};
+const int nv50_i2c_addr_nr = ARRAY_SIZE(nv50_i2c_addr);
+
+static int
+nv50_i2c_port_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 index,
+ struct nvkm_object **pobject)
+{
+ struct dcb_i2c_entry *info = data;
+ struct nv50_i2c_port *port;
+ int ret;
+
+ ret = nvkm_i2c_port_create(parent, engine, oclass, index,
+ &nvkm_i2c_bit_algo, &nv50_i2c_func, &port);
+ *pobject = nv_object(port);
+ if (ret)
+ return ret;
+
+ if (info->drive >= nv50_i2c_addr_nr)
+ return -EINVAL;
+
+ port->state = 0x00000007;
+ port->addr = nv50_i2c_addr[info->drive];
+ return 0;
+}
+
+int
+nv50_i2c_port_init(struct nvkm_object *object)
+{
+ struct nv50_i2c_priv *priv = (void *)nvkm_i2c(object);
+ struct nv50_i2c_port *port = (void *)object;
+ nv_wr32(priv, port->addr, port->state);
+ return nvkm_i2c_port_init(&port->base);
+}
+
+static struct nvkm_oclass
+nv50_i2c_sclass[] = {
+ { .handle = NV_I2C_TYPE_DCBI2C(DCB_I2C_NVIO_BIT),
+ .ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv50_i2c_port_ctor,
+ .dtor = _nvkm_i2c_port_dtor,
+ .init = nv50_i2c_port_init,
+ .fini = _nvkm_i2c_port_fini,
+ },
+ },
+ {}
+};
+
+struct nvkm_oclass *
+nv50_i2c_oclass = &(struct nvkm_i2c_impl) {
+ .base.handle = NV_SUBDEV(I2C, 0x50),
+ .base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = _nvkm_i2c_ctor,
+ .dtor = _nvkm_i2c_dtor,
+ .init = _nvkm_i2c_init,
+ .fini = _nvkm_i2c_fini,
+ },
+ .sclass = nv50_i2c_sclass,
+ .pad_x = &nv04_i2c_pad_oclass,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/nv50.h b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/nv50.h
new file mode 100644
index 000000000000..b3139e721b02
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/nv50.h
@@ -0,0 +1,32 @@
+#ifndef __NV50_I2C_H__
+#define __NV50_I2C_H__
+#include "priv.h"
+
+struct nv50_i2c_priv {
+ struct nvkm_i2c base;
+};
+
+struct nv50_i2c_port {
+ struct nvkm_i2c_port base;
+ u32 addr;
+ u32 state;
+};
+
+extern const u32 nv50_i2c_addr[];
+extern const int nv50_i2c_addr_nr;
+int nv50_i2c_port_init(struct nvkm_object *);
+int nv50_i2c_sense_scl(struct nvkm_i2c_port *);
+int nv50_i2c_sense_sda(struct nvkm_i2c_port *);
+void nv50_i2c_drive_scl(struct nvkm_i2c_port *, int state);
+void nv50_i2c_drive_sda(struct nvkm_i2c_port *, int state);
+
+int g94_aux_port_ctor(struct nvkm_object *, struct nvkm_object *,
+ struct nvkm_oclass *, void *, u32,
+ struct nvkm_object **);
+void g94_i2c_acquire(struct nvkm_i2c_port *);
+void g94_i2c_release(struct nvkm_i2c_port *);
+
+int gf110_i2c_port_ctor(struct nvkm_object *, struct nvkm_object *,
+ struct nvkm_oclass *, void *, u32,
+ struct nvkm_object **);
+#endif
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/pad.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/pad.c
new file mode 100644
index 000000000000..a242eeb67829
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/pad.c
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2014 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "pad.h"
+
+int
+_nvkm_i2c_pad_fini(struct nvkm_object *object, bool suspend)
+{
+ struct nvkm_i2c_pad *pad = (void *)object;
+ DBG("-> NULL\n");
+ pad->port = NULL;
+ return nvkm_object_fini(&pad->base, suspend);
+}
+
+int
+_nvkm_i2c_pad_init(struct nvkm_object *object)
+{
+ struct nvkm_i2c_pad *pad = (void *)object;
+ DBG("-> PORT:%02x\n", pad->next->index);
+ pad->port = pad->next;
+ return nvkm_object_init(&pad->base);
+}
+
+int
+nvkm_i2c_pad_create_(struct nvkm_object *parent,
+ struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, int index,
+ int size, void **pobject)
+{
+ struct nvkm_i2c *i2c = nvkm_i2c(parent);
+ struct nvkm_i2c_port *port;
+ struct nvkm_i2c_pad *pad;
+ int ret;
+
+ list_for_each_entry(port, &i2c->ports, head) {
+ pad = nvkm_i2c_pad(port);
+ if (pad->index == index) {
+ atomic_inc(&nv_object(pad)->refcount);
+ *pobject = pad;
+ return 1;
+ }
+ }
+
+ ret = nvkm_object_create_(parent, engine, oclass, 0, size, pobject);
+ pad = *pobject;
+ if (ret)
+ return ret;
+
+ pad->index = index;
+ return 0;
+}
+
+int
+_nvkm_i2c_pad_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 index,
+ struct nvkm_object **pobject)
+{
+ struct nvkm_i2c_pad *pad;
+ int ret;
+ ret = nvkm_i2c_pad_create(parent, engine, oclass, index, &pad);
+ *pobject = nv_object(pad);
+ return ret;
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/pad.h b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/pad.h
new file mode 100644
index 000000000000..f3422cc6f8db
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/pad.h
@@ -0,0 +1,56 @@
+#ifndef __NVKM_I2C_PAD_H__
+#define __NVKM_I2C_PAD_H__
+#include "priv.h"
+
+struct nvkm_i2c_pad {
+ struct nvkm_object base;
+ int index;
+ struct nvkm_i2c_port *port;
+ struct nvkm_i2c_port *next;
+};
+
+static inline struct nvkm_i2c_pad *
+nvkm_i2c_pad(struct nvkm_i2c_port *port)
+{
+ struct nvkm_object *pad = nv_object(port);
+ while (!nv_iclass(pad->parent, NV_SUBDEV_CLASS))
+ pad = pad->parent;
+ return (void *)pad;
+}
+
+#define nvkm_i2c_pad_create(p,e,o,i,d) \
+ nvkm_i2c_pad_create_((p), (e), (o), (i), sizeof(**d), (void **)d)
+#define nvkm_i2c_pad_destroy(p) ({ \
+ struct nvkm_i2c_pad *_p = (p); \
+ _nvkm_i2c_pad_dtor(nv_object(_p)); \
+})
+#define nvkm_i2c_pad_init(p) ({ \
+ struct nvkm_i2c_pad *_p = (p); \
+ _nvkm_i2c_pad_init(nv_object(_p)); \
+})
+#define nvkm_i2c_pad_fini(p,s) ({ \
+ struct nvkm_i2c_pad *_p = (p); \
+ _nvkm_i2c_pad_fini(nv_object(_p), (s)); \
+})
+
+int nvkm_i2c_pad_create_(struct nvkm_object *, struct nvkm_object *,
+ struct nvkm_oclass *, int index, int, void **);
+
+int _nvkm_i2c_pad_ctor(struct nvkm_object *, struct nvkm_object *,
+ struct nvkm_oclass *, void *, u32,
+ struct nvkm_object **);
+#define _nvkm_i2c_pad_dtor nvkm_object_destroy
+int _nvkm_i2c_pad_init(struct nvkm_object *);
+int _nvkm_i2c_pad_fini(struct nvkm_object *, bool);
+
+#ifndef MSG
+#define MSG(l,f,a...) do { \
+ struct nvkm_i2c_pad *_pad = (void *)pad; \
+ nv_##l(_pad, "PAD:%c:%02x: "f, \
+ _pad->index >= 0x100 ? 'X' : 'S', \
+ _pad->index >= 0x100 ? _pad->index - 0x100 : _pad->index, ##a); \
+} while(0)
+#define DBG(f,a...) MSG(debug, f, ##a)
+#define ERR(f,a...) MSG(error, f, ##a)
+#endif
+#endif
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padg94.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padg94.c
new file mode 100644
index 000000000000..e9832f7a7e38
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padg94.c
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2014 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "pad.h"
+
+struct g94_i2c_pad {
+ struct nvkm_i2c_pad base;
+ int addr;
+};
+
+static int
+g94_i2c_pad_fini(struct nvkm_object *object, bool suspend)
+{
+ struct nvkm_i2c *i2c = (void *)nvkm_i2c(object);
+ struct g94_i2c_pad *pad = (void *)object;
+ nv_mask(i2c, 0x00e50c + pad->addr, 0x00000001, 0x00000001);
+ return nvkm_i2c_pad_fini(&pad->base, suspend);
+}
+
+static int
+g94_i2c_pad_init(struct nvkm_object *object)
+{
+ struct nvkm_i2c *i2c = (void *)nvkm_i2c(object);
+ struct g94_i2c_pad *pad = (void *)object;
+
+ switch (nv_oclass(pad->base.next)->handle) {
+ case NV_I2C_TYPE_DCBI2C(DCB_I2C_NVIO_AUX):
+ nv_mask(i2c, 0x00e500 + pad->addr, 0x0000c003, 0x00000002);
+ break;
+ case NV_I2C_TYPE_DCBI2C(DCB_I2C_NVIO_BIT):
+ default:
+ nv_mask(i2c, 0x00e500 + pad->addr, 0x0000c003, 0x0000c001);
+ break;
+ }
+
+ nv_mask(i2c, 0x00e50c + pad->addr, 0x00000001, 0x00000000);
+ return nvkm_i2c_pad_init(&pad->base);
+}
+
+static int
+g94_i2c_pad_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 index,
+ struct nvkm_object **pobject)
+{
+ struct g94_i2c_pad *pad;
+ int ret;
+
+ ret = nvkm_i2c_pad_create(parent, engine, oclass, index, &pad);
+ *pobject = nv_object(pad);
+ if (ret)
+ return ret;
+
+ pad->addr = index * 0x50;;
+ return 0;
+}
+
+struct nvkm_oclass
+g94_i2c_pad_oclass = {
+ .ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = g94_i2c_pad_ctor,
+ .dtor = _nvkm_i2c_pad_dtor,
+ .init = g94_i2c_pad_init,
+ .fini = g94_i2c_pad_fini,
+ },
+};
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padgm204.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padgm204.c
new file mode 100644
index 000000000000..be590405444d
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padgm204.c
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2014 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "pad.h"
+
+struct gm204_i2c_pad {
+ struct nvkm_i2c_pad base;
+ int addr;
+};
+
+static int
+gm204_i2c_pad_fini(struct nvkm_object *object, bool suspend)
+{
+ struct nvkm_i2c *i2c = (void *)nvkm_i2c(object);
+ struct gm204_i2c_pad *pad = (void *)object;
+ nv_mask(i2c, 0x00d97c + pad->addr, 0x00000001, 0x00000001);
+ return nvkm_i2c_pad_fini(&pad->base, suspend);
+}
+
+static int
+gm204_i2c_pad_init(struct nvkm_object *object)
+{
+ struct nvkm_i2c *i2c = (void *)nvkm_i2c(object);
+ struct gm204_i2c_pad *pad = (void *)object;
+
+ switch (nv_oclass(pad->base.next)->handle) {
+ case NV_I2C_TYPE_DCBI2C(DCB_I2C_NVIO_AUX):
+ nv_mask(i2c, 0x00d970 + pad->addr, 0x0000c003, 0x00000002);
+ break;
+ case NV_I2C_TYPE_DCBI2C(DCB_I2C_NVIO_BIT):
+ default:
+ nv_mask(i2c, 0x00d970 + pad->addr, 0x0000c003, 0x0000c001);
+ break;
+ }
+
+ nv_mask(i2c, 0x00d97c + pad->addr, 0x00000001, 0x00000000);
+ return nvkm_i2c_pad_init(&pad->base);
+}
+
+static int
+gm204_i2c_pad_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 index,
+ struct nvkm_object **pobject)
+{
+ struct gm204_i2c_pad *pad;
+ int ret;
+
+ ret = nvkm_i2c_pad_create(parent, engine, oclass, index, &pad);
+ *pobject = nv_object(pad);
+ if (ret)
+ return ret;
+
+ pad->addr = index * 0x50;;
+ return 0;
+}
+
+struct nvkm_oclass
+gm204_i2c_pad_oclass = {
+ .ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = gm204_i2c_pad_ctor,
+ .dtor = _nvkm_i2c_pad_dtor,
+ .init = gm204_i2c_pad_init,
+ .fini = gm204_i2c_pad_fini,
+ },
+};
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padnv04.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padnv04.c
new file mode 100644
index 000000000000..22c7daaad3a0
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padnv04.c
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2014 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "pad.h"
+
+struct nvkm_oclass
+nv04_i2c_pad_oclass = {
+ .ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = _nvkm_i2c_pad_ctor,
+ .dtor = _nvkm_i2c_pad_dtor,
+ .init = _nvkm_i2c_pad_init,
+ .fini = _nvkm_i2c_pad_fini,
+ },
+};
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/port.h b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/port.h
new file mode 100644
index 000000000000..586f53dad813
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/port.h
@@ -0,0 +1,13 @@
+#ifndef __NVKM_I2C_PORT_H__
+#define __NVKM_I2C_PORT_H__
+#include "priv.h"
+
+#ifndef MSG
+#define MSG(l,f,a...) do { \
+ struct nvkm_i2c_port *_port = (void *)port; \
+ nv_##l(_port, "PORT:%02x: "f, _port->index, ##a); \
+} while(0)
+#define DBG(f,a...) MSG(debug, f, ##a)
+#define ERR(f,a...) MSG(error, f, ##a)
+#endif
+#endif
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/priv.h b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/priv.h
new file mode 100644
index 000000000000..6586e1567fcf
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/priv.h
@@ -0,0 +1,87 @@
+#ifndef __NVKM_I2C_PRIV_H__
+#define __NVKM_I2C_PRIV_H__
+#include <subdev/i2c.h>
+
+extern struct nvkm_oclass nv04_i2c_pad_oclass;
+extern struct nvkm_oclass g94_i2c_pad_oclass;
+extern struct nvkm_oclass gm204_i2c_pad_oclass;
+
+#define nvkm_i2c_port_create(p,e,o,i,a,f,d) \
+ nvkm_i2c_port_create_((p), (e), (o), (i), (a), (f), \
+ sizeof(**d), (void **)d)
+#define nvkm_i2c_port_destroy(p) ({ \
+ struct nvkm_i2c_port *port = (p); \
+ _nvkm_i2c_port_dtor(nv_object(i2c)); \
+})
+#define nvkm_i2c_port_init(p) \
+ nvkm_object_init(&(p)->base)
+#define nvkm_i2c_port_fini(p,s) \
+ nvkm_object_fini(&(p)->base, (s))
+
+int nvkm_i2c_port_create_(struct nvkm_object *, struct nvkm_object *,
+ struct nvkm_oclass *, u8,
+ const struct i2c_algorithm *,
+ const struct nvkm_i2c_func *,
+ int, void **);
+void _nvkm_i2c_port_dtor(struct nvkm_object *);
+#define _nvkm_i2c_port_init nvkm_object_init
+int _nvkm_i2c_port_fini(struct nvkm_object *, bool);
+
+#define nvkm_i2c_create(p,e,o,d) \
+ nvkm_i2c_create_((p), (e), (o), sizeof(**d), (void **)d)
+#define nvkm_i2c_destroy(p) ({ \
+ struct nvkm_i2c *i2c = (p); \
+ _nvkm_i2c_dtor(nv_object(i2c)); \
+})
+#define nvkm_i2c_init(p) ({ \
+ struct nvkm_i2c *i2c = (p); \
+ _nvkm_i2c_init(nv_object(i2c)); \
+})
+#define nvkm_i2c_fini(p,s) ({ \
+ struct nvkm_i2c *i2c = (p); \
+ _nvkm_i2c_fini(nv_object(i2c), (s)); \
+})
+
+int nvkm_i2c_create_(struct nvkm_object *, struct nvkm_object *,
+ struct nvkm_oclass *, int, void **);
+int _nvkm_i2c_ctor(struct nvkm_object *, struct nvkm_object *,
+ struct nvkm_oclass *, void *, u32,
+ struct nvkm_object **);
+void _nvkm_i2c_dtor(struct nvkm_object *);
+int _nvkm_i2c_init(struct nvkm_object *);
+int _nvkm_i2c_fini(struct nvkm_object *, bool);
+
+extern struct nvkm_oclass nvkm_anx9805_sclass[];
+extern struct nvkm_oclass gf110_i2c_sclass[];
+
+extern const struct i2c_algorithm nvkm_i2c_bit_algo;
+extern const struct i2c_algorithm nvkm_i2c_aux_algo;
+
+struct nvkm_i2c_impl {
+ struct nvkm_oclass base;
+
+ /* supported i2c port classes */
+ struct nvkm_oclass *sclass;
+ struct nvkm_oclass *pad_x;
+ struct nvkm_oclass *pad_s;
+
+ /* number of native dp aux channels present */
+ int aux;
+
+ /* read and ack pending interrupts, returning only data
+ * for ports that have not been masked off, while still
+ * performing the ack for anything that was pending.
+ */
+ void (*aux_stat)(struct nvkm_i2c *, u32 *, u32 *, u32 *, u32 *);
+
+ /* mask on/off interrupt types for a given set of auxch
+ */
+ void (*aux_mask)(struct nvkm_i2c *, u32, u32, u32);
+};
+
+void g94_aux_stat(struct nvkm_i2c *, u32 *, u32 *, u32 *, u32 *);
+void g94_aux_mask(struct nvkm_i2c *, u32, u32, u32);
+
+void gk104_aux_stat(struct nvkm_i2c *, u32 *, u32 *, u32 *, u32 *);
+void gk104_aux_mask(struct nvkm_i2c *, u32, u32, u32);
+#endif
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/ibus/Kbuild b/drivers/gpu/drm/nouveau/nvkm/subdev/ibus/Kbuild
new file mode 100644
index 000000000000..a0b12d27284a
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/ibus/Kbuild
@@ -0,0 +1,3 @@
+nvkm-y += nvkm/subdev/ibus/gf100.o
+nvkm-y += nvkm/subdev/ibus/gk104.o
+nvkm-y += nvkm/subdev/ibus/gk20a.o
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/ibus/gf100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/ibus/gf100.c
new file mode 100644
index 000000000000..8e578f802f66
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/ibus/gf100.c
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include <subdev/ibus.h>
+
+struct gf100_ibus_priv {
+ struct nvkm_ibus base;
+};
+
+static void
+gf100_ibus_intr_hub(struct gf100_ibus_priv *priv, int i)
+{
+ u32 addr = nv_rd32(priv, 0x122120 + (i * 0x0400));
+ u32 data = nv_rd32(priv, 0x122124 + (i * 0x0400));
+ u32 stat = nv_rd32(priv, 0x122128 + (i * 0x0400));
+ nv_error(priv, "HUB%d: 0x%06x 0x%08x (0x%08x)\n", i, addr, data, stat);
+ nv_mask(priv, 0x122128 + (i * 0x0400), 0x00000200, 0x00000000);
+}
+
+static void
+gf100_ibus_intr_rop(struct gf100_ibus_priv *priv, int i)
+{
+ u32 addr = nv_rd32(priv, 0x124120 + (i * 0x0400));
+ u32 data = nv_rd32(priv, 0x124124 + (i * 0x0400));
+ u32 stat = nv_rd32(priv, 0x124128 + (i * 0x0400));
+ nv_error(priv, "ROP%d: 0x%06x 0x%08x (0x%08x)\n", i, addr, data, stat);
+ nv_mask(priv, 0x124128 + (i * 0x0400), 0x00000200, 0x00000000);
+}
+
+static void
+gf100_ibus_intr_gpc(struct gf100_ibus_priv *priv, int i)
+{
+ u32 addr = nv_rd32(priv, 0x128120 + (i * 0x0400));
+ u32 data = nv_rd32(priv, 0x128124 + (i * 0x0400));
+ u32 stat = nv_rd32(priv, 0x128128 + (i * 0x0400));
+ nv_error(priv, "GPC%d: 0x%06x 0x%08x (0x%08x)\n", i, addr, data, stat);
+ nv_mask(priv, 0x128128 + (i * 0x0400), 0x00000200, 0x00000000);
+}
+
+static void
+gf100_ibus_intr(struct nvkm_subdev *subdev)
+{
+ struct gf100_ibus_priv *priv = (void *)subdev;
+ u32 intr0 = nv_rd32(priv, 0x121c58);
+ u32 intr1 = nv_rd32(priv, 0x121c5c);
+ u32 hubnr = nv_rd32(priv, 0x121c70);
+ u32 ropnr = nv_rd32(priv, 0x121c74);
+ u32 gpcnr = nv_rd32(priv, 0x121c78);
+ u32 i;
+
+ for (i = 0; (intr0 & 0x0000ff00) && i < hubnr; i++) {
+ u32 stat = 0x00000100 << i;
+ if (intr0 & stat) {
+ gf100_ibus_intr_hub(priv, i);
+ intr0 &= ~stat;
+ }
+ }
+
+ for (i = 0; (intr0 & 0xffff0000) && i < ropnr; i++) {
+ u32 stat = 0x00010000 << i;
+ if (intr0 & stat) {
+ gf100_ibus_intr_rop(priv, i);
+ intr0 &= ~stat;
+ }
+ }
+
+ for (i = 0; intr1 && i < gpcnr; i++) {
+ u32 stat = 0x00000001 << i;
+ if (intr1 & stat) {
+ gf100_ibus_intr_gpc(priv, i);
+ intr1 &= ~stat;
+ }
+ }
+}
+
+static int
+gf100_ibus_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 size,
+ struct nvkm_object **pobject)
+{
+ struct gf100_ibus_priv *priv;
+ int ret;
+
+ ret = nvkm_ibus_create(parent, engine, oclass, &priv);
+ *pobject = nv_object(priv);
+ if (ret)
+ return ret;
+
+ nv_subdev(priv)->intr = gf100_ibus_intr;
+ return 0;
+}
+
+struct nvkm_oclass
+gf100_ibus_oclass = {
+ .handle = NV_SUBDEV(IBUS, 0xc0),
+ .ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = gf100_ibus_ctor,
+ .dtor = _nvkm_ibus_dtor,
+ .init = _nvkm_ibus_init,
+ .fini = _nvkm_ibus_fini,
+ },
+};
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/ibus/gk104.c b/drivers/gpu/drm/nouveau/nvkm/subdev/ibus/gk104.c
new file mode 100644
index 000000000000..7b6e9a6cd7b2
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/ibus/gk104.c
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include <subdev/ibus.h>
+
+struct gk104_ibus_priv {
+ struct nvkm_ibus base;
+};
+
+static void
+gk104_ibus_intr_hub(struct gk104_ibus_priv *priv, int i)
+{
+ u32 addr = nv_rd32(priv, 0x122120 + (i * 0x0800));
+ u32 data = nv_rd32(priv, 0x122124 + (i * 0x0800));
+ u32 stat = nv_rd32(priv, 0x122128 + (i * 0x0800));
+ nv_error(priv, "HUB%d: 0x%06x 0x%08x (0x%08x)\n", i, addr, data, stat);
+ nv_mask(priv, 0x122128 + (i * 0x0800), 0x00000200, 0x00000000);
+}
+
+static void
+gk104_ibus_intr_rop(struct gk104_ibus_priv *priv, int i)
+{
+ u32 addr = nv_rd32(priv, 0x124120 + (i * 0x0800));
+ u32 data = nv_rd32(priv, 0x124124 + (i * 0x0800));
+ u32 stat = nv_rd32(priv, 0x124128 + (i * 0x0800));
+ nv_error(priv, "ROP%d: 0x%06x 0x%08x (0x%08x)\n", i, addr, data, stat);
+ nv_mask(priv, 0x124128 + (i * 0x0800), 0x00000200, 0x00000000);
+}
+
+static void
+gk104_ibus_intr_gpc(struct gk104_ibus_priv *priv, int i)
+{
+ u32 addr = nv_rd32(priv, 0x128120 + (i * 0x0800));
+ u32 data = nv_rd32(priv, 0x128124 + (i * 0x0800));
+ u32 stat = nv_rd32(priv, 0x128128 + (i * 0x0800));
+ nv_error(priv, "GPC%d: 0x%06x 0x%08x (0x%08x)\n", i, addr, data, stat);
+ nv_mask(priv, 0x128128 + (i * 0x0800), 0x00000200, 0x00000000);
+}
+
+static void
+gk104_ibus_intr(struct nvkm_subdev *subdev)
+{
+ struct gk104_ibus_priv *priv = (void *)subdev;
+ u32 intr0 = nv_rd32(priv, 0x120058);
+ u32 intr1 = nv_rd32(priv, 0x12005c);
+ u32 hubnr = nv_rd32(priv, 0x120070);
+ u32 ropnr = nv_rd32(priv, 0x120074);
+ u32 gpcnr = nv_rd32(priv, 0x120078);
+ u32 i;
+
+ for (i = 0; (intr0 & 0x0000ff00) && i < hubnr; i++) {
+ u32 stat = 0x00000100 << i;
+ if (intr0 & stat) {
+ gk104_ibus_intr_hub(priv, i);
+ intr0 &= ~stat;
+ }
+ }
+
+ for (i = 0; (intr0 & 0xffff0000) && i < ropnr; i++) {
+ u32 stat = 0x00010000 << i;
+ if (intr0 & stat) {
+ gk104_ibus_intr_rop(priv, i);
+ intr0 &= ~stat;
+ }
+ }
+
+ for (i = 0; intr1 && i < gpcnr; i++) {
+ u32 stat = 0x00000001 << i;
+ if (intr1 & stat) {
+ gk104_ibus_intr_gpc(priv, i);
+ intr1 &= ~stat;
+ }
+ }
+}
+
+static int
+gk104_ibus_init(struct nvkm_object *object)
+{
+ struct gk104_ibus_priv *priv = (void *)object;
+ int ret = nvkm_ibus_init(&priv->base);
+ if (ret == 0) {
+ nv_mask(priv, 0x122318, 0x0003ffff, 0x00001000);
+ nv_mask(priv, 0x12231c, 0x0003ffff, 0x00000200);
+ nv_mask(priv, 0x122310, 0x0003ffff, 0x00000800);
+ nv_mask(priv, 0x122348, 0x0003ffff, 0x00000100);
+ nv_mask(priv, 0x1223b0, 0x0003ffff, 0x00000fff);
+ nv_mask(priv, 0x122348, 0x0003ffff, 0x00000200);
+ nv_mask(priv, 0x122358, 0x0003ffff, 0x00002880);
+ }
+ return ret;
+}
+
+static int
+gk104_ibus_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 size,
+ struct nvkm_object **pobject)
+{
+ struct gk104_ibus_priv *priv;
+ int ret;
+
+ ret = nvkm_ibus_create(parent, engine, oclass, &priv);
+ *pobject = nv_object(priv);
+ if (ret)
+ return ret;
+
+ nv_subdev(priv)->intr = gk104_ibus_intr;
+ return 0;
+}
+
+struct nvkm_oclass
+gk104_ibus_oclass = {
+ .handle = NV_SUBDEV(IBUS, 0xe0),
+ .ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = gk104_ibus_ctor,
+ .dtor = _nvkm_ibus_dtor,
+ .init = gk104_ibus_init,
+ .fini = _nvkm_ibus_fini,
+ },
+};
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/ibus/gk20a.c b/drivers/gpu/drm/nouveau/nvkm/subdev/ibus/gk20a.c
new file mode 100644
index 000000000000..c0fdb89e74ac
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/ibus/gk20a.c
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+#include <subdev/ibus.h>
+#include <subdev/timer.h>
+
+struct gk20a_ibus_priv {
+ struct nvkm_ibus base;
+};
+
+static void
+gk20a_ibus_init_priv_ring(struct gk20a_ibus_priv *priv)
+{
+ nv_mask(priv, 0x137250, 0x3f, 0);
+
+ nv_mask(priv, 0x000200, 0x20, 0);
+ usleep_range(20, 30);
+ nv_mask(priv, 0x000200, 0x20, 0x20);
+
+ nv_wr32(priv, 0x12004c, 0x4);
+ nv_wr32(priv, 0x122204, 0x2);
+ nv_rd32(priv, 0x122204);
+}
+
+static void
+gk20a_ibus_intr(struct nvkm_subdev *subdev)
+{
+ struct gk20a_ibus_priv *priv = (void *)subdev;
+ u32 status0 = nv_rd32(priv, 0x120058);
+
+ if (status0 & 0x7) {
+ nv_debug(priv, "resetting priv ring\n");
+ gk20a_ibus_init_priv_ring(priv);
+ }
+
+ /* Acknowledge interrupt */
+ nv_mask(priv, 0x12004c, 0x2, 0x2);
+
+ if (!nv_wait(subdev, 0x12004c, 0x3f, 0x00))
+ nv_warn(priv, "timeout waiting for ringmaster ack\n");
+}
+
+static int
+gk20a_ibus_init(struct nvkm_object *object)
+{
+ struct gk20a_ibus_priv *priv = (void *)object;
+ int ret;
+
+ ret = _nvkm_ibus_init(object);
+ if (ret)
+ return ret;
+
+ gk20a_ibus_init_priv_ring(priv);
+
+ return 0;
+}
+
+static int
+gk20a_ibus_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 size,
+ struct nvkm_object **pobject)
+{
+ struct gk20a_ibus_priv *priv;
+ int ret;
+
+ ret = nvkm_ibus_create(parent, engine, oclass, &priv);
+ *pobject = nv_object(priv);
+ if (ret)
+ return ret;
+
+ nv_subdev(priv)->intr = gk20a_ibus_intr;
+ return 0;
+}
+
+struct nvkm_oclass
+gk20a_ibus_oclass = {
+ .handle = NV_SUBDEV(IBUS, 0xea),
+ .ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = gk20a_ibus_ctor,
+ .dtor = _nvkm_ibus_dtor,
+ .init = gk20a_ibus_init,
+ .fini = _nvkm_ibus_fini,
+ },
+};
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/Kbuild b/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/Kbuild
new file mode 100644
index 000000000000..e6f35abe7879
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/Kbuild
@@ -0,0 +1,4 @@
+nvkm-y += nvkm/subdev/instmem/base.o
+nvkm-y += nvkm/subdev/instmem/nv04.o
+nvkm-y += nvkm/subdev/instmem/nv40.o
+nvkm-y += nvkm/subdev/instmem/nv50.o
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/base.c
new file mode 100644
index 000000000000..d16358cc6cbb
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/base.c
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "priv.h"
+
+#include <core/engine.h>
+
+/******************************************************************************
+ * instmem object base implementation
+ *****************************************************************************/
+
+void
+_nvkm_instobj_dtor(struct nvkm_object *object)
+{
+ struct nvkm_instmem *imem = nvkm_instmem(object);
+ struct nvkm_instobj *iobj = (void *)object;
+
+ mutex_lock(&nv_subdev(imem)->mutex);
+ list_del(&iobj->head);
+ mutex_unlock(&nv_subdev(imem)->mutex);
+
+ return nvkm_object_destroy(&iobj->base);
+}
+
+int
+nvkm_instobj_create_(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, int length, void **pobject)
+{
+ struct nvkm_instmem *imem = nvkm_instmem(parent);
+ struct nvkm_instobj *iobj;
+ int ret;
+
+ ret = nvkm_object_create_(parent, engine, oclass, NV_MEMOBJ_CLASS,
+ length, pobject);
+ iobj = *pobject;
+ if (ret)
+ return ret;
+
+ mutex_lock(&imem->base.mutex);
+ list_add(&iobj->head, &imem->list);
+ mutex_unlock(&imem->base.mutex);
+ return 0;
+}
+
+/******************************************************************************
+ * instmem subdev base implementation
+ *****************************************************************************/
+
+static int
+nvkm_instmem_alloc(struct nvkm_instmem *imem, struct nvkm_object *parent,
+ u32 size, u32 align, struct nvkm_object **pobject)
+{
+ struct nvkm_instmem_impl *impl = (void *)imem->base.object.oclass;
+ struct nvkm_instobj_args args = { .size = size, .align = align };
+ return nvkm_object_ctor(parent, &parent->engine->subdev.object,
+ impl->instobj, &args, sizeof(args), pobject);
+}
+
+int
+_nvkm_instmem_fini(struct nvkm_object *object, bool suspend)
+{
+ struct nvkm_instmem *imem = (void *)object;
+ struct nvkm_instobj *iobj;
+ int i, ret = 0;
+
+ if (suspend) {
+ mutex_lock(&imem->base.mutex);
+ list_for_each_entry(iobj, &imem->list, head) {
+ iobj->suspend = vmalloc(iobj->size);
+ if (!iobj->suspend) {
+ ret = -ENOMEM;
+ break;
+ }
+
+ for (i = 0; i < iobj->size; i += 4)
+ iobj->suspend[i / 4] = nv_ro32(iobj, i);
+ }
+ mutex_unlock(&imem->base.mutex);
+ if (ret)
+ return ret;
+ }
+
+ return nvkm_subdev_fini(&imem->base, suspend);
+}
+
+int
+_nvkm_instmem_init(struct nvkm_object *object)
+{
+ struct nvkm_instmem *imem = (void *)object;
+ struct nvkm_instobj *iobj;
+ int ret, i;
+
+ ret = nvkm_subdev_init(&imem->base);
+ if (ret)
+ return ret;
+
+ mutex_lock(&imem->base.mutex);
+ list_for_each_entry(iobj, &imem->list, head) {
+ if (iobj->suspend) {
+ for (i = 0; i < iobj->size; i += 4)
+ nv_wo32(iobj, i, iobj->suspend[i / 4]);
+ vfree(iobj->suspend);
+ iobj->suspend = NULL;
+ }
+ }
+ mutex_unlock(&imem->base.mutex);
+ return 0;
+}
+
+int
+nvkm_instmem_create_(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, int length, void **pobject)
+{
+ struct nvkm_instmem *imem;
+ int ret;
+
+ ret = nvkm_subdev_create_(parent, engine, oclass, 0, "INSTMEM",
+ "instmem", length, pobject);
+ imem = *pobject;
+ if (ret)
+ return ret;
+
+ INIT_LIST_HEAD(&imem->list);
+ imem->alloc = nvkm_instmem_alloc;
+ return 0;
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv04.c b/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv04.c
new file mode 100644
index 000000000000..80614f1b2074
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv04.c
@@ -0,0 +1,185 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "nv04.h"
+
+#include <core/ramht.h>
+
+/******************************************************************************
+ * instmem object implementation
+ *****************************************************************************/
+
+static u32
+nv04_instobj_rd32(struct nvkm_object *object, u64 addr)
+{
+ struct nv04_instmem_priv *priv = (void *)nvkm_instmem(object);
+ struct nv04_instobj_priv *node = (void *)object;
+ return nv_ro32(priv, node->mem->offset + addr);
+}
+
+static void
+nv04_instobj_wr32(struct nvkm_object *object, u64 addr, u32 data)
+{
+ struct nv04_instmem_priv *priv = (void *)nvkm_instmem(object);
+ struct nv04_instobj_priv *node = (void *)object;
+ nv_wo32(priv, node->mem->offset + addr, data);
+}
+
+static void
+nv04_instobj_dtor(struct nvkm_object *object)
+{
+ struct nv04_instmem_priv *priv = (void *)nvkm_instmem(object);
+ struct nv04_instobj_priv *node = (void *)object;
+ nvkm_mm_free(&priv->heap, &node->mem);
+ nvkm_instobj_destroy(&node->base);
+}
+
+static int
+nv04_instobj_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 size,
+ struct nvkm_object **pobject)
+{
+ struct nv04_instmem_priv *priv = (void *)nvkm_instmem(parent);
+ struct nv04_instobj_priv *node;
+ struct nvkm_instobj_args *args = data;
+ int ret;
+
+ if (!args->align)
+ args->align = 1;
+
+ ret = nvkm_instobj_create(parent, engine, oclass, &node);
+ *pobject = nv_object(node);
+ if (ret)
+ return ret;
+
+ ret = nvkm_mm_head(&priv->heap, 0, 1, args->size, args->size,
+ args->align, &node->mem);
+ if (ret)
+ return ret;
+
+ node->base.addr = node->mem->offset;
+ node->base.size = node->mem->length;
+ return 0;
+}
+
+struct nvkm_instobj_impl
+nv04_instobj_oclass = {
+ .base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv04_instobj_ctor,
+ .dtor = nv04_instobj_dtor,
+ .init = _nvkm_instobj_init,
+ .fini = _nvkm_instobj_fini,
+ .rd32 = nv04_instobj_rd32,
+ .wr32 = nv04_instobj_wr32,
+ },
+};
+
+/******************************************************************************
+ * instmem subdev implementation
+ *****************************************************************************/
+
+static u32
+nv04_instmem_rd32(struct nvkm_object *object, u64 addr)
+{
+ return nv_rd32(object, 0x700000 + addr);
+}
+
+static void
+nv04_instmem_wr32(struct nvkm_object *object, u64 addr, u32 data)
+{
+ return nv_wr32(object, 0x700000 + addr, data);
+}
+
+void
+nv04_instmem_dtor(struct nvkm_object *object)
+{
+ struct nv04_instmem_priv *priv = (void *)object;
+ nvkm_gpuobj_ref(NULL, &priv->ramfc);
+ nvkm_gpuobj_ref(NULL, &priv->ramro);
+ nvkm_ramht_ref(NULL, &priv->ramht);
+ nvkm_gpuobj_ref(NULL, &priv->vbios);
+ nvkm_mm_fini(&priv->heap);
+ if (priv->iomem)
+ iounmap(priv->iomem);
+ nvkm_instmem_destroy(&priv->base);
+}
+
+static int
+nv04_instmem_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 size,
+ struct nvkm_object **pobject)
+{
+ struct nv04_instmem_priv *priv;
+ int ret;
+
+ ret = nvkm_instmem_create(parent, engine, oclass, &priv);
+ *pobject = nv_object(priv);
+ if (ret)
+ return ret;
+
+ /* PRAMIN aperture maps over the end of VRAM, reserve it */
+ priv->base.reserved = 512 * 1024;
+
+ ret = nvkm_mm_init(&priv->heap, 0, priv->base.reserved, 1);
+ if (ret)
+ return ret;
+
+ /* 0x00000-0x10000: reserve for probable vbios image */
+ ret = nvkm_gpuobj_new(nv_object(priv), NULL, 0x10000, 0, 0,
+ &priv->vbios);
+ if (ret)
+ return ret;
+
+ /* 0x10000-0x18000: reserve for RAMHT */
+ ret = nvkm_ramht_new(nv_object(priv), NULL, 0x08000, 0, &priv->ramht);
+ if (ret)
+ return ret;
+
+ /* 0x18000-0x18800: reserve for RAMFC (enough for 32 nv30 channels) */
+ ret = nvkm_gpuobj_new(nv_object(priv), NULL, 0x00800, 0,
+ NVOBJ_FLAG_ZERO_ALLOC, &priv->ramfc);
+ if (ret)
+ return ret;
+
+ /* 0x18800-0x18a00: reserve for RAMRO */
+ ret = nvkm_gpuobj_new(nv_object(priv), NULL, 0x00200, 0, 0,
+ &priv->ramro);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+struct nvkm_oclass *
+nv04_instmem_oclass = &(struct nvkm_instmem_impl) {
+ .base.handle = NV_SUBDEV(INSTMEM, 0x04),
+ .base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv04_instmem_ctor,
+ .dtor = nv04_instmem_dtor,
+ .init = _nvkm_instmem_init,
+ .fini = _nvkm_instmem_fini,
+ .rd32 = nv04_instmem_rd32,
+ .wr32 = nv04_instmem_wr32,
+ },
+ .instobj = &nv04_instobj_oclass.base,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv04.h b/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv04.h
new file mode 100644
index 000000000000..42b6c928047c
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv04.h
@@ -0,0 +1,36 @@
+#ifndef __NV04_INSTMEM_H__
+#define __NV04_INSTMEM_H__
+#include "priv.h"
+
+#include <core/mm.h>
+
+extern struct nvkm_instobj_impl nv04_instobj_oclass;
+
+struct nv04_instmem_priv {
+ struct nvkm_instmem base;
+
+ void __iomem *iomem;
+ struct nvkm_mm heap;
+
+ struct nvkm_gpuobj *vbios;
+ struct nvkm_ramht *ramht;
+ struct nvkm_gpuobj *ramro;
+ struct nvkm_gpuobj *ramfc;
+};
+
+static inline struct nv04_instmem_priv *
+nv04_instmem(void *obj)
+{
+ return (void *)nvkm_instmem(obj);
+}
+
+struct nv04_instobj_priv {
+ struct nvkm_instobj base;
+ struct nvkm_mm_node *mem;
+};
+
+void nv04_instmem_dtor(struct nvkm_object *);
+
+int nv04_instmem_alloc(struct nvkm_instmem *, struct nvkm_object *,
+ u32 size, u32 align, struct nvkm_object **pobject);
+#endif
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv40.c b/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv40.c
new file mode 100644
index 000000000000..b42b8588fc0e
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv40.c
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "nv04.h"
+
+#include <core/ramht.h>
+#include <engine/gr/nv40.h>
+
+/******************************************************************************
+ * instmem subdev implementation
+ *****************************************************************************/
+
+static u32
+nv40_instmem_rd32(struct nvkm_object *object, u64 addr)
+{
+ struct nv04_instmem_priv *priv = (void *)object;
+ return ioread32_native(priv->iomem + addr);
+}
+
+static void
+nv40_instmem_wr32(struct nvkm_object *object, u64 addr, u32 data)
+{
+ struct nv04_instmem_priv *priv = (void *)object;
+ iowrite32_native(data, priv->iomem + addr);
+}
+
+static int
+nv40_instmem_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 size,
+ struct nvkm_object **pobject)
+{
+ struct nvkm_device *device = nv_device(parent);
+ struct nv04_instmem_priv *priv;
+ int ret, bar, vs;
+
+ ret = nvkm_instmem_create(parent, engine, oclass, &priv);
+ *pobject = nv_object(priv);
+ if (ret)
+ return ret;
+
+ /* map bar */
+ if (nv_device_resource_len(device, 2))
+ bar = 2;
+ else
+ bar = 3;
+
+ priv->iomem = ioremap(nv_device_resource_start(device, bar),
+ nv_device_resource_len(device, bar));
+ if (!priv->iomem) {
+ nv_error(priv, "unable to map PRAMIN BAR\n");
+ return -EFAULT;
+ }
+
+ /* PRAMIN aperture maps over the end of vram, reserve enough space
+ * to fit graphics contexts for every channel, the magics come
+ * from engine/gr/nv40.c
+ */
+ vs = hweight8((nv_rd32(priv, 0x001540) & 0x0000ff00) >> 8);
+ if (device->chipset == 0x40) priv->base.reserved = 0x6aa0 * vs;
+ else if (device->chipset < 0x43) priv->base.reserved = 0x4f00 * vs;
+ else if (nv44_gr_class(priv)) priv->base.reserved = 0x4980 * vs;
+ else priv->base.reserved = 0x4a40 * vs;
+ priv->base.reserved += 16 * 1024;
+ priv->base.reserved *= 32; /* per-channel */
+ priv->base.reserved += 512 * 1024; /* pci(e)gart table */
+ priv->base.reserved += 512 * 1024; /* object storage */
+
+ priv->base.reserved = round_up(priv->base.reserved, 4096);
+
+ ret = nvkm_mm_init(&priv->heap, 0, priv->base.reserved, 1);
+ if (ret)
+ return ret;
+
+ /* 0x00000-0x10000: reserve for probable vbios image */
+ ret = nvkm_gpuobj_new(nv_object(priv), NULL, 0x10000, 0, 0,
+ &priv->vbios);
+ if (ret)
+ return ret;
+
+ /* 0x10000-0x18000: reserve for RAMHT */
+ ret = nvkm_ramht_new(nv_object(priv), NULL, 0x08000, 0, &priv->ramht);
+ if (ret)
+ return ret;
+
+ /* 0x18000-0x18200: reserve for RAMRO
+ * 0x18200-0x20000: padding
+ */
+ ret = nvkm_gpuobj_new(nv_object(priv), NULL, 0x08000, 0, 0,
+ &priv->ramro);
+ if (ret)
+ return ret;
+
+ /* 0x20000-0x21000: reserve for RAMFC
+ * 0x21000-0x40000: padding and some unknown crap
+ */
+ ret = nvkm_gpuobj_new(nv_object(priv), NULL, 0x20000, 0,
+ NVOBJ_FLAG_ZERO_ALLOC, &priv->ramfc);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+struct nvkm_oclass *
+nv40_instmem_oclass = &(struct nvkm_instmem_impl) {
+ .base.handle = NV_SUBDEV(INSTMEM, 0x40),
+ .base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv40_instmem_ctor,
+ .dtor = nv04_instmem_dtor,
+ .init = _nvkm_instmem_init,
+ .fini = _nvkm_instmem_fini,
+ .rd32 = nv40_instmem_rd32,
+ .wr32 = nv40_instmem_wr32,
+ },
+ .instobj = &nv04_instobj_oclass.base,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv50.c b/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv50.c
new file mode 100644
index 000000000000..8404143f93ee
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv50.c
@@ -0,0 +1,169 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "priv.h"
+
+#include <subdev/fb.h>
+
+struct nv50_instmem_priv {
+ struct nvkm_instmem base;
+ spinlock_t lock;
+ u64 addr;
+};
+
+struct nv50_instobj_priv {
+ struct nvkm_instobj base;
+ struct nvkm_mem *mem;
+};
+
+/******************************************************************************
+ * instmem object implementation
+ *****************************************************************************/
+
+static u32
+nv50_instobj_rd32(struct nvkm_object *object, u64 offset)
+{
+ struct nv50_instmem_priv *priv = (void *)nvkm_instmem(object);
+ struct nv50_instobj_priv *node = (void *)object;
+ unsigned long flags;
+ u64 base = (node->mem->offset + offset) & 0xffffff00000ULL;
+ u64 addr = (node->mem->offset + offset) & 0x000000fffffULL;
+ u32 data;
+
+ spin_lock_irqsave(&priv->lock, flags);
+ if (unlikely(priv->addr != base)) {
+ nv_wr32(priv, 0x001700, base >> 16);
+ priv->addr = base;
+ }
+ data = nv_rd32(priv, 0x700000 + addr);
+ spin_unlock_irqrestore(&priv->lock, flags);
+ return data;
+}
+
+static void
+nv50_instobj_wr32(struct nvkm_object *object, u64 offset, u32 data)
+{
+ struct nv50_instmem_priv *priv = (void *)nvkm_instmem(object);
+ struct nv50_instobj_priv *node = (void *)object;
+ unsigned long flags;
+ u64 base = (node->mem->offset + offset) & 0xffffff00000ULL;
+ u64 addr = (node->mem->offset + offset) & 0x000000fffffULL;
+
+ spin_lock_irqsave(&priv->lock, flags);
+ if (unlikely(priv->addr != base)) {
+ nv_wr32(priv, 0x001700, base >> 16);
+ priv->addr = base;
+ }
+ nv_wr32(priv, 0x700000 + addr, data);
+ spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+static void
+nv50_instobj_dtor(struct nvkm_object *object)
+{
+ struct nv50_instobj_priv *node = (void *)object;
+ struct nvkm_fb *pfb = nvkm_fb(object);
+ pfb->ram->put(pfb, &node->mem);
+ nvkm_instobj_destroy(&node->base);
+}
+
+static int
+nv50_instobj_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 size,
+ struct nvkm_object **pobject)
+{
+ struct nvkm_fb *pfb = nvkm_fb(parent);
+ struct nvkm_instobj_args *args = data;
+ struct nv50_instobj_priv *node;
+ int ret;
+
+ args->size = max((args->size + 4095) & ~4095, (u32)4096);
+ args->align = max((args->align + 4095) & ~4095, (u32)4096);
+
+ ret = nvkm_instobj_create(parent, engine, oclass, &node);
+ *pobject = nv_object(node);
+ if (ret)
+ return ret;
+
+ ret = pfb->ram->get(pfb, args->size, args->align, 0, 0x800, &node->mem);
+ if (ret)
+ return ret;
+
+ node->base.addr = node->mem->offset;
+ node->base.size = node->mem->size << 12;
+ node->mem->page_shift = 12;
+ return 0;
+}
+
+static struct nvkm_instobj_impl
+nv50_instobj_oclass = {
+ .base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv50_instobj_ctor,
+ .dtor = nv50_instobj_dtor,
+ .init = _nvkm_instobj_init,
+ .fini = _nvkm_instobj_fini,
+ .rd32 = nv50_instobj_rd32,
+ .wr32 = nv50_instobj_wr32,
+ },
+};
+
+/******************************************************************************
+ * instmem subdev implementation
+ *****************************************************************************/
+
+static int
+nv50_instmem_fini(struct nvkm_object *object, bool suspend)
+{
+ struct nv50_instmem_priv *priv = (void *)object;
+ priv->addr = ~0ULL;
+ return nvkm_instmem_fini(&priv->base, suspend);
+}
+
+static int
+nv50_instmem_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 size,
+ struct nvkm_object **pobject)
+{
+ struct nv50_instmem_priv *priv;
+ int ret;
+
+ ret = nvkm_instmem_create(parent, engine, oclass, &priv);
+ *pobject = nv_object(priv);
+ if (ret)
+ return ret;
+
+ spin_lock_init(&priv->lock);
+ return 0;
+}
+
+struct nvkm_oclass *
+nv50_instmem_oclass = &(struct nvkm_instmem_impl) {
+ .base.handle = NV_SUBDEV(INSTMEM, 0x50),
+ .base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv50_instmem_ctor,
+ .dtor = _nvkm_instmem_dtor,
+ .init = _nvkm_instmem_init,
+ .fini = nv50_instmem_fini,
+ },
+ .instobj = &nv50_instobj_oclass.base,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/priv.h b/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/priv.h
new file mode 100644
index 000000000000..b10e292e5607
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/priv.h
@@ -0,0 +1,54 @@
+#ifndef __NVKM_INSTMEM_PRIV_H__
+#define __NVKM_INSTMEM_PRIV_H__
+#include <subdev/instmem.h>
+
+struct nvkm_instobj_impl {
+ struct nvkm_oclass base;
+};
+
+struct nvkm_instobj_args {
+ u32 size;
+ u32 align;
+};
+
+#define nvkm_instobj_create(p,e,o,d) \
+ nvkm_instobj_create_((p), (e), (o), sizeof(**d), (void **)d)
+#define nvkm_instobj_destroy(p) ({ \
+ struct nvkm_instobj *iobj = (p); \
+ _nvkm_instobj_dtor(nv_object(iobj)); \
+})
+#define nvkm_instobj_init(p) \
+ nvkm_object_init(&(p)->base)
+#define nvkm_instobj_fini(p,s) \
+ nvkm_object_fini(&(p)->base, (s))
+
+int nvkm_instobj_create_(struct nvkm_object *, struct nvkm_object *,
+ struct nvkm_oclass *, int, void **);
+void _nvkm_instobj_dtor(struct nvkm_object *);
+#define _nvkm_instobj_init nvkm_object_init
+#define _nvkm_instobj_fini nvkm_object_fini
+
+struct nvkm_instmem_impl {
+ struct nvkm_oclass base;
+ struct nvkm_oclass *instobj;
+};
+
+#define nvkm_instmem_create(p,e,o,d) \
+ nvkm_instmem_create_((p), (e), (o), sizeof(**d), (void **)d)
+#define nvkm_instmem_destroy(p) \
+ nvkm_subdev_destroy(&(p)->base)
+#define nvkm_instmem_init(p) ({ \
+ struct nvkm_instmem *imem = (p); \
+ _nvkm_instmem_init(nv_object(imem)); \
+})
+#define nvkm_instmem_fini(p,s) ({ \
+ struct nvkm_instmem *imem = (p); \
+ _nvkm_instmem_fini(nv_object(imem), (s)); \
+})
+
+int nvkm_instmem_create_(struct nvkm_object *, struct nvkm_object *,
+ struct nvkm_oclass *, int, void **);
+#define _nvkm_instmem_dtor _nvkm_subdev_dtor
+int _nvkm_instmem_init(struct nvkm_object *);
+int _nvkm_instmem_fini(struct nvkm_object *, bool);
+#endif
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/Kbuild b/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/Kbuild
new file mode 100644
index 000000000000..e5df3d865f0c
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/Kbuild
@@ -0,0 +1,4 @@
+nvkm-y += nvkm/subdev/ltc/base.o
+nvkm-y += nvkm/subdev/ltc/gf100.o
+nvkm-y += nvkm/subdev/ltc/gk104.o
+nvkm-y += nvkm/subdev/ltc/gm107.o
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/base.c
new file mode 100644
index 000000000000..2fb87fbfd11c
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/base.c
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2014 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs <bskeggs@redhat.com>
+ */
+#include "priv.h"
+
+static int
+nvkm_ltc_tags_alloc(struct nvkm_ltc *ltc, u32 n, struct nvkm_mm_node **pnode)
+{
+ struct nvkm_ltc_priv *priv = (void *)ltc;
+ int ret;
+
+ ret = nvkm_mm_head(&priv->tags, 0, 1, n, n, 1, pnode);
+ if (ret)
+ *pnode = NULL;
+
+ return ret;
+}
+
+static void
+nvkm_ltc_tags_free(struct nvkm_ltc *ltc, struct nvkm_mm_node **pnode)
+{
+ struct nvkm_ltc_priv *priv = (void *)ltc;
+ nvkm_mm_free(&priv->tags, pnode);
+}
+
+static void
+nvkm_ltc_tags_clear(struct nvkm_ltc *ltc, u32 first, u32 count)
+{
+ const struct nvkm_ltc_impl *impl = (void *)nv_oclass(ltc);
+ struct nvkm_ltc_priv *priv = (void *)ltc;
+ const u32 limit = first + count - 1;
+
+ BUG_ON((first > limit) || (limit >= priv->num_tags));
+
+ impl->cbc_clear(priv, first, limit);
+ impl->cbc_wait(priv);
+}
+
+static int
+nvkm_ltc_zbc_color_get(struct nvkm_ltc *ltc, int index, const u32 color[4])
+{
+ const struct nvkm_ltc_impl *impl = (void *)nv_oclass(ltc);
+ struct nvkm_ltc_priv *priv = (void *)ltc;
+ memcpy(priv->zbc_color[index], color, sizeof(priv->zbc_color[index]));
+ impl->zbc_clear_color(priv, index, color);
+ return index;
+}
+
+static int
+nvkm_ltc_zbc_depth_get(struct nvkm_ltc *ltc, int index, const u32 depth)
+{
+ const struct nvkm_ltc_impl *impl = (void *)nv_oclass(ltc);
+ struct nvkm_ltc_priv *priv = (void *)ltc;
+ priv->zbc_depth[index] = depth;
+ impl->zbc_clear_depth(priv, index, depth);
+ return index;
+}
+
+int
+_nvkm_ltc_init(struct nvkm_object *object)
+{
+ const struct nvkm_ltc_impl *impl = (void *)nv_oclass(object);
+ struct nvkm_ltc_priv *priv = (void *)object;
+ int ret, i;
+
+ ret = nvkm_subdev_init(&priv->base.base);
+ if (ret)
+ return ret;
+
+ for (i = priv->base.zbc_min; i <= priv->base.zbc_max; i++) {
+ impl->zbc_clear_color(priv, i, priv->zbc_color[i]);
+ impl->zbc_clear_depth(priv, i, priv->zbc_depth[i]);
+ }
+
+ return 0;
+}
+
+int
+nvkm_ltc_create_(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, int length, void **pobject)
+{
+ const struct nvkm_ltc_impl *impl = (void *)oclass;
+ struct nvkm_ltc_priv *priv;
+ int ret;
+
+ ret = nvkm_subdev_create_(parent, engine, oclass, 0, "PLTCG",
+ "l2c", length, pobject);
+ priv = *pobject;
+ if (ret)
+ return ret;
+
+ memset(priv->zbc_color, 0x00, sizeof(priv->zbc_color));
+ memset(priv->zbc_depth, 0x00, sizeof(priv->zbc_depth));
+
+ priv->base.base.intr = impl->intr;
+ priv->base.tags_alloc = nvkm_ltc_tags_alloc;
+ priv->base.tags_free = nvkm_ltc_tags_free;
+ priv->base.tags_clear = nvkm_ltc_tags_clear;
+ priv->base.zbc_min = 1; /* reserve 0 for disabled */
+ priv->base.zbc_max = min(impl->zbc, NVKM_LTC_MAX_ZBC_CNT) - 1;
+ priv->base.zbc_color_get = nvkm_ltc_zbc_color_get;
+ priv->base.zbc_depth_get = nvkm_ltc_zbc_depth_get;
+ return 0;
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/gf100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/gf100.c
new file mode 100644
index 000000000000..8e7cc6200d60
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/gf100.c
@@ -0,0 +1,236 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "priv.h"
+
+#include <core/enum.h>
+#include <subdev/fb.h>
+#include <subdev/timer.h>
+
+void
+gf100_ltc_cbc_clear(struct nvkm_ltc_priv *priv, u32 start, u32 limit)
+{
+ nv_wr32(priv, 0x17e8cc, start);
+ nv_wr32(priv, 0x17e8d0, limit);
+ nv_wr32(priv, 0x17e8c8, 0x00000004);
+}
+
+void
+gf100_ltc_cbc_wait(struct nvkm_ltc_priv *priv)
+{
+ int c, s;
+ for (c = 0; c < priv->ltc_nr; c++) {
+ for (s = 0; s < priv->lts_nr; s++)
+ nv_wait(priv, 0x1410c8 + c * 0x2000 + s * 0x400, ~0, 0);
+ }
+}
+
+void
+gf100_ltc_zbc_clear_color(struct nvkm_ltc_priv *priv, int i, const u32 color[4])
+{
+ nv_mask(priv, 0x17ea44, 0x0000000f, i);
+ nv_wr32(priv, 0x17ea48, color[0]);
+ nv_wr32(priv, 0x17ea4c, color[1]);
+ nv_wr32(priv, 0x17ea50, color[2]);
+ nv_wr32(priv, 0x17ea54, color[3]);
+}
+
+void
+gf100_ltc_zbc_clear_depth(struct nvkm_ltc_priv *priv, int i, const u32 depth)
+{
+ nv_mask(priv, 0x17ea44, 0x0000000f, i);
+ nv_wr32(priv, 0x17ea58, depth);
+}
+
+static const struct nvkm_bitfield
+gf100_ltc_lts_intr_name[] = {
+ { 0x00000001, "IDLE_ERROR_IQ" },
+ { 0x00000002, "IDLE_ERROR_CBC" },
+ { 0x00000004, "IDLE_ERROR_TSTG" },
+ { 0x00000008, "IDLE_ERROR_DSTG" },
+ { 0x00000010, "EVICTED_CB" },
+ { 0x00000020, "ILLEGAL_COMPSTAT" },
+ { 0x00000040, "BLOCKLINEAR_CB" },
+ { 0x00000100, "ECC_SEC_ERROR" },
+ { 0x00000200, "ECC_DED_ERROR" },
+ { 0x00000400, "DEBUG" },
+ { 0x00000800, "ATOMIC_TO_Z" },
+ { 0x00001000, "ILLEGAL_ATOMIC" },
+ { 0x00002000, "BLKACTIVITY_ERR" },
+ {}
+};
+
+static void
+gf100_ltc_lts_intr(struct nvkm_ltc_priv *priv, int ltc, int lts)
+{
+ u32 base = 0x141000 + (ltc * 0x2000) + (lts * 0x400);
+ u32 intr = nv_rd32(priv, base + 0x020);
+ u32 stat = intr & 0x0000ffff;
+
+ if (stat) {
+ nv_info(priv, "LTC%d_LTS%d:", ltc, lts);
+ nvkm_bitfield_print(gf100_ltc_lts_intr_name, stat);
+ pr_cont("\n");
+ }
+
+ nv_wr32(priv, base + 0x020, intr);
+}
+
+void
+gf100_ltc_intr(struct nvkm_subdev *subdev)
+{
+ struct nvkm_ltc_priv *priv = (void *)subdev;
+ u32 mask;
+
+ mask = nv_rd32(priv, 0x00017c);
+ while (mask) {
+ u32 lts, ltc = __ffs(mask);
+ for (lts = 0; lts < priv->lts_nr; lts++)
+ gf100_ltc_lts_intr(priv, ltc, lts);
+ mask &= ~(1 << ltc);
+ }
+}
+
+static int
+gf100_ltc_init(struct nvkm_object *object)
+{
+ struct nvkm_ltc_priv *priv = (void *)object;
+ u32 lpg128 = !(nv_rd32(priv, 0x100c80) & 0x00000001);
+ int ret;
+
+ ret = nvkm_ltc_init(priv);
+ if (ret)
+ return ret;
+
+ nv_mask(priv, 0x17e820, 0x00100000, 0x00000000); /* INTR_EN &= ~0x10 */
+ nv_wr32(priv, 0x17e8d8, priv->ltc_nr);
+ nv_wr32(priv, 0x17e8d4, priv->tag_base);
+ nv_mask(priv, 0x17e8c0, 0x00000002, lpg128 ? 0x00000002 : 0x00000000);
+ return 0;
+}
+
+void
+gf100_ltc_dtor(struct nvkm_object *object)
+{
+ struct nvkm_fb *pfb = nvkm_fb(object);
+ struct nvkm_ltc_priv *priv = (void *)object;
+
+ nvkm_mm_fini(&priv->tags);
+ nvkm_mm_free(&pfb->vram, &priv->tag_ram);
+
+ nvkm_ltc_destroy(priv);
+}
+
+/* TODO: Figure out tag memory details and drop the over-cautious allocation.
+ */
+int
+gf100_ltc_init_tag_ram(struct nvkm_fb *pfb, struct nvkm_ltc_priv *priv)
+{
+ u32 tag_size, tag_margin, tag_align;
+ int ret;
+
+ /* tags for 1/4 of VRAM should be enough (8192/4 per GiB of VRAM) */
+ priv->num_tags = (pfb->ram->size >> 17) / 4;
+ if (priv->num_tags > (1 << 17))
+ priv->num_tags = 1 << 17; /* we have 17 bits in PTE */
+ priv->num_tags = (priv->num_tags + 63) & ~63; /* round up to 64 */
+
+ tag_align = priv->ltc_nr * 0x800;
+ tag_margin = (tag_align < 0x6000) ? 0x6000 : tag_align;
+
+ /* 4 part 4 sub: 0x2000 bytes for 56 tags */
+ /* 3 part 4 sub: 0x6000 bytes for 168 tags */
+ /*
+ * About 147 bytes per tag. Let's be safe and allocate x2, which makes
+ * 0x4980 bytes for 64 tags, and round up to 0x6000 bytes for 64 tags.
+ *
+ * For 4 GiB of memory we'll have 8192 tags which makes 3 MiB, < 0.1 %.
+ */
+ tag_size = (priv->num_tags / 64) * 0x6000 + tag_margin;
+ tag_size += tag_align;
+ tag_size = (tag_size + 0xfff) >> 12; /* round up */
+
+ ret = nvkm_mm_tail(&pfb->vram, 1, 1, tag_size, tag_size, 1,
+ &priv->tag_ram);
+ if (ret) {
+ priv->num_tags = 0;
+ } else {
+ u64 tag_base = ((u64)priv->tag_ram->offset << 12) + tag_margin;
+
+ tag_base += tag_align - 1;
+ ret = do_div(tag_base, tag_align);
+
+ priv->tag_base = tag_base;
+ }
+
+ ret = nvkm_mm_init(&priv->tags, 0, priv->num_tags, 1);
+ return ret;
+}
+
+int
+gf100_ltc_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 size,
+ struct nvkm_object **pobject)
+{
+ struct nvkm_fb *pfb = nvkm_fb(parent);
+ struct nvkm_ltc_priv *priv;
+ u32 parts, mask;
+ int ret, i;
+
+ ret = nvkm_ltc_create(parent, engine, oclass, &priv);
+ *pobject = nv_object(priv);
+ if (ret)
+ return ret;
+
+ parts = nv_rd32(priv, 0x022438);
+ mask = nv_rd32(priv, 0x022554);
+ for (i = 0; i < parts; i++) {
+ if (!(mask & (1 << i)))
+ priv->ltc_nr++;
+ }
+ priv->lts_nr = nv_rd32(priv, 0x17e8dc) >> 28;
+
+ ret = gf100_ltc_init_tag_ram(pfb, priv);
+ if (ret)
+ return ret;
+
+ nv_subdev(priv)->intr = gf100_ltc_intr;
+ return 0;
+}
+
+struct nvkm_oclass *
+gf100_ltc_oclass = &(struct nvkm_ltc_impl) {
+ .base.handle = NV_SUBDEV(LTC, 0xc0),
+ .base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = gf100_ltc_ctor,
+ .dtor = gf100_ltc_dtor,
+ .init = gf100_ltc_init,
+ .fini = _nvkm_ltc_fini,
+ },
+ .intr = gf100_ltc_intr,
+ .cbc_clear = gf100_ltc_cbc_clear,
+ .cbc_wait = gf100_ltc_cbc_wait,
+ .zbc = 16,
+ .zbc_clear_color = gf100_ltc_zbc_clear_color,
+ .zbc_clear_depth = gf100_ltc_zbc_clear_depth,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/gk104.c b/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/gk104.c
new file mode 100644
index 000000000000..d53959b5ec67
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/gk104.c
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "priv.h"
+
+static int
+gk104_ltc_init(struct nvkm_object *object)
+{
+ struct nvkm_ltc_priv *priv = (void *)object;
+ u32 lpg128 = !(nv_rd32(priv, 0x100c80) & 0x00000001);
+ int ret;
+
+ ret = nvkm_ltc_init(priv);
+ if (ret)
+ return ret;
+
+ nv_wr32(priv, 0x17e8d8, priv->ltc_nr);
+ nv_wr32(priv, 0x17e000, priv->ltc_nr);
+ nv_wr32(priv, 0x17e8d4, priv->tag_base);
+ nv_mask(priv, 0x17e8c0, 0x00000002, lpg128 ? 0x00000002 : 0x00000000);
+ return 0;
+}
+
+struct nvkm_oclass *
+gk104_ltc_oclass = &(struct nvkm_ltc_impl) {
+ .base.handle = NV_SUBDEV(LTC, 0xe4),
+ .base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = gf100_ltc_ctor,
+ .dtor = gf100_ltc_dtor,
+ .init = gk104_ltc_init,
+ .fini = _nvkm_ltc_fini,
+ },
+ .intr = gf100_ltc_intr,
+ .cbc_clear = gf100_ltc_cbc_clear,
+ .cbc_wait = gf100_ltc_cbc_wait,
+ .zbc = 16,
+ .zbc_clear_color = gf100_ltc_zbc_clear_color,
+ .zbc_clear_depth = gf100_ltc_zbc_clear_depth,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/gm107.c b/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/gm107.c
new file mode 100644
index 000000000000..6b3f6f4ce107
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/gm107.c
@@ -0,0 +1,153 @@
+/*
+ * Copyright 2014 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "priv.h"
+
+#include <subdev/fb.h>
+#include <subdev/timer.h>
+
+static void
+gm107_ltc_cbc_clear(struct nvkm_ltc_priv *priv, u32 start, u32 limit)
+{
+ nv_wr32(priv, 0x17e270, start);
+ nv_wr32(priv, 0x17e274, limit);
+ nv_wr32(priv, 0x17e26c, 0x00000004);
+}
+
+static void
+gm107_ltc_cbc_wait(struct nvkm_ltc_priv *priv)
+{
+ int c, s;
+ for (c = 0; c < priv->ltc_nr; c++) {
+ for (s = 0; s < priv->lts_nr; s++)
+ nv_wait(priv, 0x14046c + c * 0x2000 + s * 0x200, ~0, 0);
+ }
+}
+
+static void
+gm107_ltc_zbc_clear_color(struct nvkm_ltc_priv *priv, int i, const u32 color[4])
+{
+ nv_mask(priv, 0x17e338, 0x0000000f, i);
+ nv_wr32(priv, 0x17e33c, color[0]);
+ nv_wr32(priv, 0x17e340, color[1]);
+ nv_wr32(priv, 0x17e344, color[2]);
+ nv_wr32(priv, 0x17e348, color[3]);
+}
+
+static void
+gm107_ltc_zbc_clear_depth(struct nvkm_ltc_priv *priv, int i, const u32 depth)
+{
+ nv_mask(priv, 0x17e338, 0x0000000f, i);
+ nv_wr32(priv, 0x17e34c, depth);
+}
+
+static void
+gm107_ltc_lts_isr(struct nvkm_ltc_priv *priv, int ltc, int lts)
+{
+ u32 base = 0x140000 + (ltc * 0x2000) + (lts * 0x400);
+ u32 stat = nv_rd32(priv, base + 0x00c);
+
+ if (stat) {
+ nv_info(priv, "LTC%d_LTS%d: 0x%08x\n", ltc, lts, stat);
+ nv_wr32(priv, base + 0x00c, stat);
+ }
+}
+
+static void
+gm107_ltc_intr(struct nvkm_subdev *subdev)
+{
+ struct nvkm_ltc_priv *priv = (void *)subdev;
+ u32 mask;
+
+ mask = nv_rd32(priv, 0x00017c);
+ while (mask) {
+ u32 lts, ltc = __ffs(mask);
+ for (lts = 0; lts < priv->lts_nr; lts++)
+ gm107_ltc_lts_isr(priv, ltc, lts);
+ mask &= ~(1 << ltc);
+ }
+}
+
+static int
+gm107_ltc_init(struct nvkm_object *object)
+{
+ struct nvkm_ltc_priv *priv = (void *)object;
+ u32 lpg128 = !(nv_rd32(priv, 0x100c80) & 0x00000001);
+ int ret;
+
+ ret = nvkm_ltc_init(priv);
+ if (ret)
+ return ret;
+
+ nv_wr32(priv, 0x17e27c, priv->ltc_nr);
+ nv_wr32(priv, 0x17e278, priv->tag_base);
+ nv_mask(priv, 0x17e264, 0x00000002, lpg128 ? 0x00000002 : 0x00000000);
+ return 0;
+}
+
+static int
+gm107_ltc_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 size,
+ struct nvkm_object **pobject)
+{
+ struct nvkm_fb *pfb = nvkm_fb(parent);
+ struct nvkm_ltc_priv *priv;
+ u32 parts, mask;
+ int ret, i;
+
+ ret = nvkm_ltc_create(parent, engine, oclass, &priv);
+ *pobject = nv_object(priv);
+ if (ret)
+ return ret;
+
+ parts = nv_rd32(priv, 0x022438);
+ mask = nv_rd32(priv, 0x021c14);
+ for (i = 0; i < parts; i++) {
+ if (!(mask & (1 << i)))
+ priv->ltc_nr++;
+ }
+ priv->lts_nr = nv_rd32(priv, 0x17e280) >> 28;
+
+ ret = gf100_ltc_init_tag_ram(pfb, priv);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+struct nvkm_oclass *
+gm107_ltc_oclass = &(struct nvkm_ltc_impl) {
+ .base.handle = NV_SUBDEV(LTC, 0xff),
+ .base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = gm107_ltc_ctor,
+ .dtor = gf100_ltc_dtor,
+ .init = gm107_ltc_init,
+ .fini = _nvkm_ltc_fini,
+ },
+ .intr = gm107_ltc_intr,
+ .cbc_clear = gm107_ltc_cbc_clear,
+ .cbc_wait = gm107_ltc_cbc_wait,
+ .zbc = 16,
+ .zbc_clear_color = gm107_ltc_zbc_clear_color,
+ .zbc_clear_depth = gm107_ltc_zbc_clear_depth,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/priv.h b/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/priv.h
new file mode 100644
index 000000000000..09537d7b6783
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/priv.h
@@ -0,0 +1,69 @@
+#ifndef __NVKM_LTC_PRIV_H__
+#define __NVKM_LTC_PRIV_H__
+#include <subdev/ltc.h>
+
+#include <core/mm.h>
+struct nvkm_fb;
+
+struct nvkm_ltc_priv {
+ struct nvkm_ltc base;
+ u32 ltc_nr;
+ u32 lts_nr;
+
+ u32 num_tags;
+ u32 tag_base;
+ struct nvkm_mm tags;
+ struct nvkm_mm_node *tag_ram;
+
+ u32 zbc_color[NVKM_LTC_MAX_ZBC_CNT][4];
+ u32 zbc_depth[NVKM_LTC_MAX_ZBC_CNT];
+};
+
+#define nvkm_ltc_create(p,e,o,d) \
+ nvkm_ltc_create_((p), (e), (o), sizeof(**d), (void **)d)
+#define nvkm_ltc_destroy(p) ({ \
+ struct nvkm_ltc_priv *_priv = (p); \
+ _nvkm_ltc_dtor(nv_object(_priv)); \
+})
+#define nvkm_ltc_init(p) ({ \
+ struct nvkm_ltc_priv *_priv = (p); \
+ _nvkm_ltc_init(nv_object(_priv)); \
+})
+#define nvkm_ltc_fini(p,s) ({ \
+ struct nvkm_ltc_priv *_priv = (p); \
+ _nvkm_ltc_fini(nv_object(_priv), (s)); \
+})
+
+int nvkm_ltc_create_(struct nvkm_object *, struct nvkm_object *,
+ struct nvkm_oclass *, int, void **);
+
+#define _nvkm_ltc_dtor _nvkm_subdev_dtor
+int _nvkm_ltc_init(struct nvkm_object *);
+#define _nvkm_ltc_fini _nvkm_subdev_fini
+
+int gf100_ltc_ctor(struct nvkm_object *, struct nvkm_object *,
+ struct nvkm_oclass *, void *, u32,
+ struct nvkm_object **);
+void gf100_ltc_dtor(struct nvkm_object *);
+int gf100_ltc_init_tag_ram(struct nvkm_fb *, struct nvkm_ltc_priv *);
+int gf100_ltc_tags_alloc(struct nvkm_ltc *, u32, struct nvkm_mm_node **);
+void gf100_ltc_tags_free(struct nvkm_ltc *, struct nvkm_mm_node **);
+
+struct nvkm_ltc_impl {
+ struct nvkm_oclass base;
+ void (*intr)(struct nvkm_subdev *);
+
+ void (*cbc_clear)(struct nvkm_ltc_priv *, u32 start, u32 limit);
+ void (*cbc_wait)(struct nvkm_ltc_priv *);
+
+ int zbc;
+ void (*zbc_clear_color)(struct nvkm_ltc_priv *, int, const u32[4]);
+ void (*zbc_clear_depth)(struct nvkm_ltc_priv *, int, const u32);
+};
+
+void gf100_ltc_intr(struct nvkm_subdev *);
+void gf100_ltc_cbc_clear(struct nvkm_ltc_priv *, u32, u32);
+void gf100_ltc_cbc_wait(struct nvkm_ltc_priv *);
+void gf100_ltc_zbc_clear_color(struct nvkm_ltc_priv *, int, const u32[4]);
+void gf100_ltc_zbc_clear_depth(struct nvkm_ltc_priv *, int, const u32);
+#endif
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mc/Kbuild b/drivers/gpu/drm/nouveau/nvkm/subdev/mc/Kbuild
new file mode 100644
index 000000000000..721643f04bb5
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mc/Kbuild
@@ -0,0 +1,11 @@
+nvkm-y += nvkm/subdev/mc/base.o
+nvkm-y += nvkm/subdev/mc/nv04.o
+nvkm-y += nvkm/subdev/mc/nv40.o
+nvkm-y += nvkm/subdev/mc/nv44.o
+nvkm-y += nvkm/subdev/mc/nv4c.o
+nvkm-y += nvkm/subdev/mc/nv50.o
+nvkm-y += nvkm/subdev/mc/g94.o
+nvkm-y += nvkm/subdev/mc/g98.o
+nvkm-y += nvkm/subdev/mc/gf100.o
+nvkm-y += nvkm/subdev/mc/gf106.o
+nvkm-y += nvkm/subdev/mc/gk20a.o
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mc/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mc/base.c
new file mode 100644
index 000000000000..5b051a26653e
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mc/base.c
@@ -0,0 +1,169 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "priv.h"
+
+#include <core/device.h>
+#include <core/option.h>
+
+static inline void
+nvkm_mc_unk260(struct nvkm_mc *pmc, u32 data)
+{
+ const struct nvkm_mc_oclass *impl = (void *)nv_oclass(pmc);
+ if (impl->unk260)
+ impl->unk260(pmc, data);
+}
+
+static inline u32
+nvkm_mc_intr_mask(struct nvkm_mc *pmc)
+{
+ u32 intr = nv_rd32(pmc, 0x000100);
+ if (intr == 0xffffffff) /* likely fallen off the bus */
+ intr = 0x00000000;
+ return intr;
+}
+
+static irqreturn_t
+nvkm_mc_intr(int irq, void *arg)
+{
+ struct nvkm_mc *pmc = arg;
+ const struct nvkm_mc_oclass *oclass = (void *)nv_object(pmc)->oclass;
+ const struct nvkm_mc_intr *map = oclass->intr;
+ struct nvkm_subdev *unit;
+ u32 intr;
+
+ nv_wr32(pmc, 0x000140, 0x00000000);
+ nv_rd32(pmc, 0x000140);
+ intr = nvkm_mc_intr_mask(pmc);
+ if (pmc->use_msi)
+ oclass->msi_rearm(pmc);
+
+ if (intr) {
+ u32 stat = intr = nvkm_mc_intr_mask(pmc);
+ while (map->stat) {
+ if (intr & map->stat) {
+ unit = nvkm_subdev(pmc, map->unit);
+ if (unit && unit->intr)
+ unit->intr(unit);
+ stat &= ~map->stat;
+ }
+ map++;
+ }
+
+ if (stat)
+ nv_error(pmc, "unknown intr 0x%08x\n", stat);
+ }
+
+ nv_wr32(pmc, 0x000140, 0x00000001);
+ return intr ? IRQ_HANDLED : IRQ_NONE;
+}
+
+int
+_nvkm_mc_fini(struct nvkm_object *object, bool suspend)
+{
+ struct nvkm_mc *pmc = (void *)object;
+ nv_wr32(pmc, 0x000140, 0x00000000);
+ return nvkm_subdev_fini(&pmc->base, suspend);
+}
+
+int
+_nvkm_mc_init(struct nvkm_object *object)
+{
+ struct nvkm_mc *pmc = (void *)object;
+ int ret = nvkm_subdev_init(&pmc->base);
+ if (ret)
+ return ret;
+ nv_wr32(pmc, 0x000140, 0x00000001);
+ return 0;
+}
+
+void
+_nvkm_mc_dtor(struct nvkm_object *object)
+{
+ struct nvkm_device *device = nv_device(object);
+ struct nvkm_mc *pmc = (void *)object;
+ free_irq(pmc->irq, pmc);
+ if (pmc->use_msi)
+ pci_disable_msi(device->pdev);
+ nvkm_subdev_destroy(&pmc->base);
+}
+
+int
+nvkm_mc_create_(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *bclass, int length, void **pobject)
+{
+ const struct nvkm_mc_oclass *oclass = (void *)bclass;
+ struct nvkm_device *device = nv_device(parent);
+ struct nvkm_mc *pmc;
+ int ret;
+
+ ret = nvkm_subdev_create_(parent, engine, bclass, 0, "PMC",
+ "master", length, pobject);
+ pmc = *pobject;
+ if (ret)
+ return ret;
+
+ pmc->unk260 = nvkm_mc_unk260;
+
+ if (nv_device_is_pci(device)) {
+ switch (device->pdev->device & 0x0ff0) {
+ case 0x00f0:
+ case 0x02e0:
+ /* BR02? NFI how these would be handled yet exactly */
+ break;
+ default:
+ switch (device->chipset) {
+ case 0xaa:
+ /* reported broken, nv also disable it */
+ break;
+ default:
+ pmc->use_msi = true;
+ break;
+ }
+ }
+
+ pmc->use_msi = nvkm_boolopt(device->cfgopt, "NvMSI",
+ pmc->use_msi);
+
+ if (pmc->use_msi && oclass->msi_rearm) {
+ pmc->use_msi = pci_enable_msi(device->pdev) == 0;
+ if (pmc->use_msi) {
+ nv_info(pmc, "MSI interrupts enabled\n");
+ oclass->msi_rearm(pmc);
+ }
+ } else {
+ pmc->use_msi = false;
+ }
+ }
+
+ ret = nv_device_get_irq(device, true);
+ if (ret < 0)
+ return ret;
+ pmc->irq = ret;
+
+ ret = request_irq(pmc->irq, nvkm_mc_intr, IRQF_SHARED, "nvkm", pmc);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mc/g94.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mc/g94.c
new file mode 100644
index 000000000000..f042e7d8321d
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mc/g94.c
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "nv04.h"
+
+struct nvkm_oclass *
+g94_mc_oclass = &(struct nvkm_mc_oclass) {
+ .base.handle = NV_SUBDEV(MC, 0x94),
+ .base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv04_mc_ctor,
+ .dtor = _nvkm_mc_dtor,
+ .init = nv50_mc_init,
+ .fini = _nvkm_mc_fini,
+ },
+ .intr = nv50_mc_intr,
+ .msi_rearm = nv40_mc_msi_rearm,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mc/g98.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mc/g98.c
new file mode 100644
index 000000000000..8ab7f1272a14
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mc/g98.c
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "nv04.h"
+
+static const struct nvkm_mc_intr
+g98_mc_intr[] = {
+ { 0x04000000, NVDEV_ENGINE_DISP }, /* DISP first, so pageflip timestamps work */
+ { 0x00000001, NVDEV_ENGINE_MSPPP },
+ { 0x00000100, NVDEV_ENGINE_FIFO },
+ { 0x00001000, NVDEV_ENGINE_GR },
+ { 0x00004000, NVDEV_ENGINE_SEC }, /* NV84:NVA3 */
+ { 0x00008000, NVDEV_ENGINE_MSVLD },
+ { 0x00020000, NVDEV_ENGINE_MSPDEC },
+ { 0x00040000, NVDEV_SUBDEV_PMU }, /* NVA3:NVC0 */
+ { 0x00080000, NVDEV_SUBDEV_THERM }, /* NVA3:NVC0 */
+ { 0x00100000, NVDEV_SUBDEV_TIMER },
+ { 0x00200000, NVDEV_SUBDEV_GPIO }, /* PMGR->GPIO */
+ { 0x00200000, NVDEV_SUBDEV_I2C }, /* PMGR->I2C/AUX */
+ { 0x00400000, NVDEV_ENGINE_CE0 }, /* NVA3- */
+ { 0x10000000, NVDEV_SUBDEV_BUS },
+ { 0x80000000, NVDEV_ENGINE_SW },
+ { 0x0042d101, NVDEV_SUBDEV_FB },
+ {},
+};
+
+struct nvkm_oclass *
+g98_mc_oclass = &(struct nvkm_mc_oclass) {
+ .base.handle = NV_SUBDEV(MC, 0x98),
+ .base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv04_mc_ctor,
+ .dtor = _nvkm_mc_dtor,
+ .init = nv50_mc_init,
+ .fini = _nvkm_mc_fini,
+ },
+ .intr = g98_mc_intr,
+ .msi_rearm = nv40_mc_msi_rearm,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mc/gf100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mc/gf100.c
new file mode 100644
index 000000000000..2425984b045e
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mc/gf100.c
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "nv04.h"
+
+const struct nvkm_mc_intr
+gf100_mc_intr[] = {
+ { 0x04000000, NVDEV_ENGINE_DISP }, /* DISP first, so pageflip timestamps work. */
+ { 0x00000001, NVDEV_ENGINE_MSPPP },
+ { 0x00000020, NVDEV_ENGINE_CE0 },
+ { 0x00000040, NVDEV_ENGINE_CE1 },
+ { 0x00000080, NVDEV_ENGINE_CE2 },
+ { 0x00000100, NVDEV_ENGINE_FIFO },
+ { 0x00001000, NVDEV_ENGINE_GR },
+ { 0x00002000, NVDEV_SUBDEV_FB },
+ { 0x00008000, NVDEV_ENGINE_MSVLD },
+ { 0x00040000, NVDEV_SUBDEV_THERM },
+ { 0x00020000, NVDEV_ENGINE_MSPDEC },
+ { 0x00100000, NVDEV_SUBDEV_TIMER },
+ { 0x00200000, NVDEV_SUBDEV_GPIO }, /* PMGR->GPIO */
+ { 0x00200000, NVDEV_SUBDEV_I2C }, /* PMGR->I2C/AUX */
+ { 0x01000000, NVDEV_SUBDEV_PMU },
+ { 0x02000000, NVDEV_SUBDEV_LTC },
+ { 0x08000000, NVDEV_SUBDEV_FB },
+ { 0x10000000, NVDEV_SUBDEV_BUS },
+ { 0x40000000, NVDEV_SUBDEV_IBUS },
+ { 0x80000000, NVDEV_ENGINE_SW },
+ {},
+};
+
+static void
+gf100_mc_msi_rearm(struct nvkm_mc *pmc)
+{
+ struct nv04_mc_priv *priv = (void *)pmc;
+ nv_wr32(priv, 0x088704, 0x00000000);
+}
+
+void
+gf100_mc_unk260(struct nvkm_mc *pmc, u32 data)
+{
+ nv_wr32(pmc, 0x000260, data);
+}
+
+struct nvkm_oclass *
+gf100_mc_oclass = &(struct nvkm_mc_oclass) {
+ .base.handle = NV_SUBDEV(MC, 0xc0),
+ .base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv04_mc_ctor,
+ .dtor = _nvkm_mc_dtor,
+ .init = nv50_mc_init,
+ .fini = _nvkm_mc_fini,
+ },
+ .intr = gf100_mc_intr,
+ .msi_rearm = gf100_mc_msi_rearm,
+ .unk260 = gf100_mc_unk260,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mc/gf106.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mc/gf106.c
new file mode 100644
index 000000000000..8d2a8f457778
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mc/gf106.c
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "nv04.h"
+
+struct nvkm_oclass *
+gf106_mc_oclass = &(struct nvkm_mc_oclass) {
+ .base.handle = NV_SUBDEV(MC, 0xc3),
+ .base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv04_mc_ctor,
+ .dtor = _nvkm_mc_dtor,
+ .init = nv50_mc_init,
+ .fini = _nvkm_mc_fini,
+ },
+ .intr = gf100_mc_intr,
+ .msi_rearm = nv40_mc_msi_rearm,
+ .unk260 = gf100_mc_unk260,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mc/gk20a.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mc/gk20a.c
new file mode 100644
index 000000000000..43b27742956d
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mc/gk20a.c
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "nv04.h"
+
+struct nvkm_oclass *
+gk20a_mc_oclass = &(struct nvkm_mc_oclass) {
+ .base.handle = NV_SUBDEV(MC, 0xea),
+ .base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv04_mc_ctor,
+ .dtor = _nvkm_mc_dtor,
+ .init = nv50_mc_init,
+ .fini = _nvkm_mc_fini,
+ },
+ .intr = gf100_mc_intr,
+ .msi_rearm = nv40_mc_msi_rearm,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv04.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv04.c
new file mode 100644
index 000000000000..32713827b4dc
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv04.c
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "nv04.h"
+
+const struct nvkm_mc_intr
+nv04_mc_intr[] = {
+ { 0x00000001, NVDEV_ENGINE_MPEG }, /* NV17- MPEG/ME */
+ { 0x00000100, NVDEV_ENGINE_FIFO },
+ { 0x00001000, NVDEV_ENGINE_GR },
+ { 0x00010000, NVDEV_ENGINE_DISP },
+ { 0x00020000, NVDEV_ENGINE_VP }, /* NV40- */
+ { 0x00100000, NVDEV_SUBDEV_TIMER },
+ { 0x01000000, NVDEV_ENGINE_DISP }, /* NV04- PCRTC0 */
+ { 0x02000000, NVDEV_ENGINE_DISP }, /* NV11- PCRTC1 */
+ { 0x10000000, NVDEV_SUBDEV_BUS },
+ { 0x80000000, NVDEV_ENGINE_SW },
+ {}
+};
+
+int
+nv04_mc_init(struct nvkm_object *object)
+{
+ struct nv04_mc_priv *priv = (void *)object;
+
+ nv_wr32(priv, 0x000200, 0xffffffff); /* everything enabled */
+ nv_wr32(priv, 0x001850, 0x00000001); /* disable rom access */
+
+ return nvkm_mc_init(&priv->base);
+}
+
+int
+nv04_mc_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 size,
+ struct nvkm_object **pobject)
+{
+ struct nv04_mc_priv *priv;
+ int ret;
+
+ ret = nvkm_mc_create(parent, engine, oclass, &priv);
+ *pobject = nv_object(priv);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+struct nvkm_oclass *
+nv04_mc_oclass = &(struct nvkm_mc_oclass) {
+ .base.handle = NV_SUBDEV(MC, 0x04),
+ .base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv04_mc_ctor,
+ .dtor = _nvkm_mc_dtor,
+ .init = nv04_mc_init,
+ .fini = _nvkm_mc_fini,
+ },
+ .intr = nv04_mc_intr,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv04.h b/drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv04.h
new file mode 100644
index 000000000000..411de3d08ab6
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv04.h
@@ -0,0 +1,20 @@
+#ifndef __NVKM_MC_NV04_H__
+#define __NVKM_MC_NV04_H__
+#include "priv.h"
+
+struct nv04_mc_priv {
+ struct nvkm_mc base;
+};
+
+int nv04_mc_ctor(struct nvkm_object *, struct nvkm_object *,
+ struct nvkm_oclass *, void *, u32,
+ struct nvkm_object **);
+
+extern const struct nvkm_mc_intr nv04_mc_intr[];
+int nv04_mc_init(struct nvkm_object *);
+void nv40_mc_msi_rearm(struct nvkm_mc *);
+int nv44_mc_init(struct nvkm_object *object);
+int nv50_mc_init(struct nvkm_object *);
+extern const struct nvkm_mc_intr nv50_mc_intr[];
+extern const struct nvkm_mc_intr gf100_mc_intr[];
+#endif
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv40.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv40.c
new file mode 100644
index 000000000000..b7613059da08
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv40.c
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "nv04.h"
+
+void
+nv40_mc_msi_rearm(struct nvkm_mc *pmc)
+{
+ struct nv04_mc_priv *priv = (void *)pmc;
+ nv_wr08(priv, 0x088068, 0xff);
+}
+
+struct nvkm_oclass *
+nv40_mc_oclass = &(struct nvkm_mc_oclass) {
+ .base.handle = NV_SUBDEV(MC, 0x40),
+ .base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv04_mc_ctor,
+ .dtor = _nvkm_mc_dtor,
+ .init = nv04_mc_init,
+ .fini = _nvkm_mc_fini,
+ },
+ .intr = nv04_mc_intr,
+ .msi_rearm = nv40_mc_msi_rearm,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv44.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv44.c
new file mode 100644
index 000000000000..2c7f7c701a2b
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv44.c
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "nv04.h"
+
+int
+nv44_mc_init(struct nvkm_object *object)
+{
+ struct nv04_mc_priv *priv = (void *)object;
+ u32 tmp = nv_rd32(priv, 0x10020c);
+
+ nv_wr32(priv, 0x000200, 0xffffffff); /* everything enabled */
+
+ nv_wr32(priv, 0x001700, tmp);
+ nv_wr32(priv, 0x001704, 0);
+ nv_wr32(priv, 0x001708, 0);
+ nv_wr32(priv, 0x00170c, tmp);
+
+ return nvkm_mc_init(&priv->base);
+}
+
+struct nvkm_oclass *
+nv44_mc_oclass = &(struct nvkm_mc_oclass) {
+ .base.handle = NV_SUBDEV(MC, 0x44),
+ .base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv04_mc_ctor,
+ .dtor = _nvkm_mc_dtor,
+ .init = nv44_mc_init,
+ .fini = _nvkm_mc_fini,
+ },
+ .intr = nv04_mc_intr,
+ .msi_rearm = nv40_mc_msi_rearm,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv4c.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv4c.c
new file mode 100644
index 000000000000..c0aac7e20d45
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv4c.c
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2014 Ilia Mirkin
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ilia Mirkin
+ */
+#include "nv04.h"
+
+struct nvkm_oclass *
+nv4c_mc_oclass = &(struct nvkm_mc_oclass) {
+ .base.handle = NV_SUBDEV(MC, 0x4c),
+ .base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv04_mc_ctor,
+ .dtor = _nvkm_mc_dtor,
+ .init = nv44_mc_init,
+ .fini = _nvkm_mc_fini,
+ },
+ .intr = nv04_mc_intr,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv50.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv50.c
new file mode 100644
index 000000000000..40e3019e1fde
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv50.c
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "nv04.h"
+
+#include <core/device.h>
+
+const struct nvkm_mc_intr
+nv50_mc_intr[] = {
+ { 0x04000000, NVDEV_ENGINE_DISP }, /* DISP before FIFO, so pageflip-timestamping works! */
+ { 0x00000001, NVDEV_ENGINE_MPEG },
+ { 0x00000100, NVDEV_ENGINE_FIFO },
+ { 0x00001000, NVDEV_ENGINE_GR },
+ { 0x00004000, NVDEV_ENGINE_CIPHER }, /* NV84- */
+ { 0x00008000, NVDEV_ENGINE_BSP }, /* NV84- */
+ { 0x00020000, NVDEV_ENGINE_VP }, /* NV84- */
+ { 0x00100000, NVDEV_SUBDEV_TIMER },
+ { 0x00200000, NVDEV_SUBDEV_GPIO }, /* PMGR->GPIO */
+ { 0x00200000, NVDEV_SUBDEV_I2C }, /* PMGR->I2C/AUX */
+ { 0x10000000, NVDEV_SUBDEV_BUS },
+ { 0x80000000, NVDEV_ENGINE_SW },
+ { 0x0002d101, NVDEV_SUBDEV_FB },
+ {},
+};
+
+static void
+nv50_mc_msi_rearm(struct nvkm_mc *pmc)
+{
+ struct nvkm_device *device = nv_device(pmc);
+ pci_write_config_byte(device->pdev, 0x68, 0xff);
+}
+
+int
+nv50_mc_init(struct nvkm_object *object)
+{
+ struct nv04_mc_priv *priv = (void *)object;
+ nv_wr32(priv, 0x000200, 0xffffffff); /* everything on */
+ return nvkm_mc_init(&priv->base);
+}
+
+struct nvkm_oclass *
+nv50_mc_oclass = &(struct nvkm_mc_oclass) {
+ .base.handle = NV_SUBDEV(MC, 0x50),
+ .base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv04_mc_ctor,
+ .dtor = _nvkm_mc_dtor,
+ .init = nv50_mc_init,
+ .fini = _nvkm_mc_fini,
+ },
+ .intr = nv50_mc_intr,
+ .msi_rearm = nv50_mc_msi_rearm,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mc/priv.h b/drivers/gpu/drm/nouveau/nvkm/subdev/mc/priv.h
new file mode 100644
index 000000000000..d2cad07afd1a
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mc/priv.h
@@ -0,0 +1,36 @@
+#ifndef __NVKM_MC_PRIV_H__
+#define __NVKM_MC_PRIV_H__
+#include <subdev/mc.h>
+
+#define nvkm_mc_create(p,e,o,d) \
+ nvkm_mc_create_((p), (e), (o), sizeof(**d), (void **)d)
+#define nvkm_mc_destroy(p) ({ \
+ struct nvkm_mc *pmc = (p); _nvkm_mc_dtor(nv_object(pmc)); \
+})
+#define nvkm_mc_init(p) ({ \
+ struct nvkm_mc *pmc = (p); _nvkm_mc_init(nv_object(pmc)); \
+})
+#define nvkm_mc_fini(p,s) ({ \
+ struct nvkm_mc *pmc = (p); _nvkm_mc_fini(nv_object(pmc), (s)); \
+})
+
+int nvkm_mc_create_(struct nvkm_object *, struct nvkm_object *,
+ struct nvkm_oclass *, int, void **);
+void _nvkm_mc_dtor(struct nvkm_object *);
+int _nvkm_mc_init(struct nvkm_object *);
+int _nvkm_mc_fini(struct nvkm_object *, bool);
+
+struct nvkm_mc_intr {
+ u32 stat;
+ u32 unit;
+};
+
+struct nvkm_mc_oclass {
+ struct nvkm_oclass base;
+ const struct nvkm_mc_intr *intr;
+ void (*msi_rearm)(struct nvkm_mc *);
+ void (*unk260)(struct nvkm_mc *, u32);
+};
+
+void gf100_mc_unk260(struct nvkm_mc *, u32);
+#endif
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/Kbuild b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/Kbuild
new file mode 100644
index 000000000000..012c9db687b2
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/Kbuild
@@ -0,0 +1,6 @@
+nvkm-y += nvkm/subdev/mmu/base.o
+nvkm-y += nvkm/subdev/mmu/nv04.o
+nvkm-y += nvkm/subdev/mmu/nv41.o
+nvkm-y += nvkm/subdev/mmu/nv44.o
+nvkm-y += nvkm/subdev/mmu/nv50.o
+nvkm-y += nvkm/subdev/mmu/gf100.o
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/base.c
new file mode 100644
index 000000000000..277b6ec04e24
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/base.c
@@ -0,0 +1,480 @@
+/*
+ * Copyright 2010 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include <subdev/mmu.h>
+#include <subdev/fb.h>
+
+#include <core/gpuobj.h>
+
+void
+nvkm_vm_map_at(struct nvkm_vma *vma, u64 delta, struct nvkm_mem *node)
+{
+ struct nvkm_vm *vm = vma->vm;
+ struct nvkm_mmu *mmu = vm->mmu;
+ struct nvkm_mm_node *r;
+ int big = vma->node->type != mmu->spg_shift;
+ u32 offset = vma->node->offset + (delta >> 12);
+ u32 bits = vma->node->type - 12;
+ u32 pde = (offset >> mmu->pgt_bits) - vm->fpde;
+ u32 pte = (offset & ((1 << mmu->pgt_bits) - 1)) >> bits;
+ u32 max = 1 << (mmu->pgt_bits - bits);
+ u32 end, len;
+
+ delta = 0;
+ list_for_each_entry(r, &node->regions, rl_entry) {
+ u64 phys = (u64)r->offset << 12;
+ u32 num = r->length >> bits;
+
+ while (num) {
+ struct nvkm_gpuobj *pgt = vm->pgt[pde].obj[big];
+
+ end = (pte + num);
+ if (unlikely(end >= max))
+ end = max;
+ len = end - pte;
+
+ mmu->map(vma, pgt, node, pte, len, phys, delta);
+
+ num -= len;
+ pte += len;
+ if (unlikely(end >= max)) {
+ phys += len << (bits + 12);
+ pde++;
+ pte = 0;
+ }
+
+ delta += (u64)len << vma->node->type;
+ }
+ }
+
+ mmu->flush(vm);
+}
+
+static void
+nvkm_vm_map_sg_table(struct nvkm_vma *vma, u64 delta, u64 length,
+ struct nvkm_mem *mem)
+{
+ struct nvkm_vm *vm = vma->vm;
+ struct nvkm_mmu *mmu = vm->mmu;
+ int big = vma->node->type != mmu->spg_shift;
+ u32 offset = vma->node->offset + (delta >> 12);
+ u32 bits = vma->node->type - 12;
+ u32 num = length >> vma->node->type;
+ u32 pde = (offset >> mmu->pgt_bits) - vm->fpde;
+ u32 pte = (offset & ((1 << mmu->pgt_bits) - 1)) >> bits;
+ u32 max = 1 << (mmu->pgt_bits - bits);
+ unsigned m, sglen;
+ u32 end, len;
+ int i;
+ struct scatterlist *sg;
+
+ for_each_sg(mem->sg->sgl, sg, mem->sg->nents, i) {
+ struct nvkm_gpuobj *pgt = vm->pgt[pde].obj[big];
+ sglen = sg_dma_len(sg) >> PAGE_SHIFT;
+
+ end = pte + sglen;
+ if (unlikely(end >= max))
+ end = max;
+ len = end - pte;
+
+ for (m = 0; m < len; m++) {
+ dma_addr_t addr = sg_dma_address(sg) + (m << PAGE_SHIFT);
+
+ mmu->map_sg(vma, pgt, mem, pte, 1, &addr);
+ num--;
+ pte++;
+
+ if (num == 0)
+ goto finish;
+ }
+ if (unlikely(end >= max)) {
+ pde++;
+ pte = 0;
+ }
+ if (m < sglen) {
+ for (; m < sglen; m++) {
+ dma_addr_t addr = sg_dma_address(sg) + (m << PAGE_SHIFT);
+
+ mmu->map_sg(vma, pgt, mem, pte, 1, &addr);
+ num--;
+ pte++;
+ if (num == 0)
+ goto finish;
+ }
+ }
+
+ }
+finish:
+ mmu->flush(vm);
+}
+
+static void
+nvkm_vm_map_sg(struct nvkm_vma *vma, u64 delta, u64 length,
+ struct nvkm_mem *mem)
+{
+ struct nvkm_vm *vm = vma->vm;
+ struct nvkm_mmu *mmu = vm->mmu;
+ dma_addr_t *list = mem->pages;
+ int big = vma->node->type != mmu->spg_shift;
+ u32 offset = vma->node->offset + (delta >> 12);
+ u32 bits = vma->node->type - 12;
+ u32 num = length >> vma->node->type;
+ u32 pde = (offset >> mmu->pgt_bits) - vm->fpde;
+ u32 pte = (offset & ((1 << mmu->pgt_bits) - 1)) >> bits;
+ u32 max = 1 << (mmu->pgt_bits - bits);
+ u32 end, len;
+
+ while (num) {
+ struct nvkm_gpuobj *pgt = vm->pgt[pde].obj[big];
+
+ end = (pte + num);
+ if (unlikely(end >= max))
+ end = max;
+ len = end - pte;
+
+ mmu->map_sg(vma, pgt, mem, pte, len, list);
+
+ num -= len;
+ pte += len;
+ list += len;
+ if (unlikely(end >= max)) {
+ pde++;
+ pte = 0;
+ }
+ }
+
+ mmu->flush(vm);
+}
+
+void
+nvkm_vm_map(struct nvkm_vma *vma, struct nvkm_mem *node)
+{
+ if (node->sg)
+ nvkm_vm_map_sg_table(vma, 0, node->size << 12, node);
+ else
+ if (node->pages)
+ nvkm_vm_map_sg(vma, 0, node->size << 12, node);
+ else
+ nvkm_vm_map_at(vma, 0, node);
+}
+
+void
+nvkm_vm_unmap_at(struct nvkm_vma *vma, u64 delta, u64 length)
+{
+ struct nvkm_vm *vm = vma->vm;
+ struct nvkm_mmu *mmu = vm->mmu;
+ int big = vma->node->type != mmu->spg_shift;
+ u32 offset = vma->node->offset + (delta >> 12);
+ u32 bits = vma->node->type - 12;
+ u32 num = length >> vma->node->type;
+ u32 pde = (offset >> mmu->pgt_bits) - vm->fpde;
+ u32 pte = (offset & ((1 << mmu->pgt_bits) - 1)) >> bits;
+ u32 max = 1 << (mmu->pgt_bits - bits);
+ u32 end, len;
+
+ while (num) {
+ struct nvkm_gpuobj *pgt = vm->pgt[pde].obj[big];
+
+ end = (pte + num);
+ if (unlikely(end >= max))
+ end = max;
+ len = end - pte;
+
+ mmu->unmap(pgt, pte, len);
+
+ num -= len;
+ pte += len;
+ if (unlikely(end >= max)) {
+ pde++;
+ pte = 0;
+ }
+ }
+
+ mmu->flush(vm);
+}
+
+void
+nvkm_vm_unmap(struct nvkm_vma *vma)
+{
+ nvkm_vm_unmap_at(vma, 0, (u64)vma->node->length << 12);
+}
+
+static void
+nvkm_vm_unmap_pgt(struct nvkm_vm *vm, int big, u32 fpde, u32 lpde)
+{
+ struct nvkm_mmu *mmu = vm->mmu;
+ struct nvkm_vm_pgd *vpgd;
+ struct nvkm_vm_pgt *vpgt;
+ struct nvkm_gpuobj *pgt;
+ u32 pde;
+
+ for (pde = fpde; pde <= lpde; pde++) {
+ vpgt = &vm->pgt[pde - vm->fpde];
+ if (--vpgt->refcount[big])
+ continue;
+
+ pgt = vpgt->obj[big];
+ vpgt->obj[big] = NULL;
+
+ list_for_each_entry(vpgd, &vm->pgd_list, head) {
+ mmu->map_pgt(vpgd->obj, pde, vpgt->obj);
+ }
+
+ mutex_unlock(&nv_subdev(mmu)->mutex);
+ nvkm_gpuobj_ref(NULL, &pgt);
+ mutex_lock(&nv_subdev(mmu)->mutex);
+ }
+}
+
+static int
+nvkm_vm_map_pgt(struct nvkm_vm *vm, u32 pde, u32 type)
+{
+ struct nvkm_mmu *mmu = vm->mmu;
+ struct nvkm_vm_pgt *vpgt = &vm->pgt[pde - vm->fpde];
+ struct nvkm_vm_pgd *vpgd;
+ struct nvkm_gpuobj *pgt;
+ int big = (type != mmu->spg_shift);
+ u32 pgt_size;
+ int ret;
+
+ pgt_size = (1 << (mmu->pgt_bits + 12)) >> type;
+ pgt_size *= 8;
+
+ mutex_unlock(&nv_subdev(mmu)->mutex);
+ ret = nvkm_gpuobj_new(nv_object(vm->mmu), NULL, pgt_size, 0x1000,
+ NVOBJ_FLAG_ZERO_ALLOC, &pgt);
+ mutex_lock(&nv_subdev(mmu)->mutex);
+ if (unlikely(ret))
+ return ret;
+
+ /* someone beat us to filling the PDE while we didn't have the lock */
+ if (unlikely(vpgt->refcount[big]++)) {
+ mutex_unlock(&nv_subdev(mmu)->mutex);
+ nvkm_gpuobj_ref(NULL, &pgt);
+ mutex_lock(&nv_subdev(mmu)->mutex);
+ return 0;
+ }
+
+ vpgt->obj[big] = pgt;
+ list_for_each_entry(vpgd, &vm->pgd_list, head) {
+ mmu->map_pgt(vpgd->obj, pde, vpgt->obj);
+ }
+
+ return 0;
+}
+
+int
+nvkm_vm_get(struct nvkm_vm *vm, u64 size, u32 page_shift, u32 access,
+ struct nvkm_vma *vma)
+{
+ struct nvkm_mmu *mmu = vm->mmu;
+ u32 align = (1 << page_shift) >> 12;
+ u32 msize = size >> 12;
+ u32 fpde, lpde, pde;
+ int ret;
+
+ mutex_lock(&nv_subdev(mmu)->mutex);
+ ret = nvkm_mm_head(&vm->mm, 0, page_shift, msize, msize, align,
+ &vma->node);
+ if (unlikely(ret != 0)) {
+ mutex_unlock(&nv_subdev(mmu)->mutex);
+ return ret;
+ }
+
+ fpde = (vma->node->offset >> mmu->pgt_bits);
+ lpde = (vma->node->offset + vma->node->length - 1) >> mmu->pgt_bits;
+
+ for (pde = fpde; pde <= lpde; pde++) {
+ struct nvkm_vm_pgt *vpgt = &vm->pgt[pde - vm->fpde];
+ int big = (vma->node->type != mmu->spg_shift);
+
+ if (likely(vpgt->refcount[big])) {
+ vpgt->refcount[big]++;
+ continue;
+ }
+
+ ret = nvkm_vm_map_pgt(vm, pde, vma->node->type);
+ if (ret) {
+ if (pde != fpde)
+ nvkm_vm_unmap_pgt(vm, big, fpde, pde - 1);
+ nvkm_mm_free(&vm->mm, &vma->node);
+ mutex_unlock(&nv_subdev(mmu)->mutex);
+ return ret;
+ }
+ }
+ mutex_unlock(&nv_subdev(mmu)->mutex);
+
+ vma->vm = NULL;
+ nvkm_vm_ref(vm, &vma->vm, NULL);
+ vma->offset = (u64)vma->node->offset << 12;
+ vma->access = access;
+ return 0;
+}
+
+void
+nvkm_vm_put(struct nvkm_vma *vma)
+{
+ struct nvkm_vm *vm = vma->vm;
+ struct nvkm_mmu *mmu = vm->mmu;
+ u32 fpde, lpde;
+
+ if (unlikely(vma->node == NULL))
+ return;
+ fpde = (vma->node->offset >> mmu->pgt_bits);
+ lpde = (vma->node->offset + vma->node->length - 1) >> mmu->pgt_bits;
+
+ mutex_lock(&nv_subdev(mmu)->mutex);
+ nvkm_vm_unmap_pgt(vm, vma->node->type != mmu->spg_shift, fpde, lpde);
+ nvkm_mm_free(&vm->mm, &vma->node);
+ mutex_unlock(&nv_subdev(mmu)->mutex);
+
+ nvkm_vm_ref(NULL, &vma->vm, NULL);
+}
+
+int
+nvkm_vm_create(struct nvkm_mmu *mmu, u64 offset, u64 length, u64 mm_offset,
+ u32 block, struct nvkm_vm **pvm)
+{
+ struct nvkm_vm *vm;
+ u64 mm_length = (offset + length) - mm_offset;
+ int ret;
+
+ vm = kzalloc(sizeof(*vm), GFP_KERNEL);
+ if (!vm)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&vm->pgd_list);
+ vm->mmu = mmu;
+ kref_init(&vm->refcount);
+ vm->fpde = offset >> (mmu->pgt_bits + 12);
+ vm->lpde = (offset + length - 1) >> (mmu->pgt_bits + 12);
+
+ vm->pgt = vzalloc((vm->lpde - vm->fpde + 1) * sizeof(*vm->pgt));
+ if (!vm->pgt) {
+ kfree(vm);
+ return -ENOMEM;
+ }
+
+ ret = nvkm_mm_init(&vm->mm, mm_offset >> 12, mm_length >> 12,
+ block >> 12);
+ if (ret) {
+ vfree(vm->pgt);
+ kfree(vm);
+ return ret;
+ }
+
+ *pvm = vm;
+
+ return 0;
+}
+
+int
+nvkm_vm_new(struct nvkm_device *device, u64 offset, u64 length, u64 mm_offset,
+ struct nvkm_vm **pvm)
+{
+ struct nvkm_mmu *mmu = nvkm_mmu(device);
+ return mmu->create(mmu, offset, length, mm_offset, pvm);
+}
+
+static int
+nvkm_vm_link(struct nvkm_vm *vm, struct nvkm_gpuobj *pgd)
+{
+ struct nvkm_mmu *mmu = vm->mmu;
+ struct nvkm_vm_pgd *vpgd;
+ int i;
+
+ if (!pgd)
+ return 0;
+
+ vpgd = kzalloc(sizeof(*vpgd), GFP_KERNEL);
+ if (!vpgd)
+ return -ENOMEM;
+
+ nvkm_gpuobj_ref(pgd, &vpgd->obj);
+
+ mutex_lock(&nv_subdev(mmu)->mutex);
+ for (i = vm->fpde; i <= vm->lpde; i++)
+ mmu->map_pgt(pgd, i, vm->pgt[i - vm->fpde].obj);
+ list_add(&vpgd->head, &vm->pgd_list);
+ mutex_unlock(&nv_subdev(mmu)->mutex);
+ return 0;
+}
+
+static void
+nvkm_vm_unlink(struct nvkm_vm *vm, struct nvkm_gpuobj *mpgd)
+{
+ struct nvkm_mmu *mmu = vm->mmu;
+ struct nvkm_vm_pgd *vpgd, *tmp;
+ struct nvkm_gpuobj *pgd = NULL;
+
+ if (!mpgd)
+ return;
+
+ mutex_lock(&nv_subdev(mmu)->mutex);
+ list_for_each_entry_safe(vpgd, tmp, &vm->pgd_list, head) {
+ if (vpgd->obj == mpgd) {
+ pgd = vpgd->obj;
+ list_del(&vpgd->head);
+ kfree(vpgd);
+ break;
+ }
+ }
+ mutex_unlock(&nv_subdev(mmu)->mutex);
+
+ nvkm_gpuobj_ref(NULL, &pgd);
+}
+
+static void
+nvkm_vm_del(struct kref *kref)
+{
+ struct nvkm_vm *vm = container_of(kref, typeof(*vm), refcount);
+ struct nvkm_vm_pgd *vpgd, *tmp;
+
+ list_for_each_entry_safe(vpgd, tmp, &vm->pgd_list, head) {
+ nvkm_vm_unlink(vm, vpgd->obj);
+ }
+
+ nvkm_mm_fini(&vm->mm);
+ vfree(vm->pgt);
+ kfree(vm);
+}
+
+int
+nvkm_vm_ref(struct nvkm_vm *ref, struct nvkm_vm **ptr, struct nvkm_gpuobj *pgd)
+{
+ if (ref) {
+ int ret = nvkm_vm_link(ref, pgd);
+ if (ret)
+ return ret;
+
+ kref_get(&ref->refcount);
+ }
+
+ if (*ptr) {
+ nvkm_vm_unlink(*ptr, pgd);
+ kref_put(&(*ptr)->refcount, nvkm_vm_del);
+ }
+
+ *ptr = ref;
+ return 0;
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/gf100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/gf100.c
new file mode 100644
index 000000000000..294cda37f068
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/gf100.c
@@ -0,0 +1,237 @@
+/*
+ * Copyright 2010 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include <subdev/mmu.h>
+#include <subdev/bar.h>
+#include <subdev/fb.h>
+#include <subdev/ltc.h>
+#include <subdev/timer.h>
+
+#include <core/gpuobj.h>
+
+struct gf100_mmu_priv {
+ struct nvkm_mmu base;
+};
+
+
+/* Map from compressed to corresponding uncompressed storage type.
+ * The value 0xff represents an invalid storage type.
+ */
+const u8 gf100_pte_storage_type_map[256] =
+{
+ 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0xff, 0x01, /* 0x00 */
+ 0x01, 0x01, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0x11, 0xff, 0xff, 0xff, 0xff, 0xff, 0x11, /* 0x10 */
+ 0x11, 0x11, 0x11, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x26, 0x27, /* 0x20 */
+ 0x28, 0x29, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x30 */
+ 0xff, 0xff, 0x26, 0x27, 0x28, 0x29, 0x26, 0x27,
+ 0x28, 0x29, 0xff, 0xff, 0xff, 0xff, 0x46, 0xff, /* 0x40 */
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0x46, 0x46, 0x46, 0x46, 0xff, 0xff, 0xff, /* 0x50 */
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x60 */
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x70 */
+ 0xff, 0xff, 0xff, 0x7b, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7b, 0x7b, /* 0x80 */
+ 0x7b, 0x7b, 0xff, 0x8b, 0x8c, 0x8d, 0x8e, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x90 */
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0x8b, 0x8c, 0x8d, 0x8e, 0xa7, /* 0xa0 */
+ 0xa8, 0xa9, 0xaa, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xb0 */
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa7,
+ 0xa8, 0xa9, 0xaa, 0xc3, 0xff, 0xff, 0xff, 0xff, /* 0xc0 */
+ 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xc3, 0xc3,
+ 0xc3, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xd0 */
+ 0xfe, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfe,
+ 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, /* 0xe0 */
+ 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xff,
+ 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0xf0 */
+ 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xfd, 0xfe, 0xff
+};
+
+
+static void
+gf100_vm_map_pgt(struct nvkm_gpuobj *pgd, u32 index, struct nvkm_gpuobj *pgt[2])
+{
+ u32 pde[2] = { 0, 0 };
+
+ if (pgt[0])
+ pde[1] = 0x00000001 | (pgt[0]->addr >> 8);
+ if (pgt[1])
+ pde[0] = 0x00000001 | (pgt[1]->addr >> 8);
+
+ nv_wo32(pgd, (index * 8) + 0, pde[0]);
+ nv_wo32(pgd, (index * 8) + 4, pde[1]);
+}
+
+static inline u64
+gf100_vm_addr(struct nvkm_vma *vma, u64 phys, u32 memtype, u32 target)
+{
+ phys >>= 8;
+
+ phys |= 0x00000001; /* present */
+ if (vma->access & NV_MEM_ACCESS_SYS)
+ phys |= 0x00000002;
+
+ phys |= ((u64)target << 32);
+ phys |= ((u64)memtype << 36);
+ return phys;
+}
+
+static void
+gf100_vm_map(struct nvkm_vma *vma, struct nvkm_gpuobj *pgt,
+ struct nvkm_mem *mem, u32 pte, u32 cnt, u64 phys, u64 delta)
+{
+ u64 next = 1 << (vma->node->type - 8);
+
+ phys = gf100_vm_addr(vma, phys, mem->memtype, 0);
+ pte <<= 3;
+
+ if (mem->tag) {
+ struct nvkm_ltc *ltc = nvkm_ltc(vma->vm->mmu);
+ u32 tag = mem->tag->offset + (delta >> 17);
+ phys |= (u64)tag << (32 + 12);
+ next |= (u64)1 << (32 + 12);
+ ltc->tags_clear(ltc, tag, cnt);
+ }
+
+ while (cnt--) {
+ nv_wo32(pgt, pte + 0, lower_32_bits(phys));
+ nv_wo32(pgt, pte + 4, upper_32_bits(phys));
+ phys += next;
+ pte += 8;
+ }
+}
+
+static void
+gf100_vm_map_sg(struct nvkm_vma *vma, struct nvkm_gpuobj *pgt,
+ struct nvkm_mem *mem, u32 pte, u32 cnt, dma_addr_t *list)
+{
+ u32 target = (vma->access & NV_MEM_ACCESS_NOSNOOP) ? 7 : 5;
+ /* compressed storage types are invalid for system memory */
+ u32 memtype = gf100_pte_storage_type_map[mem->memtype & 0xff];
+
+ pte <<= 3;
+ while (cnt--) {
+ u64 phys = gf100_vm_addr(vma, *list++, memtype, target);
+ nv_wo32(pgt, pte + 0, lower_32_bits(phys));
+ nv_wo32(pgt, pte + 4, upper_32_bits(phys));
+ pte += 8;
+ }
+}
+
+static void
+gf100_vm_unmap(struct nvkm_gpuobj *pgt, u32 pte, u32 cnt)
+{
+ pte <<= 3;
+ while (cnt--) {
+ nv_wo32(pgt, pte + 0, 0x00000000);
+ nv_wo32(pgt, pte + 4, 0x00000000);
+ pte += 8;
+ }
+}
+
+static void
+gf100_vm_flush(struct nvkm_vm *vm)
+{
+ struct gf100_mmu_priv *priv = (void *)vm->mmu;
+ struct nvkm_bar *bar = nvkm_bar(priv);
+ struct nvkm_vm_pgd *vpgd;
+ u32 type;
+
+ bar->flush(bar);
+
+ type = 0x00000001; /* PAGE_ALL */
+ if (atomic_read(&vm->engref[NVDEV_SUBDEV_BAR]))
+ type |= 0x00000004; /* HUB_ONLY */
+
+ mutex_lock(&nv_subdev(priv)->mutex);
+ list_for_each_entry(vpgd, &vm->pgd_list, head) {
+ /* looks like maybe a "free flush slots" counter, the
+ * faster you write to 0x100cbc to more it decreases
+ */
+ if (!nv_wait_ne(priv, 0x100c80, 0x00ff0000, 0x00000000)) {
+ nv_error(priv, "vm timeout 0: 0x%08x %d\n",
+ nv_rd32(priv, 0x100c80), type);
+ }
+
+ nv_wr32(priv, 0x100cb8, vpgd->obj->addr >> 8);
+ nv_wr32(priv, 0x100cbc, 0x80000000 | type);
+
+ /* wait for flush to be queued? */
+ if (!nv_wait(priv, 0x100c80, 0x00008000, 0x00008000)) {
+ nv_error(priv, "vm timeout 1: 0x%08x %d\n",
+ nv_rd32(priv, 0x100c80), type);
+ }
+ }
+ mutex_unlock(&nv_subdev(priv)->mutex);
+}
+
+static int
+gf100_vm_create(struct nvkm_mmu *mmu, u64 offset, u64 length, u64 mm_offset,
+ struct nvkm_vm **pvm)
+{
+ return nvkm_vm_create(mmu, offset, length, mm_offset, 4096, pvm);
+}
+
+static int
+gf100_mmu_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 size,
+ struct nvkm_object **pobject)
+{
+ struct gf100_mmu_priv *priv;
+ int ret;
+
+ ret = nvkm_mmu_create(parent, engine, oclass, "VM", "vm", &priv);
+ *pobject = nv_object(priv);
+ if (ret)
+ return ret;
+
+ priv->base.limit = 1ULL << 40;
+ priv->base.dma_bits = 40;
+ priv->base.pgt_bits = 27 - 12;
+ priv->base.spg_shift = 12;
+ priv->base.lpg_shift = 17;
+ priv->base.create = gf100_vm_create;
+ priv->base.map_pgt = gf100_vm_map_pgt;
+ priv->base.map = gf100_vm_map;
+ priv->base.map_sg = gf100_vm_map_sg;
+ priv->base.unmap = gf100_vm_unmap;
+ priv->base.flush = gf100_vm_flush;
+ return 0;
+}
+
+struct nvkm_oclass
+gf100_mmu_oclass = {
+ .handle = NV_SUBDEV(MMU, 0xc0),
+ .ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = gf100_mmu_ctor,
+ .dtor = _nvkm_mmu_dtor,
+ .init = _nvkm_mmu_init,
+ .fini = _nvkm_mmu_fini,
+ },
+};
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/nv04.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/nv04.c
new file mode 100644
index 000000000000..fe93ea2711c9
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/nv04.c
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "nv04.h"
+
+#include <core/device.h>
+#include <core/gpuobj.h>
+
+#define NV04_PDMA_SIZE (128 * 1024 * 1024)
+#define NV04_PDMA_PAGE ( 4 * 1024)
+
+/*******************************************************************************
+ * VM map/unmap callbacks
+ ******************************************************************************/
+
+static void
+nv04_vm_map_sg(struct nvkm_vma *vma, struct nvkm_gpuobj *pgt,
+ struct nvkm_mem *mem, u32 pte, u32 cnt, dma_addr_t *list)
+{
+ pte = 0x00008 + (pte * 4);
+ while (cnt) {
+ u32 page = PAGE_SIZE / NV04_PDMA_PAGE;
+ u32 phys = (u32)*list++;
+ while (cnt && page--) {
+ nv_wo32(pgt, pte, phys | 3);
+ phys += NV04_PDMA_PAGE;
+ pte += 4;
+ cnt -= 1;
+ }
+ }
+}
+
+static void
+nv04_vm_unmap(struct nvkm_gpuobj *pgt, u32 pte, u32 cnt)
+{
+ pte = 0x00008 + (pte * 4);
+ while (cnt--) {
+ nv_wo32(pgt, pte, 0x00000000);
+ pte += 4;
+ }
+}
+
+static void
+nv04_vm_flush(struct nvkm_vm *vm)
+{
+}
+
+/*******************************************************************************
+ * VM object
+ ******************************************************************************/
+
+int
+nv04_vm_create(struct nvkm_mmu *mmu, u64 offset, u64 length, u64 mmstart,
+ struct nvkm_vm **pvm)
+{
+ return -EINVAL;
+}
+
+/*******************************************************************************
+ * MMU subdev
+ ******************************************************************************/
+
+static int
+nv04_mmu_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 size,
+ struct nvkm_object **pobject)
+{
+ struct nv04_mmu_priv *priv;
+ struct nvkm_gpuobj *dma;
+ int ret;
+
+ ret = nvkm_mmu_create(parent, engine, oclass, "PCIGART",
+ "pcigart", &priv);
+ *pobject = nv_object(priv);
+ if (ret)
+ return ret;
+
+ priv->base.create = nv04_vm_create;
+ priv->base.limit = NV04_PDMA_SIZE;
+ priv->base.dma_bits = 32;
+ priv->base.pgt_bits = 32 - 12;
+ priv->base.spg_shift = 12;
+ priv->base.lpg_shift = 12;
+ priv->base.map_sg = nv04_vm_map_sg;
+ priv->base.unmap = nv04_vm_unmap;
+ priv->base.flush = nv04_vm_flush;
+
+ ret = nvkm_vm_create(&priv->base, 0, NV04_PDMA_SIZE, 0, 4096,
+ &priv->vm);
+ if (ret)
+ return ret;
+
+ ret = nvkm_gpuobj_new(nv_object(priv), NULL,
+ (NV04_PDMA_SIZE / NV04_PDMA_PAGE) * 4 + 8,
+ 16, NVOBJ_FLAG_ZERO_ALLOC,
+ &priv->vm->pgt[0].obj[0]);
+ dma = priv->vm->pgt[0].obj[0];
+ priv->vm->pgt[0].refcount[0] = 1;
+ if (ret)
+ return ret;
+
+ nv_wo32(dma, 0x00000, 0x0002103d); /* PCI, RW, PT, !LN */
+ nv_wo32(dma, 0x00004, NV04_PDMA_SIZE - 1);
+ return 0;
+}
+
+void
+nv04_mmu_dtor(struct nvkm_object *object)
+{
+ struct nv04_mmu_priv *priv = (void *)object;
+ if (priv->vm) {
+ nvkm_gpuobj_ref(NULL, &priv->vm->pgt[0].obj[0]);
+ nvkm_vm_ref(NULL, &priv->vm, NULL);
+ }
+ if (priv->nullp) {
+ pci_free_consistent(nv_device(priv)->pdev, 16 * 1024,
+ priv->nullp, priv->null);
+ }
+ nvkm_mmu_destroy(&priv->base);
+}
+
+struct nvkm_oclass
+nv04_mmu_oclass = {
+ .handle = NV_SUBDEV(MMU, 0x04),
+ .ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv04_mmu_ctor,
+ .dtor = nv04_mmu_dtor,
+ .init = _nvkm_mmu_init,
+ .fini = _nvkm_mmu_fini,
+ },
+};
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/nv04.h b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/nv04.h
new file mode 100644
index 000000000000..7bf6f4b38f1d
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/nv04.h
@@ -0,0 +1,19 @@
+#ifndef __NV04_MMU_PRIV__
+#define __NV04_MMU_PRIV__
+
+#include <subdev/mmu.h>
+
+struct nv04_mmu_priv {
+ struct nvkm_mmu base;
+ struct nvkm_vm *vm;
+ dma_addr_t null;
+ void *nullp;
+};
+
+static inline struct nv04_mmu_priv *
+nv04_mmu(void *obj)
+{
+ return (void *)nvkm_mmu(obj);
+}
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/nv41.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/nv41.c
new file mode 100644
index 000000000000..61ee3ab11660
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/nv41.c
@@ -0,0 +1,157 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "nv04.h"
+
+#include <core/device.h>
+#include <core/gpuobj.h>
+#include <core/option.h>
+#include <subdev/timer.h>
+
+#define NV41_GART_SIZE (512 * 1024 * 1024)
+#define NV41_GART_PAGE ( 4 * 1024)
+
+/*******************************************************************************
+ * VM map/unmap callbacks
+ ******************************************************************************/
+
+static void
+nv41_vm_map_sg(struct nvkm_vma *vma, struct nvkm_gpuobj *pgt,
+ struct nvkm_mem *mem, u32 pte, u32 cnt, dma_addr_t *list)
+{
+ pte = pte * 4;
+ while (cnt) {
+ u32 page = PAGE_SIZE / NV41_GART_PAGE;
+ u64 phys = (u64)*list++;
+ while (cnt && page--) {
+ nv_wo32(pgt, pte, (phys >> 7) | 1);
+ phys += NV41_GART_PAGE;
+ pte += 4;
+ cnt -= 1;
+ }
+ }
+}
+
+static void
+nv41_vm_unmap(struct nvkm_gpuobj *pgt, u32 pte, u32 cnt)
+{
+ pte = pte * 4;
+ while (cnt--) {
+ nv_wo32(pgt, pte, 0x00000000);
+ pte += 4;
+ }
+}
+
+static void
+nv41_vm_flush(struct nvkm_vm *vm)
+{
+ struct nv04_mmu_priv *priv = (void *)vm->mmu;
+
+ mutex_lock(&nv_subdev(priv)->mutex);
+ nv_wr32(priv, 0x100810, 0x00000022);
+ if (!nv_wait(priv, 0x100810, 0x00000020, 0x00000020)) {
+ nv_warn(priv, "flush timeout, 0x%08x\n",
+ nv_rd32(priv, 0x100810));
+ }
+ nv_wr32(priv, 0x100810, 0x00000000);
+ mutex_unlock(&nv_subdev(priv)->mutex);
+}
+
+/*******************************************************************************
+ * MMU subdev
+ ******************************************************************************/
+
+static int
+nv41_mmu_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 size,
+ struct nvkm_object **pobject)
+{
+ struct nvkm_device *device = nv_device(parent);
+ struct nv04_mmu_priv *priv;
+ int ret;
+
+ if (pci_find_capability(device->pdev, PCI_CAP_ID_AGP) ||
+ !nvkm_boolopt(device->cfgopt, "NvPCIE", true)) {
+ return nvkm_object_ctor(parent, engine, &nv04_mmu_oclass,
+ data, size, pobject);
+ }
+
+ ret = nvkm_mmu_create(parent, engine, oclass, "PCIEGART",
+ "pciegart", &priv);
+ *pobject = nv_object(priv);
+ if (ret)
+ return ret;
+
+ priv->base.create = nv04_vm_create;
+ priv->base.limit = NV41_GART_SIZE;
+ priv->base.dma_bits = 39;
+ priv->base.pgt_bits = 32 - 12;
+ priv->base.spg_shift = 12;
+ priv->base.lpg_shift = 12;
+ priv->base.map_sg = nv41_vm_map_sg;
+ priv->base.unmap = nv41_vm_unmap;
+ priv->base.flush = nv41_vm_flush;
+
+ ret = nvkm_vm_create(&priv->base, 0, NV41_GART_SIZE, 0, 4096,
+ &priv->vm);
+ if (ret)
+ return ret;
+
+ ret = nvkm_gpuobj_new(nv_object(priv), NULL,
+ (NV41_GART_SIZE / NV41_GART_PAGE) * 4, 16,
+ NVOBJ_FLAG_ZERO_ALLOC,
+ &priv->vm->pgt[0].obj[0]);
+ priv->vm->pgt[0].refcount[0] = 1;
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int
+nv41_mmu_init(struct nvkm_object *object)
+{
+ struct nv04_mmu_priv *priv = (void *)object;
+ struct nvkm_gpuobj *dma = priv->vm->pgt[0].obj[0];
+ int ret;
+
+ ret = nvkm_mmu_init(&priv->base);
+ if (ret)
+ return ret;
+
+ nv_wr32(priv, 0x100800, dma->addr | 0x00000002);
+ nv_mask(priv, 0x10008c, 0x00000100, 0x00000100);
+ nv_wr32(priv, 0x100820, 0x00000000);
+ return 0;
+}
+
+struct nvkm_oclass
+nv41_mmu_oclass = {
+ .handle = NV_SUBDEV(MMU, 0x41),
+ .ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv41_mmu_ctor,
+ .dtor = nv04_mmu_dtor,
+ .init = nv41_mmu_init,
+ .fini = _nvkm_mmu_fini,
+ },
+};
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/nv44.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/nv44.c
new file mode 100644
index 000000000000..b90ded1887aa
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/nv44.c
@@ -0,0 +1,247 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "nv04.h"
+
+#include <core/device.h>
+#include <core/gpuobj.h>
+#include <core/option.h>
+#include <subdev/timer.h>
+
+#define NV44_GART_SIZE (512 * 1024 * 1024)
+#define NV44_GART_PAGE ( 4 * 1024)
+
+/*******************************************************************************
+ * VM map/unmap callbacks
+ ******************************************************************************/
+
+static void
+nv44_vm_fill(struct nvkm_gpuobj *pgt, dma_addr_t null,
+ dma_addr_t *list, u32 pte, u32 cnt)
+{
+ u32 base = (pte << 2) & ~0x0000000f;
+ u32 tmp[4];
+
+ tmp[0] = nv_ro32(pgt, base + 0x0);
+ tmp[1] = nv_ro32(pgt, base + 0x4);
+ tmp[2] = nv_ro32(pgt, base + 0x8);
+ tmp[3] = nv_ro32(pgt, base + 0xc);
+
+ while (cnt--) {
+ u32 addr = list ? (*list++ >> 12) : (null >> 12);
+ switch (pte++ & 0x3) {
+ case 0:
+ tmp[0] &= ~0x07ffffff;
+ tmp[0] |= addr;
+ break;
+ case 1:
+ tmp[0] &= ~0xf8000000;
+ tmp[0] |= addr << 27;
+ tmp[1] &= ~0x003fffff;
+ tmp[1] |= addr >> 5;
+ break;
+ case 2:
+ tmp[1] &= ~0xffc00000;
+ tmp[1] |= addr << 22;
+ tmp[2] &= ~0x0001ffff;
+ tmp[2] |= addr >> 10;
+ break;
+ case 3:
+ tmp[2] &= ~0xfffe0000;
+ tmp[2] |= addr << 17;
+ tmp[3] &= ~0x00000fff;
+ tmp[3] |= addr >> 15;
+ break;
+ }
+ }
+
+ nv_wo32(pgt, base + 0x0, tmp[0]);
+ nv_wo32(pgt, base + 0x4, tmp[1]);
+ nv_wo32(pgt, base + 0x8, tmp[2]);
+ nv_wo32(pgt, base + 0xc, tmp[3] | 0x40000000);
+}
+
+static void
+nv44_vm_map_sg(struct nvkm_vma *vma, struct nvkm_gpuobj *pgt,
+ struct nvkm_mem *mem, u32 pte, u32 cnt, dma_addr_t *list)
+{
+ struct nv04_mmu_priv *priv = (void *)vma->vm->mmu;
+ u32 tmp[4];
+ int i;
+
+ if (pte & 3) {
+ u32 max = 4 - (pte & 3);
+ u32 part = (cnt > max) ? max : cnt;
+ nv44_vm_fill(pgt, priv->null, list, pte, part);
+ pte += part;
+ list += part;
+ cnt -= part;
+ }
+
+ while (cnt >= 4) {
+ for (i = 0; i < 4; i++)
+ tmp[i] = *list++ >> 12;
+ nv_wo32(pgt, pte++ * 4, tmp[0] >> 0 | tmp[1] << 27);
+ nv_wo32(pgt, pte++ * 4, tmp[1] >> 5 | tmp[2] << 22);
+ nv_wo32(pgt, pte++ * 4, tmp[2] >> 10 | tmp[3] << 17);
+ nv_wo32(pgt, pte++ * 4, tmp[3] >> 15 | 0x40000000);
+ cnt -= 4;
+ }
+
+ if (cnt)
+ nv44_vm_fill(pgt, priv->null, list, pte, cnt);
+}
+
+static void
+nv44_vm_unmap(struct nvkm_gpuobj *pgt, u32 pte, u32 cnt)
+{
+ struct nv04_mmu_priv *priv = (void *)nvkm_mmu(pgt);
+
+ if (pte & 3) {
+ u32 max = 4 - (pte & 3);
+ u32 part = (cnt > max) ? max : cnt;
+ nv44_vm_fill(pgt, priv->null, NULL, pte, part);
+ pte += part;
+ cnt -= part;
+ }
+
+ while (cnt >= 4) {
+ nv_wo32(pgt, pte++ * 4, 0x00000000);
+ nv_wo32(pgt, pte++ * 4, 0x00000000);
+ nv_wo32(pgt, pte++ * 4, 0x00000000);
+ nv_wo32(pgt, pte++ * 4, 0x00000000);
+ cnt -= 4;
+ }
+
+ if (cnt)
+ nv44_vm_fill(pgt, priv->null, NULL, pte, cnt);
+}
+
+static void
+nv44_vm_flush(struct nvkm_vm *vm)
+{
+ struct nv04_mmu_priv *priv = (void *)vm->mmu;
+ nv_wr32(priv, 0x100814, priv->base.limit - NV44_GART_PAGE);
+ nv_wr32(priv, 0x100808, 0x00000020);
+ if (!nv_wait(priv, 0x100808, 0x00000001, 0x00000001))
+ nv_error(priv, "timeout: 0x%08x\n", nv_rd32(priv, 0x100808));
+ nv_wr32(priv, 0x100808, 0x00000000);
+}
+
+/*******************************************************************************
+ * MMU subdev
+ ******************************************************************************/
+
+static int
+nv44_mmu_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 size,
+ struct nvkm_object **pobject)
+{
+ struct nvkm_device *device = nv_device(parent);
+ struct nv04_mmu_priv *priv;
+ int ret;
+
+ if (pci_find_capability(device->pdev, PCI_CAP_ID_AGP) ||
+ !nvkm_boolopt(device->cfgopt, "NvPCIE", true)) {
+ return nvkm_object_ctor(parent, engine, &nv04_mmu_oclass,
+ data, size, pobject);
+ }
+
+ ret = nvkm_mmu_create(parent, engine, oclass, "PCIEGART",
+ "pciegart", &priv);
+ *pobject = nv_object(priv);
+ if (ret)
+ return ret;
+
+ priv->base.create = nv04_vm_create;
+ priv->base.limit = NV44_GART_SIZE;
+ priv->base.dma_bits = 39;
+ priv->base.pgt_bits = 32 - 12;
+ priv->base.spg_shift = 12;
+ priv->base.lpg_shift = 12;
+ priv->base.map_sg = nv44_vm_map_sg;
+ priv->base.unmap = nv44_vm_unmap;
+ priv->base.flush = nv44_vm_flush;
+
+ priv->nullp = pci_alloc_consistent(device->pdev, 16 * 1024, &priv->null);
+ if (!priv->nullp) {
+ nv_error(priv, "unable to allocate dummy pages\n");
+ return -ENOMEM;
+ }
+
+ ret = nvkm_vm_create(&priv->base, 0, NV44_GART_SIZE, 0, 4096,
+ &priv->vm);
+ if (ret)
+ return ret;
+
+ ret = nvkm_gpuobj_new(nv_object(priv), NULL,
+ (NV44_GART_SIZE / NV44_GART_PAGE) * 4,
+ 512 * 1024, NVOBJ_FLAG_ZERO_ALLOC,
+ &priv->vm->pgt[0].obj[0]);
+ priv->vm->pgt[0].refcount[0] = 1;
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int
+nv44_mmu_init(struct nvkm_object *object)
+{
+ struct nv04_mmu_priv *priv = (void *)object;
+ struct nvkm_gpuobj *gart = priv->vm->pgt[0].obj[0];
+ u32 addr;
+ int ret;
+
+ ret = nvkm_mmu_init(&priv->base);
+ if (ret)
+ return ret;
+
+ /* calculate vram address of this PRAMIN block, object must be
+ * allocated on 512KiB alignment, and not exceed a total size
+ * of 512KiB for this to work correctly
+ */
+ addr = nv_rd32(priv, 0x10020c);
+ addr -= ((gart->addr >> 19) + 1) << 19;
+
+ nv_wr32(priv, 0x100850, 0x80000000);
+ nv_wr32(priv, 0x100818, priv->null);
+ nv_wr32(priv, 0x100804, NV44_GART_SIZE);
+ nv_wr32(priv, 0x100850, 0x00008000);
+ nv_mask(priv, 0x10008c, 0x00000200, 0x00000200);
+ nv_wr32(priv, 0x100820, 0x00000000);
+ nv_wr32(priv, 0x10082c, 0x00000001);
+ nv_wr32(priv, 0x100800, addr | 0x00000010);
+ return 0;
+}
+
+struct nvkm_oclass
+nv44_mmu_oclass = {
+ .handle = NV_SUBDEV(MMU, 0x44),
+ .ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv44_mmu_ctor,
+ .dtor = nv04_mmu_dtor,
+ .init = nv44_mmu_init,
+ .fini = _nvkm_mmu_fini,
+ },
+};
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/nv50.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/nv50.c
new file mode 100644
index 000000000000..b83550fa7f96
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/nv50.c
@@ -0,0 +1,241 @@
+/*
+ * Copyright 2010 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include <subdev/mmu.h>
+#include <subdev/bar.h>
+#include <subdev/fb.h>
+#include <subdev/timer.h>
+
+#include <core/engine.h>
+#include <core/gpuobj.h>
+
+struct nv50_mmu_priv {
+ struct nvkm_mmu base;
+};
+
+static void
+nv50_vm_map_pgt(struct nvkm_gpuobj *pgd, u32 pde, struct nvkm_gpuobj *pgt[2])
+{
+ u64 phys = 0xdeadcafe00000000ULL;
+ u32 coverage = 0;
+
+ if (pgt[0]) {
+ phys = 0x00000003 | pgt[0]->addr; /* present, 4KiB pages */
+ coverage = (pgt[0]->size >> 3) << 12;
+ } else
+ if (pgt[1]) {
+ phys = 0x00000001 | pgt[1]->addr; /* present */
+ coverage = (pgt[1]->size >> 3) << 16;
+ }
+
+ if (phys & 1) {
+ if (coverage <= 32 * 1024 * 1024)
+ phys |= 0x60;
+ else if (coverage <= 64 * 1024 * 1024)
+ phys |= 0x40;
+ else if (coverage <= 128 * 1024 * 1024)
+ phys |= 0x20;
+ }
+
+ nv_wo32(pgd, (pde * 8) + 0, lower_32_bits(phys));
+ nv_wo32(pgd, (pde * 8) + 4, upper_32_bits(phys));
+}
+
+static inline u64
+vm_addr(struct nvkm_vma *vma, u64 phys, u32 memtype, u32 target)
+{
+ phys |= 1; /* present */
+ phys |= (u64)memtype << 40;
+ phys |= target << 4;
+ if (vma->access & NV_MEM_ACCESS_SYS)
+ phys |= (1 << 6);
+ if (!(vma->access & NV_MEM_ACCESS_WO))
+ phys |= (1 << 3);
+ return phys;
+}
+
+static void
+nv50_vm_map(struct nvkm_vma *vma, struct nvkm_gpuobj *pgt,
+ struct nvkm_mem *mem, u32 pte, u32 cnt, u64 phys, u64 delta)
+{
+ u32 comp = (mem->memtype & 0x180) >> 7;
+ u32 block, target;
+ int i;
+
+ /* IGPs don't have real VRAM, re-target to stolen system memory */
+ target = 0;
+ if (nvkm_fb(vma->vm->mmu)->ram->stolen) {
+ phys += nvkm_fb(vma->vm->mmu)->ram->stolen;
+ target = 3;
+ }
+
+ phys = vm_addr(vma, phys, mem->memtype, target);
+ pte <<= 3;
+ cnt <<= 3;
+
+ while (cnt) {
+ u32 offset_h = upper_32_bits(phys);
+ u32 offset_l = lower_32_bits(phys);
+
+ for (i = 7; i >= 0; i--) {
+ block = 1 << (i + 3);
+ if (cnt >= block && !(pte & (block - 1)))
+ break;
+ }
+ offset_l |= (i << 7);
+
+ phys += block << (vma->node->type - 3);
+ cnt -= block;
+ if (comp) {
+ u32 tag = mem->tag->offset + ((delta >> 16) * comp);
+ offset_h |= (tag << 17);
+ delta += block << (vma->node->type - 3);
+ }
+
+ while (block) {
+ nv_wo32(pgt, pte + 0, offset_l);
+ nv_wo32(pgt, pte + 4, offset_h);
+ pte += 8;
+ block -= 8;
+ }
+ }
+}
+
+static void
+nv50_vm_map_sg(struct nvkm_vma *vma, struct nvkm_gpuobj *pgt,
+ struct nvkm_mem *mem, u32 pte, u32 cnt, dma_addr_t *list)
+{
+ u32 target = (vma->access & NV_MEM_ACCESS_NOSNOOP) ? 3 : 2;
+ pte <<= 3;
+ while (cnt--) {
+ u64 phys = vm_addr(vma, (u64)*list++, mem->memtype, target);
+ nv_wo32(pgt, pte + 0, lower_32_bits(phys));
+ nv_wo32(pgt, pte + 4, upper_32_bits(phys));
+ pte += 8;
+ }
+}
+
+static void
+nv50_vm_unmap(struct nvkm_gpuobj *pgt, u32 pte, u32 cnt)
+{
+ pte <<= 3;
+ while (cnt--) {
+ nv_wo32(pgt, pte + 0, 0x00000000);
+ nv_wo32(pgt, pte + 4, 0x00000000);
+ pte += 8;
+ }
+}
+
+static void
+nv50_vm_flush(struct nvkm_vm *vm)
+{
+ struct nv50_mmu_priv *priv = (void *)vm->mmu;
+ struct nvkm_bar *bar = nvkm_bar(priv);
+ struct nvkm_engine *engine;
+ int i, vme;
+
+ bar->flush(bar);
+
+ mutex_lock(&nv_subdev(priv)->mutex);
+ for (i = 0; i < NVDEV_SUBDEV_NR; i++) {
+ if (!atomic_read(&vm->engref[i]))
+ continue;
+
+ /* unfortunate hw bug workaround... */
+ engine = nvkm_engine(priv, i);
+ if (engine && engine->tlb_flush) {
+ engine->tlb_flush(engine);
+ continue;
+ }
+
+ switch (i) {
+ case NVDEV_ENGINE_GR : vme = 0x00; break;
+ case NVDEV_ENGINE_VP :
+ case NVDEV_ENGINE_MSPDEC: vme = 0x01; break;
+ case NVDEV_SUBDEV_BAR : vme = 0x06; break;
+ case NVDEV_ENGINE_MSPPP :
+ case NVDEV_ENGINE_MPEG : vme = 0x08; break;
+ case NVDEV_ENGINE_BSP :
+ case NVDEV_ENGINE_MSVLD : vme = 0x09; break;
+ case NVDEV_ENGINE_CIPHER:
+ case NVDEV_ENGINE_SEC : vme = 0x0a; break;
+ case NVDEV_ENGINE_CE0 : vme = 0x0d; break;
+ default:
+ continue;
+ }
+
+ nv_wr32(priv, 0x100c80, (vme << 16) | 1);
+ if (!nv_wait(priv, 0x100c80, 0x00000001, 0x00000000))
+ nv_error(priv, "vm flush timeout: engine %d\n", vme);
+ }
+ mutex_unlock(&nv_subdev(priv)->mutex);
+}
+
+static int
+nv50_vm_create(struct nvkm_mmu *mmu, u64 offset, u64 length,
+ u64 mm_offset, struct nvkm_vm **pvm)
+{
+ u32 block = (1 << (mmu->pgt_bits + 12));
+ if (block > length)
+ block = length;
+
+ return nvkm_vm_create(mmu, offset, length, mm_offset, block, pvm);
+}
+
+static int
+nv50_mmu_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 size,
+ struct nvkm_object **pobject)
+{
+ struct nv50_mmu_priv *priv;
+ int ret;
+
+ ret = nvkm_mmu_create(parent, engine, oclass, "VM", "vm", &priv);
+ *pobject = nv_object(priv);
+ if (ret)
+ return ret;
+
+ priv->base.limit = 1ULL << 40;
+ priv->base.dma_bits = 40;
+ priv->base.pgt_bits = 29 - 12;
+ priv->base.spg_shift = 12;
+ priv->base.lpg_shift = 16;
+ priv->base.create = nv50_vm_create;
+ priv->base.map_pgt = nv50_vm_map_pgt;
+ priv->base.map = nv50_vm_map;
+ priv->base.map_sg = nv50_vm_map_sg;
+ priv->base.unmap = nv50_vm_unmap;
+ priv->base.flush = nv50_vm_flush;
+ return 0;
+}
+
+struct nvkm_oclass
+nv50_mmu_oclass = {
+ .handle = NV_SUBDEV(MMU, 0x50),
+ .ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv50_mmu_ctor,
+ .dtor = _nvkm_mmu_dtor,
+ .init = _nvkm_mmu_init,
+ .fini = _nvkm_mmu_fini,
+ },
+};
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mxm/Kbuild b/drivers/gpu/drm/nouveau/nvkm/subdev/mxm/Kbuild
new file mode 100644
index 000000000000..1a479e050b54
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mxm/Kbuild
@@ -0,0 +1,3 @@
+nvkm-y += nvkm/subdev/mxm/base.o
+nvkm-y += nvkm/subdev/mxm/mxms.o
+nvkm-y += nvkm/subdev/mxm/nv50.o
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mxm/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mxm/base.c
new file mode 100644
index 000000000000..0ca9dcabb6d3
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mxm/base.c
@@ -0,0 +1,271 @@
+/*
+ * Copyright 2011 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "mxms.h"
+
+#include <core/device.h>
+#include <core/option.h>
+#include <subdev/bios.h>
+#include <subdev/bios/mxm.h>
+#include <subdev/i2c.h>
+
+static bool
+mxm_shadow_rom_fetch(struct nvkm_i2c_port *i2c, u8 addr,
+ u8 offset, u8 size, u8 *data)
+{
+ struct i2c_msg msgs[] = {
+ { .addr = addr, .flags = 0, .len = 1, .buf = &offset },
+ { .addr = addr, .flags = I2C_M_RD, .len = size, .buf = data, },
+ };
+
+ return i2c_transfer(&i2c->adapter, msgs, 2) == 2;
+}
+
+static bool
+mxm_shadow_rom(struct nvkm_mxm *mxm, u8 version)
+{
+ struct nvkm_bios *bios = nvkm_bios(mxm);
+ struct nvkm_i2c *i2c = nvkm_i2c(mxm);
+ struct nvkm_i2c_port *port = NULL;
+ u8 i2cidx, mxms[6], addr, size;
+
+ i2cidx = mxm_ddc_map(bios, 1 /* LVDS_DDC */) & 0x0f;
+ if (i2cidx < 0x0f)
+ port = i2c->find(i2c, i2cidx);
+ if (!port)
+ return false;
+
+ addr = 0x54;
+ if (!mxm_shadow_rom_fetch(port, addr, 0, 6, mxms)) {
+ addr = 0x56;
+ if (!mxm_shadow_rom_fetch(port, addr, 0, 6, mxms))
+ return false;
+ }
+
+ mxm->mxms = mxms;
+ size = mxms_headerlen(mxm) + mxms_structlen(mxm);
+ mxm->mxms = kmalloc(size, GFP_KERNEL);
+
+ if (mxm->mxms &&
+ mxm_shadow_rom_fetch(port, addr, 0, size, mxm->mxms))
+ return true;
+
+ kfree(mxm->mxms);
+ mxm->mxms = NULL;
+ return false;
+}
+
+#if defined(CONFIG_ACPI)
+static bool
+mxm_shadow_dsm(struct nvkm_mxm *mxm, u8 version)
+{
+ struct nvkm_device *device = nv_device(mxm);
+ static char muid[] = {
+ 0x00, 0xA4, 0x04, 0x40, 0x7D, 0x91, 0xF2, 0x4C,
+ 0xB8, 0x9C, 0x79, 0xB6, 0x2F, 0xD5, 0x56, 0x65
+ };
+ u32 mxms_args[] = { 0x00000000 };
+ union acpi_object argv4 = {
+ .buffer.type = ACPI_TYPE_BUFFER,
+ .buffer.length = sizeof(mxms_args),
+ .buffer.pointer = (char *)mxms_args,
+ };
+ union acpi_object *obj;
+ acpi_handle handle;
+ int rev;
+
+ handle = ACPI_HANDLE(nv_device_base(device));
+ if (!handle)
+ return false;
+
+ /*
+ * spec says this can be zero to mean "highest revision", but
+ * of course there's at least one bios out there which fails
+ * unless you pass in exactly the version it supports..
+ */
+ rev = (version & 0xf0) << 4 | (version & 0x0f);
+ obj = acpi_evaluate_dsm(handle, muid, rev, 0x00000010, &argv4);
+ if (!obj) {
+ nv_debug(mxm, "DSM MXMS failed\n");
+ return false;
+ }
+
+ if (obj->type == ACPI_TYPE_BUFFER) {
+ mxm->mxms = kmemdup(obj->buffer.pointer,
+ obj->buffer.length, GFP_KERNEL);
+ } else if (obj->type == ACPI_TYPE_INTEGER) {
+ nv_debug(mxm, "DSM MXMS returned 0x%llx\n", obj->integer.value);
+ }
+
+ ACPI_FREE(obj);
+ return mxm->mxms != NULL;
+}
+#endif
+
+#if defined(CONFIG_ACPI_WMI) || defined(CONFIG_ACPI_WMI_MODULE)
+
+#define WMI_WMMX_GUID "F6CB5C3C-9CAE-4EBD-B577-931EA32A2CC0"
+
+static u8
+wmi_wmmx_mxmi(struct nvkm_mxm *mxm, u8 version)
+{
+ u32 mxmi_args[] = { 0x494D584D /* MXMI */, version, 0 };
+ struct acpi_buffer args = { sizeof(mxmi_args), mxmi_args };
+ struct acpi_buffer retn = { ACPI_ALLOCATE_BUFFER, NULL };
+ union acpi_object *obj;
+ acpi_status status;
+
+ status = wmi_evaluate_method(WMI_WMMX_GUID, 0, 0, &args, &retn);
+ if (ACPI_FAILURE(status)) {
+ nv_debug(mxm, "WMMX MXMI returned %d\n", status);
+ return 0x00;
+ }
+
+ obj = retn.pointer;
+ if (obj->type == ACPI_TYPE_INTEGER) {
+ version = obj->integer.value;
+ nv_debug(mxm, "WMMX MXMI version %d.%d\n",
+ (version >> 4), version & 0x0f);
+ } else {
+ version = 0;
+ nv_debug(mxm, "WMMX MXMI returned non-integer\n");
+ }
+
+ kfree(obj);
+ return version;
+}
+
+static bool
+mxm_shadow_wmi(struct nvkm_mxm *mxm, u8 version)
+{
+ u32 mxms_args[] = { 0x534D584D /* MXMS */, version, 0 };
+ struct acpi_buffer args = { sizeof(mxms_args), mxms_args };
+ struct acpi_buffer retn = { ACPI_ALLOCATE_BUFFER, NULL };
+ union acpi_object *obj;
+ acpi_status status;
+
+ if (!wmi_has_guid(WMI_WMMX_GUID)) {
+ nv_debug(mxm, "WMMX GUID not found\n");
+ return false;
+ }
+
+ mxms_args[1] = wmi_wmmx_mxmi(mxm, 0x00);
+ if (!mxms_args[1])
+ mxms_args[1] = wmi_wmmx_mxmi(mxm, version);
+ if (!mxms_args[1])
+ return false;
+
+ status = wmi_evaluate_method(WMI_WMMX_GUID, 0, 0, &args, &retn);
+ if (ACPI_FAILURE(status)) {
+ nv_debug(mxm, "WMMX MXMS returned %d\n", status);
+ return false;
+ }
+
+ obj = retn.pointer;
+ if (obj->type == ACPI_TYPE_BUFFER) {
+ mxm->mxms = kmemdup(obj->buffer.pointer,
+ obj->buffer.length, GFP_KERNEL);
+ }
+
+ kfree(obj);
+ return mxm->mxms != NULL;
+}
+#endif
+
+static struct mxm_shadow_h {
+ const char *name;
+ bool (*exec)(struct nvkm_mxm *, u8 version);
+} _mxm_shadow[] = {
+ { "ROM", mxm_shadow_rom },
+#if defined(CONFIG_ACPI)
+ { "DSM", mxm_shadow_dsm },
+#endif
+#if defined(CONFIG_ACPI_WMI) || defined(CONFIG_ACPI_WMI_MODULE)
+ { "WMI", mxm_shadow_wmi },
+#endif
+ {}
+};
+
+static int
+mxm_shadow(struct nvkm_mxm *mxm, u8 version)
+{
+ struct mxm_shadow_h *shadow = _mxm_shadow;
+ do {
+ nv_debug(mxm, "checking %s\n", shadow->name);
+ if (shadow->exec(mxm, version)) {
+ if (mxms_valid(mxm))
+ return 0;
+ kfree(mxm->mxms);
+ mxm->mxms = NULL;
+ }
+ } while ((++shadow)->name);
+ return -ENOENT;
+}
+
+int
+nvkm_mxm_create_(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, int length, void **pobject)
+{
+ struct nvkm_device *device = nv_device(parent);
+ struct nvkm_bios *bios = nvkm_bios(device);
+ struct nvkm_mxm *mxm;
+ u8 ver, len;
+ u16 data;
+ int ret;
+
+ ret = nvkm_subdev_create_(parent, engine, oclass, 0, "MXM", "mxm",
+ length, pobject);
+ mxm = *pobject;
+ if (ret)
+ return ret;
+
+ data = mxm_table(bios, &ver, &len);
+ if (!data || !(ver = nv_ro08(bios, data))) {
+ nv_debug(mxm, "no VBIOS data, nothing to do\n");
+ return 0;
+ }
+
+ nv_info(mxm, "BIOS version %d.%d\n", ver >> 4, ver & 0x0f);
+
+ if (mxm_shadow(mxm, ver)) {
+ nv_info(mxm, "failed to locate valid SIS\n");
+#if 0
+ /* we should, perhaps, fall back to some kind of limited
+ * mode here if the x86 vbios hasn't already done the
+ * work for us (so we prevent loading with completely
+ * whacked vbios tables).
+ */
+ return -EINVAL;
+#else
+ return 0;
+#endif
+ }
+
+ nv_info(mxm, "MXMS Version %d.%d\n",
+ mxms_version(mxm) >> 8, mxms_version(mxm) & 0xff);
+ mxms_foreach(mxm, 0, NULL, NULL);
+
+ if (nvkm_boolopt(device->cfgopt, "NvMXMDCB", true))
+ mxm->action |= MXM_SANITISE_DCB;
+ return 0;
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mxm/mxms.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mxm/mxms.c
new file mode 100644
index 000000000000..a9b1d63fed58
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mxm/mxms.c
@@ -0,0 +1,191 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "mxms.h"
+
+#define ROM16(x) le16_to_cpu(*(u16 *)&(x))
+#define ROM32(x) le32_to_cpu(*(u32 *)&(x))
+
+static u8 *
+mxms_data(struct nvkm_mxm *mxm)
+{
+ return mxm->mxms;
+
+}
+
+u16
+mxms_version(struct nvkm_mxm *mxm)
+{
+ u8 *mxms = mxms_data(mxm);
+ u16 version = (mxms[4] << 8) | mxms[5];
+ switch (version ) {
+ case 0x0200:
+ case 0x0201:
+ case 0x0300:
+ return version;
+ default:
+ break;
+ }
+
+ nv_debug(mxm, "unknown version %d.%d\n", mxms[4], mxms[5]);
+ return 0x0000;
+}
+
+u16
+mxms_headerlen(struct nvkm_mxm *mxm)
+{
+ return 8;
+}
+
+u16
+mxms_structlen(struct nvkm_mxm *mxm)
+{
+ return *(u16 *)&mxms_data(mxm)[6];
+}
+
+bool
+mxms_checksum(struct nvkm_mxm *mxm)
+{
+ u16 size = mxms_headerlen(mxm) + mxms_structlen(mxm);
+ u8 *mxms = mxms_data(mxm), sum = 0;
+ while (size--)
+ sum += *mxms++;
+ if (sum) {
+ nv_debug(mxm, "checksum invalid\n");
+ return false;
+ }
+ return true;
+}
+
+bool
+mxms_valid(struct nvkm_mxm *mxm)
+{
+ u8 *mxms = mxms_data(mxm);
+ if (*(u32 *)mxms != 0x5f4d584d) {
+ nv_debug(mxm, "signature invalid\n");
+ return false;
+ }
+
+ if (!mxms_version(mxm) || !mxms_checksum(mxm))
+ return false;
+
+ return true;
+}
+
+bool
+mxms_foreach(struct nvkm_mxm *mxm, u8 types,
+ bool (*exec)(struct nvkm_mxm *, u8 *, void *), void *info)
+{
+ u8 *mxms = mxms_data(mxm);
+ u8 *desc = mxms + mxms_headerlen(mxm);
+ u8 *fini = desc + mxms_structlen(mxm) - 1;
+ while (desc < fini) {
+ u8 type = desc[0] & 0x0f;
+ u8 headerlen = 0;
+ u8 recordlen = 0;
+ u8 entries = 0;
+
+ switch (type) {
+ case 0: /* Output Device Structure */
+ if (mxms_version(mxm) >= 0x0300)
+ headerlen = 8;
+ else
+ headerlen = 6;
+ break;
+ case 1: /* System Cooling Capability Structure */
+ case 2: /* Thermal Structure */
+ case 3: /* Input Power Structure */
+ headerlen = 4;
+ break;
+ case 4: /* GPIO Device Structure */
+ headerlen = 4;
+ recordlen = 2;
+ entries = (ROM32(desc[0]) & 0x01f00000) >> 20;
+ break;
+ case 5: /* Vendor Specific Structure */
+ headerlen = 8;
+ break;
+ case 6: /* Backlight Control Structure */
+ if (mxms_version(mxm) >= 0x0300) {
+ headerlen = 4;
+ recordlen = 8;
+ entries = (desc[1] & 0xf0) >> 4;
+ } else {
+ headerlen = 8;
+ }
+ break;
+ case 7: /* Fan Control Structure */
+ headerlen = 8;
+ recordlen = 4;
+ entries = desc[1] & 0x07;
+ break;
+ default:
+ nv_debug(mxm, "unknown descriptor type %d\n", type);
+ return false;
+ }
+
+ if (nv_subdev(mxm)->debug >= NV_DBG_DEBUG && (exec == NULL)) {
+ static const char * mxms_desc_name[] = {
+ "ODS", "SCCS", "TS", "IPS",
+ "GSD", "VSS", "BCS", "FCS",
+ };
+ u8 *dump = desc;
+ int i, j;
+
+ nv_debug(mxm, "%4s: ", mxms_desc_name[type]);
+ for (j = headerlen - 1; j >= 0; j--)
+ pr_cont("%02x", dump[j]);
+ pr_cont("\n");
+ dump += headerlen;
+
+ for (i = 0; i < entries; i++, dump += recordlen) {
+ nv_debug(mxm, " ");
+ for (j = recordlen - 1; j >= 0; j--)
+ pr_cont("%02x", dump[j]);
+ pr_cont("\n");
+ }
+ }
+
+ if (types & (1 << type)) {
+ if (!exec(mxm, desc, info))
+ return false;
+ }
+
+ desc += headerlen + (entries * recordlen);
+ }
+
+ return true;
+}
+
+void
+mxms_output_device(struct nvkm_mxm *mxm, u8 *pdata, struct mxms_odev *desc)
+{
+ u64 data = ROM32(pdata[0]);
+ if (mxms_version(mxm) >= 0x0300)
+ data |= (u64)ROM16(pdata[4]) << 32;
+
+ desc->outp_type = (data & 0x00000000000000f0ULL) >> 4;
+ desc->ddc_port = (data & 0x0000000000000f00ULL) >> 8;
+ desc->conn_type = (data & 0x000000000001f000ULL) >> 12;
+ desc->dig_conn = (data & 0x0000000000780000ULL) >> 19;
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mxm/mxms.h b/drivers/gpu/drm/nouveau/nvkm/subdev/mxm/mxms.h
new file mode 100644
index 000000000000..4ef804012d06
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mxm/mxms.h
@@ -0,0 +1,22 @@
+#ifndef __NVMXM_MXMS_H__
+#define __NVMXM_MXMS_H__
+#include <subdev/mxm.h>
+
+struct mxms_odev {
+ u8 outp_type;
+ u8 conn_type;
+ u8 ddc_port;
+ u8 dig_conn;
+};
+
+void mxms_output_device(struct nvkm_mxm *, u8 *, struct mxms_odev *);
+
+u16 mxms_version(struct nvkm_mxm *);
+u16 mxms_headerlen(struct nvkm_mxm *);
+u16 mxms_structlen(struct nvkm_mxm *);
+bool mxms_checksum(struct nvkm_mxm *);
+bool mxms_valid(struct nvkm_mxm *);
+
+bool mxms_foreach(struct nvkm_mxm *, u8,
+ bool (*)(struct nvkm_mxm *, u8 *, void *), void *);
+#endif
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mxm/nv50.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mxm/nv50.c
new file mode 100644
index 000000000000..42cac13ca629
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mxm/nv50.c
@@ -0,0 +1,231 @@
+/*
+ * Copyright 2011 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "mxms.h"
+
+#include <subdev/bios.h>
+#include <subdev/bios/conn.h>
+#include <subdev/bios/dcb.h>
+#include <subdev/bios/mxm.h>
+
+struct nv50_mxm_priv {
+ struct nvkm_mxm base;
+};
+
+struct context {
+ u32 *outp;
+ struct mxms_odev desc;
+};
+
+static bool
+mxm_match_tmds_partner(struct nvkm_mxm *mxm, u8 *data, void *info)
+{
+ struct context *ctx = info;
+ struct mxms_odev desc;
+
+ mxms_output_device(mxm, data, &desc);
+ if (desc.outp_type == 2 &&
+ desc.dig_conn == ctx->desc.dig_conn)
+ return false;
+ return true;
+}
+
+static bool
+mxm_match_dcb(struct nvkm_mxm *mxm, u8 *data, void *info)
+{
+ struct nvkm_bios *bios = nvkm_bios(mxm);
+ struct context *ctx = info;
+ u64 desc = *(u64 *)data;
+
+ mxms_output_device(mxm, data, &ctx->desc);
+
+ /* match dcb encoder type to mxm-ods device type */
+ if ((ctx->outp[0] & 0x0000000f) != ctx->desc.outp_type)
+ return true;
+
+ /* digital output, have some extra stuff to match here, there's a
+ * table in the vbios that provides a mapping from the mxm digital
+ * connection enum values to SOR/link
+ */
+ if ((desc & 0x00000000000000f0) >= 0x20) {
+ /* check against sor index */
+ u8 link = mxm_sor_map(bios, ctx->desc.dig_conn);
+ if ((ctx->outp[0] & 0x0f000000) != (link & 0x0f) << 24)
+ return true;
+
+ /* check dcb entry has a compatible link field */
+ link = (link & 0x30) >> 4;
+ if ((link & ((ctx->outp[1] & 0x00000030) >> 4)) != link)
+ return true;
+ }
+
+ /* mark this descriptor accounted for by setting invalid device type,
+ * except of course some manufactures don't follow specs properly and
+ * we need to avoid killing off the TMDS function on DP connectors
+ * if MXM-SIS is missing an entry for it.
+ */
+ data[0] &= ~0xf0;
+ if (ctx->desc.outp_type == 6 && ctx->desc.conn_type == 6 &&
+ mxms_foreach(mxm, 0x01, mxm_match_tmds_partner, ctx)) {
+ data[0] |= 0x20; /* modify descriptor to match TMDS now */
+ } else {
+ data[0] |= 0xf0;
+ }
+
+ return false;
+}
+
+static int
+mxm_dcb_sanitise_entry(struct nvkm_bios *bios, void *data, int idx, u16 pdcb)
+{
+ struct nvkm_mxm *mxm = data;
+ struct context ctx = { .outp = (u32 *)(bios->data + pdcb) };
+ u8 type, i2cidx, link, ver, len;
+ u8 *conn;
+
+ /* look for an output device structure that matches this dcb entry.
+ * if one isn't found, disable it.
+ */
+ if (mxms_foreach(mxm, 0x01, mxm_match_dcb, &ctx)) {
+ nv_debug(mxm, "disable %d: 0x%08x 0x%08x\n",
+ idx, ctx.outp[0], ctx.outp[1]);
+ ctx.outp[0] |= 0x0000000f;
+ return 0;
+ }
+
+ /* modify the output's ddc/aux port, there's a pointer to a table
+ * with the mapping from mxm ddc/aux port to dcb i2c_index in the
+ * vbios mxm table
+ */
+ i2cidx = mxm_ddc_map(bios, ctx.desc.ddc_port);
+ if ((ctx.outp[0] & 0x0000000f) != DCB_OUTPUT_DP)
+ i2cidx = (i2cidx & 0x0f) << 4;
+ else
+ i2cidx = (i2cidx & 0xf0);
+
+ if (i2cidx != 0xf0) {
+ ctx.outp[0] &= ~0x000000f0;
+ ctx.outp[0] |= i2cidx;
+ }
+
+ /* override dcb sorconf.link, based on what mxm data says */
+ switch (ctx.desc.outp_type) {
+ case 0x00: /* Analog CRT */
+ case 0x01: /* Analog TV/HDTV */
+ break;
+ default:
+ link = mxm_sor_map(bios, ctx.desc.dig_conn) & 0x30;
+ ctx.outp[1] &= ~0x00000030;
+ ctx.outp[1] |= link;
+ break;
+ }
+
+ /* we may need to fixup various other vbios tables based on what
+ * the descriptor says the connector type should be.
+ *
+ * in a lot of cases, the vbios tables will claim DVI-I is possible,
+ * and the mxm data says the connector is really HDMI. another
+ * common example is DP->eDP.
+ */
+ conn = bios->data;
+ conn += nvbios_connEe(bios, (ctx.outp[0] & 0x0000f000) >> 12, &ver, &len);
+ type = conn[0];
+ switch (ctx.desc.conn_type) {
+ case 0x01: /* LVDS */
+ ctx.outp[1] |= 0x00000004; /* use_power_scripts */
+ /* XXX: modify default link width in LVDS table */
+ break;
+ case 0x02: /* HDMI */
+ type = DCB_CONNECTOR_HDMI_1;
+ break;
+ case 0x03: /* DVI-D */
+ type = DCB_CONNECTOR_DVI_D;
+ break;
+ case 0x0e: /* eDP, falls through to DPint */
+ ctx.outp[1] |= 0x00010000;
+ case 0x07: /* DP internal, wtf is this?? HP8670w */
+ ctx.outp[1] |= 0x00000004; /* use_power_scripts? */
+ type = DCB_CONNECTOR_eDP;
+ break;
+ default:
+ break;
+ }
+
+ if (mxms_version(mxm) >= 0x0300)
+ conn[0] = type;
+
+ return 0;
+}
+
+static bool
+mxm_show_unmatched(struct nvkm_mxm *mxm, u8 *data, void *info)
+{
+ u64 desc = *(u64 *)data;
+ if ((desc & 0xf0) != 0xf0)
+ nv_info(mxm, "unmatched output device 0x%016llx\n", desc);
+ return true;
+}
+
+static void
+mxm_dcb_sanitise(struct nvkm_mxm *mxm)
+{
+ struct nvkm_bios *bios = nvkm_bios(mxm);
+ u8 ver, hdr, cnt, len;
+ u16 dcb = dcb_table(bios, &ver, &hdr, &cnt, &len);
+ if (dcb == 0x0000 || ver != 0x40) {
+ nv_debug(mxm, "unsupported DCB version\n");
+ return;
+ }
+
+ dcb_outp_foreach(bios, mxm, mxm_dcb_sanitise_entry);
+ mxms_foreach(mxm, 0x01, mxm_show_unmatched, NULL);
+}
+
+static int
+nv50_mxm_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 size,
+ struct nvkm_object **pobject)
+{
+ struct nv50_mxm_priv *priv;
+ int ret;
+
+ ret = nvkm_mxm_create(parent, engine, oclass, &priv);
+ *pobject = nv_object(priv);
+ if (ret)
+ return ret;
+
+ if (priv->base.action & MXM_SANITISE_DCB)
+ mxm_dcb_sanitise(&priv->base);
+ return 0;
+}
+
+struct nvkm_oclass
+nv50_mxm_oclass = {
+ .handle = NV_SUBDEV(MXM, 0x50),
+ .ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv50_mxm_ctor,
+ .dtor = _nvkm_mxm_dtor,
+ .init = _nvkm_mxm_init,
+ .fini = _nvkm_mxm_fini,
+ },
+};
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/Kbuild b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/Kbuild
new file mode 100644
index 000000000000..9a150d520225
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/Kbuild
@@ -0,0 +1,8 @@
+nvkm-y += nvkm/subdev/pmu/base.o
+nvkm-y += nvkm/subdev/pmu/memx.o
+nvkm-y += nvkm/subdev/pmu/gt215.o
+nvkm-y += nvkm/subdev/pmu/gf100.o
+nvkm-y += nvkm/subdev/pmu/gf110.o
+nvkm-y += nvkm/subdev/pmu/gk104.o
+nvkm-y += nvkm/subdev/pmu/gk208.o
+nvkm-y += nvkm/subdev/pmu/gk20a.o
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/base.c
new file mode 100644
index 000000000000..054b2d2eec35
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/base.c
@@ -0,0 +1,268 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "priv.h"
+
+#include <subdev/timer.h>
+
+void
+nvkm_pmu_pgob(struct nvkm_pmu *pmu, bool enable)
+{
+ const struct nvkm_pmu_impl *impl = (void *)nv_oclass(pmu);
+ if (impl->pgob)
+ impl->pgob(pmu, enable);
+}
+
+static int
+nvkm_pmu_send(struct nvkm_pmu *pmu, u32 reply[2],
+ u32 process, u32 message, u32 data0, u32 data1)
+{
+ struct nvkm_subdev *subdev = nv_subdev(pmu);
+ u32 addr;
+
+ /* wait for a free slot in the fifo */
+ addr = nv_rd32(pmu, 0x10a4a0);
+ if (!nv_wait_ne(pmu, 0x10a4b0, 0xffffffff, addr ^ 8))
+ return -EBUSY;
+
+ /* we currently only support a single process at a time waiting
+ * on a synchronous reply, take the PMU mutex and tell the
+ * receive handler what we're waiting for
+ */
+ if (reply) {
+ mutex_lock(&subdev->mutex);
+ pmu->recv.message = message;
+ pmu->recv.process = process;
+ }
+
+ /* acquire data segment access */
+ do {
+ nv_wr32(pmu, 0x10a580, 0x00000001);
+ } while (nv_rd32(pmu, 0x10a580) != 0x00000001);
+
+ /* write the packet */
+ nv_wr32(pmu, 0x10a1c0, 0x01000000 | (((addr & 0x07) << 4) +
+ pmu->send.base));
+ nv_wr32(pmu, 0x10a1c4, process);
+ nv_wr32(pmu, 0x10a1c4, message);
+ nv_wr32(pmu, 0x10a1c4, data0);
+ nv_wr32(pmu, 0x10a1c4, data1);
+ nv_wr32(pmu, 0x10a4a0, (addr + 1) & 0x0f);
+
+ /* release data segment access */
+ nv_wr32(pmu, 0x10a580, 0x00000000);
+
+ /* wait for reply, if requested */
+ if (reply) {
+ wait_event(pmu->recv.wait, (pmu->recv.process == 0));
+ reply[0] = pmu->recv.data[0];
+ reply[1] = pmu->recv.data[1];
+ mutex_unlock(&subdev->mutex);
+ }
+
+ return 0;
+}
+
+static void
+nvkm_pmu_recv(struct work_struct *work)
+{
+ struct nvkm_pmu *pmu = container_of(work, struct nvkm_pmu, recv.work);
+ u32 process, message, data0, data1;
+
+ /* nothing to do if GET == PUT */
+ u32 addr = nv_rd32(pmu, 0x10a4cc);
+ if (addr == nv_rd32(pmu, 0x10a4c8))
+ return;
+
+ /* acquire data segment access */
+ do {
+ nv_wr32(pmu, 0x10a580, 0x00000002);
+ } while (nv_rd32(pmu, 0x10a580) != 0x00000002);
+
+ /* read the packet */
+ nv_wr32(pmu, 0x10a1c0, 0x02000000 | (((addr & 0x07) << 4) +
+ pmu->recv.base));
+ process = nv_rd32(pmu, 0x10a1c4);
+ message = nv_rd32(pmu, 0x10a1c4);
+ data0 = nv_rd32(pmu, 0x10a1c4);
+ data1 = nv_rd32(pmu, 0x10a1c4);
+ nv_wr32(pmu, 0x10a4cc, (addr + 1) & 0x0f);
+
+ /* release data segment access */
+ nv_wr32(pmu, 0x10a580, 0x00000000);
+
+ /* wake process if it's waiting on a synchronous reply */
+ if (pmu->recv.process) {
+ if (process == pmu->recv.process &&
+ message == pmu->recv.message) {
+ pmu->recv.data[0] = data0;
+ pmu->recv.data[1] = data1;
+ pmu->recv.process = 0;
+ wake_up(&pmu->recv.wait);
+ return;
+ }
+ }
+
+ /* right now there's no other expected responses from the engine,
+ * so assume that any unexpected message is an error.
+ */
+ nv_warn(pmu, "%c%c%c%c 0x%08x 0x%08x 0x%08x 0x%08x\n",
+ (char)((process & 0x000000ff) >> 0),
+ (char)((process & 0x0000ff00) >> 8),
+ (char)((process & 0x00ff0000) >> 16),
+ (char)((process & 0xff000000) >> 24),
+ process, message, data0, data1);
+}
+
+static void
+nvkm_pmu_intr(struct nvkm_subdev *subdev)
+{
+ struct nvkm_pmu *pmu = (void *)subdev;
+ u32 disp = nv_rd32(pmu, 0x10a01c);
+ u32 intr = nv_rd32(pmu, 0x10a008) & disp & ~(disp >> 16);
+
+ if (intr & 0x00000020) {
+ u32 stat = nv_rd32(pmu, 0x10a16c);
+ if (stat & 0x80000000) {
+ nv_error(pmu, "UAS fault at 0x%06x addr 0x%08x\n",
+ stat & 0x00ffffff, nv_rd32(pmu, 0x10a168));
+ nv_wr32(pmu, 0x10a16c, 0x00000000);
+ intr &= ~0x00000020;
+ }
+ }
+
+ if (intr & 0x00000040) {
+ schedule_work(&pmu->recv.work);
+ nv_wr32(pmu, 0x10a004, 0x00000040);
+ intr &= ~0x00000040;
+ }
+
+ if (intr & 0x00000080) {
+ nv_info(pmu, "wr32 0x%06x 0x%08x\n", nv_rd32(pmu, 0x10a7a0),
+ nv_rd32(pmu, 0x10a7a4));
+ nv_wr32(pmu, 0x10a004, 0x00000080);
+ intr &= ~0x00000080;
+ }
+
+ if (intr) {
+ nv_error(pmu, "intr 0x%08x\n", intr);
+ nv_wr32(pmu, 0x10a004, intr);
+ }
+}
+
+int
+_nvkm_pmu_fini(struct nvkm_object *object, bool suspend)
+{
+ struct nvkm_pmu *pmu = (void *)object;
+
+ nv_wr32(pmu, 0x10a014, 0x00000060);
+ flush_work(&pmu->recv.work);
+
+ return nvkm_subdev_fini(&pmu->base, suspend);
+}
+
+int
+_nvkm_pmu_init(struct nvkm_object *object)
+{
+ const struct nvkm_pmu_impl *impl = (void *)object->oclass;
+ struct nvkm_pmu *pmu = (void *)object;
+ int ret, i;
+
+ ret = nvkm_subdev_init(&pmu->base);
+ if (ret)
+ return ret;
+
+ nv_subdev(pmu)->intr = nvkm_pmu_intr;
+ pmu->message = nvkm_pmu_send;
+ pmu->pgob = nvkm_pmu_pgob;
+
+ /* prevent previous ucode from running, wait for idle, reset */
+ nv_wr32(pmu, 0x10a014, 0x0000ffff); /* INTR_EN_CLR = ALL */
+ nv_wait(pmu, 0x10a04c, 0xffffffff, 0x00000000);
+ nv_mask(pmu, 0x000200, 0x00002000, 0x00000000);
+ nv_mask(pmu, 0x000200, 0x00002000, 0x00002000);
+ nv_rd32(pmu, 0x000200);
+ nv_wait(pmu, 0x10a10c, 0x00000006, 0x00000000);
+
+ /* upload data segment */
+ nv_wr32(pmu, 0x10a1c0, 0x01000000);
+ for (i = 0; i < impl->data.size / 4; i++)
+ nv_wr32(pmu, 0x10a1c4, impl->data.data[i]);
+
+ /* upload code segment */
+ nv_wr32(pmu, 0x10a180, 0x01000000);
+ for (i = 0; i < impl->code.size / 4; i++) {
+ if ((i & 0x3f) == 0)
+ nv_wr32(pmu, 0x10a188, i >> 6);
+ nv_wr32(pmu, 0x10a184, impl->code.data[i]);
+ }
+
+ /* start it running */
+ nv_wr32(pmu, 0x10a10c, 0x00000000);
+ nv_wr32(pmu, 0x10a104, 0x00000000);
+ nv_wr32(pmu, 0x10a100, 0x00000002);
+
+ /* wait for valid host->pmu ring configuration */
+ if (!nv_wait_ne(pmu, 0x10a4d0, 0xffffffff, 0x00000000))
+ return -EBUSY;
+ pmu->send.base = nv_rd32(pmu, 0x10a4d0) & 0x0000ffff;
+ pmu->send.size = nv_rd32(pmu, 0x10a4d0) >> 16;
+
+ /* wait for valid pmu->host ring configuration */
+ if (!nv_wait_ne(pmu, 0x10a4dc, 0xffffffff, 0x00000000))
+ return -EBUSY;
+ pmu->recv.base = nv_rd32(pmu, 0x10a4dc) & 0x0000ffff;
+ pmu->recv.size = nv_rd32(pmu, 0x10a4dc) >> 16;
+
+ nv_wr32(pmu, 0x10a010, 0x000000e0);
+ return 0;
+}
+
+int
+nvkm_pmu_create_(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, int length, void **pobject)
+{
+ struct nvkm_pmu *pmu;
+ int ret;
+
+ ret = nvkm_subdev_create_(parent, engine, oclass, 0, "PMU",
+ "pmu", length, pobject);
+ pmu = *pobject;
+ if (ret)
+ return ret;
+
+ INIT_WORK(&pmu->recv.work, nvkm_pmu_recv);
+ init_waitqueue_head(&pmu->recv.wait);
+ return 0;
+}
+
+int
+_nvkm_pmu_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 size,
+ struct nvkm_object **pobject)
+{
+ struct nvkm_pmu *pmu;
+ int ret = nvkm_pmu_create(parent, engine, oclass, &pmu);
+ *pobject = nv_object(pmu);
+ return ret;
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/arith.fuc b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/arith.fuc
new file mode 100644
index 000000000000..214a6d9e088d
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/arith.fuc
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2014 Martin Peres <martin.peres@free.fr>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the folloing conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Martin Peres
+ */
+
+/******************************************************************************
+ * arith data segment
+ *****************************************************************************/
+#ifdef INCLUDE_PROC
+#endif
+
+#ifdef INCLUDE_DATA
+#endif
+
+/******************************************************************************
+ * arith code segment
+ *****************************************************************************/
+#ifdef INCLUDE_CODE
+
+// does a 32x32 -> 64 multiplication
+//
+// A * B = A_lo * B_lo
+// + ( A_hi * B_lo ) << 16
+// + ( A_lo * B_hi ) << 16
+// + ( A_hi * B_hi ) << 32
+//
+// $r15 - current
+// $r14 - A
+// $r13 - B
+// $r12 - mul_lo (return)
+// $r11 - mul_hi (return)
+// $r0 - zero
+mulu32_32_64:
+ push $r1 // A_hi
+ push $r2 // B_hi
+ push $r3 // tmp0
+ push $r4 // tmp1
+
+ shr b32 $r1 $r14 16
+ shr b32 $r2 $r13 16
+
+ clear b32 $r12
+ clear b32 $r11
+
+ // A_lo * B_lo
+ mulu $r12 $r14 $r13
+
+ // ( A_hi * B_lo ) << 16
+ mulu $r3 $r1 $r13 // tmp0 = A_hi * B_lo
+ mov b32 $r4 $r3
+ and $r3 0xffff // tmp0 = tmp0_lo
+ shl b32 $r3 16
+ shr b32 $r4 16 // tmp1 = tmp0_hi
+ add b32 $r12 $r3
+ adc b32 $r11 $r4
+
+ // ( A_lo * B_hi ) << 16
+ mulu $r3 $r14 $r2 // tmp0 = A_lo * B_hi
+ mov b32 $r4 $r3
+ and $r3 0xffff // tmp0 = tmp0_lo
+ shl b32 $r3 16
+ shr b32 $r4 16 // tmp1 = tmp0_hi
+ add b32 $r12 $r3
+ adc b32 $r11 $r4
+
+ // ( A_hi * B_hi ) << 32
+ mulu $r3 $r1 $r2 // tmp0 = A_hi * B_hi
+ add b32 $r11 $r3
+
+ pop $r4
+ pop $r3
+ pop $r2
+ pop $r1
+ ret
+#endif
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gf100.fuc3 b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gf100.fuc3
new file mode 100644
index 000000000000..37e8407b7462
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gf100.fuc3
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#define NVKM_PPWR_CHIPSET GF100
+#define HW_TICKS_PER_US 203 // should be 202.5
+
+//#define NVKM_FALCON_PC24
+//#define NVKM_FALCON_UNSHIFTED_IO
+//#define NVKM_FALCON_MMIO_UAS
+//#define NVKM_FALCON_MMIO_TRAP
+
+#include "macros.fuc"
+
+.section #gf100_pmu_data
+#define INCLUDE_PROC
+#include "kernel.fuc"
+#include "arith.fuc"
+#include "host.fuc"
+#include "memx.fuc"
+#include "perf.fuc"
+#include "i2c_.fuc"
+#include "test.fuc"
+#include "idle.fuc"
+#undef INCLUDE_PROC
+
+#define INCLUDE_DATA
+#include "kernel.fuc"
+#include "arith.fuc"
+#include "host.fuc"
+#include "memx.fuc"
+#include "perf.fuc"
+#include "i2c_.fuc"
+#include "test.fuc"
+#include "idle.fuc"
+#undef INCLUDE_DATA
+.align 256
+
+.section #gf100_pmu_code
+#define INCLUDE_CODE
+#include "kernel.fuc"
+#include "arith.fuc"
+#include "host.fuc"
+#include "memx.fuc"
+#include "perf.fuc"
+#include "i2c_.fuc"
+#include "test.fuc"
+#include "idle.fuc"
+#undef INCLUDE_CODE
+.align 256
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gf100.fuc3.h b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gf100.fuc3.h
new file mode 100644
index 000000000000..302557c52d03
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gf100.fuc3.h
@@ -0,0 +1,1865 @@
+uint32_t gf100_pmu_data[] = {
+/* 0x0000: proc_kern */
+ 0x52544e49,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+/* 0x0058: proc_list_head */
+ 0x54534f48,
+ 0x00000512,
+ 0x000004af,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x584d454d,
+ 0x0000075e,
+ 0x00000750,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x46524550,
+ 0x00000762,
+ 0x00000760,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x5f433249,
+ 0x00000b92,
+ 0x00000a35,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x54534554,
+ 0x00000bbb,
+ 0x00000b94,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x454c4449,
+ 0x00000bc7,
+ 0x00000bc5,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+/* 0x0268: proc_list_tail */
+/* 0x0268: time_prev */
+ 0x00000000,
+/* 0x026c: time_next */
+ 0x00000000,
+/* 0x0270: fifo_queue */
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+/* 0x02f0: rfifo_queue */
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+/* 0x0370: memx_func_head */
+ 0x00000001,
+ 0x00000000,
+ 0x00000551,
+/* 0x037c: memx_func_next */
+ 0x00000002,
+ 0x00000000,
+ 0x000005db,
+ 0x00000003,
+ 0x00000002,
+ 0x000006a5,
+ 0x00040004,
+ 0x00000000,
+ 0x000006c1,
+ 0x00010005,
+ 0x00000000,
+ 0x000006de,
+ 0x00010006,
+ 0x00000000,
+ 0x00000663,
+ 0x00000007,
+ 0x00000000,
+ 0x000006e9,
+/* 0x03c4: memx_func_tail */
+/* 0x03c4: memx_ts_start */
+ 0x00000000,
+/* 0x03c8: memx_ts_end */
+ 0x00000000,
+/* 0x03cc: memx_data_head */
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+/* 0x0bcc: memx_data_tail */
+/* 0x0bcc: memx_train_head */
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+/* 0x0ccc: memx_train_tail */
+/* 0x0ccc: i2c_scl_map */
+ 0x00001000,
+ 0x00004000,
+ 0x00010000,
+ 0x00000100,
+ 0x00040000,
+ 0x00100000,
+ 0x00400000,
+ 0x01000000,
+ 0x04000000,
+ 0x10000000,
+/* 0x0cf4: i2c_sda_map */
+ 0x00002000,
+ 0x00008000,
+ 0x00020000,
+ 0x00000200,
+ 0x00080000,
+ 0x00200000,
+ 0x00800000,
+ 0x02000000,
+ 0x08000000,
+ 0x20000000,
+/* 0x0d1c: i2c_ctrl */
+ 0x0000e138,
+ 0x0000e150,
+ 0x0000e168,
+ 0x0000e180,
+ 0x0000e254,
+ 0x0000e274,
+ 0x0000e764,
+ 0x0000e780,
+ 0x0000e79c,
+ 0x0000e7b8,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+};
+
+uint32_t gf100_pmu_code[] = {
+ 0x039e0ef5,
+/* 0x0004: rd32 */
+ 0x07a007f1,
+ 0xd00604b6,
+ 0x04bd000e,
+ 0xf001d7f0,
+ 0x07f101d3,
+ 0x04b607ac,
+ 0x000dd006,
+/* 0x0022: rd32_wait */
+ 0xd7f104bd,
+ 0xd4b607ac,
+ 0x00ddcf06,
+ 0x7000d4f1,
+ 0xf1f21bf4,
+ 0xb607a4d7,
+ 0xddcf06d4,
+/* 0x003f: wr32 */
+ 0xf100f800,
+ 0xb607a007,
+ 0x0ed00604,
+ 0xf104bd00,
+ 0xb607a407,
+ 0x0dd00604,
+ 0xf004bd00,
+ 0xd5f002d7,
+ 0x01d3f0f0,
+ 0x07ac07f1,
+ 0xd00604b6,
+ 0x04bd000d,
+/* 0x006c: wr32_wait */
+ 0x07acd7f1,
+ 0xcf06d4b6,
+ 0xd4f100dd,
+ 0x1bf47000,
+/* 0x007f: nsec */
+ 0xf900f8f2,
+ 0xf080f990,
+ 0x84b62c87,
+ 0x0088cf06,
+/* 0x008c: nsec_loop */
+ 0xb62c97f0,
+ 0x99cf0694,
+ 0x0298bb00,
+ 0xf4069eb8,
+ 0x80fcf11e,
+ 0x00f890fc,
+/* 0x00a4: wait */
+ 0x80f990f9,
+ 0xb62c87f0,
+ 0x88cf0684,
+/* 0x00b1: wait_loop */
+ 0x02eeb900,
+ 0xb90421f4,
+ 0xadfd02da,
+ 0x06acb804,
+ 0xf0150bf4,
+ 0x94b62c97,
+ 0x0099cf06,
+ 0xb80298bb,
+ 0x1ef4069b,
+/* 0x00d5: wait_done */
+ 0xfc80fcdf,
+/* 0x00db: intr_watchdog */
+ 0x9800f890,
+ 0x96b003e9,
+ 0x2a0bf400,
+ 0xbb9a0a98,
+ 0x1cf4029a,
+ 0x01d7f00f,
+ 0x02dd21f5,
+ 0x0ef494bd,
+/* 0x00f9: intr_watchdog_next_time */
+ 0x9b0a9815,
+ 0xf400a6b0,
+ 0x9ab8090b,
+ 0x061cf406,
+/* 0x0108: intr_watchdog_next_time_set */
+/* 0x010b: intr_watchdog_next_proc */
+ 0x809b0980,
+ 0xe0b603e9,
+ 0x68e6b158,
+ 0xc61bf402,
+/* 0x011a: intr */
+ 0x00f900f8,
+ 0x80f904bd,
+ 0xa0f990f9,
+ 0xc0f9b0f9,
+ 0xe0f9d0f9,
+ 0xf7f0f0f9,
+ 0x0188fe00,
+ 0x87f180f9,
+ 0x84b605d0,
+ 0x0088cf06,
+ 0xf10180b6,
+ 0xb605d007,
+ 0x08d00604,
+ 0xf004bd00,
+ 0x84b60887,
+ 0x0088cf06,
+ 0xf40289c4,
+ 0x0080230b,
+ 0x58e7f09b,
+ 0x98db21f4,
+ 0x96b09b09,
+ 0x110bf400,
+ 0xb63407f0,
+ 0x09d00604,
+ 0x8004bd00,
+/* 0x017e: intr_skip_watchdog */
+ 0x89e49a09,
+ 0x0bf40800,
+ 0x8897f148,
+ 0x0694b606,
+ 0xc40099cf,
+ 0x0bf4029a,
+ 0xc0c7f12c,
+ 0x06c4b604,
+ 0xf900cccf,
+ 0x48e7f1c0,
+ 0x53e3f14f,
+ 0x00d7f054,
+ 0x034221f5,
+ 0x07f1c0fc,
+ 0x04b604c0,
+ 0x000cd006,
+/* 0x01be: intr_subintr_skip_fifo */
+ 0x07f104bd,
+ 0x04b60688,
+ 0x0009d006,
+/* 0x01ca: intr_skip_subintr */
+ 0x89c404bd,
+ 0x070bf420,
+ 0xffbfa4f1,
+/* 0x01d4: intr_skip_pause */
+ 0xf44089c4,
+ 0xa4f1070b,
+/* 0x01de: intr_skip_user0 */
+ 0x07f0ffbf,
+ 0x0604b604,
+ 0xbd0008d0,
+ 0xfe80fc04,
+ 0xf0fc0088,
+ 0xd0fce0fc,
+ 0xb0fcc0fc,
+ 0x90fca0fc,
+ 0x00fc80fc,
+ 0xf80032f4,
+/* 0x0205: ticks_from_ns */
+ 0xf9c0f901,
+ 0xcbd7f1b0,
+ 0x00d3f000,
+ 0x041321f5,
+ 0x03e8ccec,
+ 0xf400b4b0,
+ 0xeeec120b,
+ 0xd7f103e8,
+ 0xd3f000cb,
+ 0x1321f500,
+/* 0x022d: ticks_from_ns_quit */
+ 0x02ceb904,
+ 0xc0fcb0fc,
+/* 0x0236: ticks_from_us */
+ 0xc0f900f8,
+ 0xd7f1b0f9,
+ 0xd3f000cb,
+ 0x1321f500,
+ 0x02ceb904,
+ 0xf400b4b0,
+ 0xe4bd050b,
+/* 0x0250: ticks_from_us_quit */
+ 0xc0fcb0fc,
+/* 0x0256: ticks_to_us */
+ 0xd7f100f8,
+ 0xd3f000cb,
+ 0xecedff00,
+/* 0x0262: timer */
+ 0x90f900f8,
+ 0x32f480f9,
+ 0x03f89810,
+ 0xf40086b0,
+ 0x84bd651c,
+ 0xb63807f0,
+ 0x08d00604,
+ 0xf004bd00,
+ 0x84b63487,
+ 0x0088cf06,
+ 0xbb9a0998,
+ 0xe9bb0298,
+ 0x03fe8000,
+ 0xb60887f0,
+ 0x88cf0684,
+ 0x0284f000,
+ 0xf0261bf4,
+ 0x84b63487,
+ 0x0088cf06,
+ 0xf406e0b8,
+ 0xe8b8090b,
+ 0x111cf406,
+/* 0x02b8: timer_reset */
+ 0xb63407f0,
+ 0x0ed00604,
+ 0x8004bd00,
+/* 0x02c6: timer_enable */
+ 0x87f09a0e,
+ 0x3807f001,
+ 0xd00604b6,
+ 0x04bd0008,
+/* 0x02d4: timer_done */
+ 0xfc1031f4,
+ 0xf890fc80,
+/* 0x02dd: send_proc */
+ 0xf980f900,
+ 0x05e89890,
+ 0xf004e998,
+ 0x89b80486,
+ 0x2a0bf406,
+ 0x940398c4,
+ 0x80b60488,
+ 0x008ebb18,
+ 0x8000fa98,
+ 0x8d80008a,
+ 0x028c8001,
+ 0xb6038b80,
+ 0x94f00190,
+ 0x04e98007,
+/* 0x0317: send_done */
+ 0xfc0231f4,
+ 0xf880fc90,
+/* 0x031d: find */
+ 0xf080f900,
+ 0x31f45887,
+/* 0x0325: find_loop */
+ 0x008a9801,
+ 0xf406aeb8,
+ 0x80b6100b,
+ 0x6886b158,
+ 0xf01bf402,
+/* 0x033b: find_done */
+ 0xb90132f4,
+ 0x80fc028e,
+/* 0x0342: send */
+ 0x21f500f8,
+ 0x01f4031d,
+/* 0x034b: recv */
+ 0xf900f897,
+ 0x9880f990,
+ 0xe99805e8,
+ 0x0132f404,
+ 0xf40689b8,
+ 0x89c43d0b,
+ 0x0180b603,
+ 0x800784f0,
+ 0xea9805e8,
+ 0xfef0f902,
+ 0xf0f9018f,
+ 0x9402efb9,
+ 0xe9bb0499,
+ 0x18e0b600,
+ 0x9803eb98,
+ 0xed9802ec,
+ 0x00ee9801,
+ 0xf0fca5f9,
+ 0xf400f8fe,
+ 0xf0fc0131,
+/* 0x0398: recv_done */
+ 0x90fc80fc,
+/* 0x039e: init */
+ 0x17f100f8,
+ 0x14b60108,
+ 0x0011cf06,
+ 0x010911e7,
+ 0xfe0814b6,
+ 0x17f10014,
+ 0x13f000e0,
+ 0x1c07f000,
+ 0xd00604b6,
+ 0x04bd0001,
+ 0xf0ff17f0,
+ 0x04b61407,
+ 0x0001d006,
+ 0x17f004bd,
+ 0x0015f102,
+ 0x1007f008,
+ 0xd00604b6,
+ 0x04bd0001,
+ 0x011a17f1,
+ 0xfe0013f0,
+ 0x31f40010,
+ 0x0117f010,
+ 0xb63807f0,
+ 0x01d00604,
+ 0xf004bd00,
+/* 0x0402: init_proc */
+ 0xf19858f7,
+ 0x0016b001,
+ 0xf9fa0bf4,
+ 0x58f0b615,
+/* 0x0413: mulu32_32_64 */
+ 0xf9f20ef4,
+ 0xf920f910,
+ 0x9540f930,
+ 0xd29510e1,
+ 0xbdc4bd10,
+ 0xc0edffb4,
+ 0xb9301dff,
+ 0x34f10234,
+ 0x34b6ffff,
+ 0x1045b610,
+ 0xbb00c3bb,
+ 0xe2ff01b4,
+ 0x0234b930,
+ 0xffff34f1,
+ 0xb61034b6,
+ 0xc3bb1045,
+ 0x01b4bb00,
+ 0xbb3012ff,
+ 0x40fc00b3,
+ 0x20fc30fc,
+ 0x00f810fc,
+/* 0x0464: host_send */
+ 0x04b017f1,
+ 0xcf0614b6,
+ 0x27f10011,
+ 0x24b604a0,
+ 0x0022cf06,
+ 0xf40612b8,
+ 0x1ec4320b,
+ 0x04ee9407,
+ 0x0270e0b7,
+ 0x9803eb98,
+ 0xed9802ec,
+ 0x00ee9801,
+ 0x034221f5,
+ 0xc40110b6,
+ 0x07f10f1e,
+ 0x04b604b0,
+ 0x000ed006,
+ 0x0ef404bd,
+/* 0x04ad: host_send_done */
+/* 0x04af: host_recv */
+ 0xf100f8ba,
+ 0xf14e4917,
+ 0xb8525413,
+ 0x0bf406e1,
+/* 0x04bd: host_recv_wait */
+ 0xcc17f1aa,
+ 0x0614b604,
+ 0xf10011cf,
+ 0xb604c827,
+ 0x22cf0624,
+ 0x0816f000,
+ 0xf40612b8,
+ 0x23c4e60b,
+ 0x0434b607,
+ 0x02f030b7,
+ 0x80033b80,
+ 0x3d80023c,
+ 0x003e8001,
+ 0xf00120b6,
+ 0x07f10f24,
+ 0x04b604c8,
+ 0x0002d006,
+ 0x27f004bd,
+ 0x0007f040,
+ 0xd00604b6,
+ 0x04bd0002,
+/* 0x0512: host_init */
+ 0x17f100f8,
+ 0x14b60080,
+ 0x7015f110,
+ 0xd007f102,
+ 0x0604b604,
+ 0xbd0001d0,
+ 0x8017f104,
+ 0x1014b600,
+ 0x02f015f1,
+ 0x04dc07f1,
+ 0xd00604b6,
+ 0x04bd0001,
+ 0xf10117f0,
+ 0xb604c407,
+ 0x01d00604,
+ 0xf804bd00,
+/* 0x0551: memx_func_enter */
+ 0x2067f100,
+ 0x5d77f116,
+ 0xff73f1f5,
+ 0x026eb9ff,
+ 0xb90421f4,
+ 0x87fd02d8,
+ 0xf960f904,
+ 0xfcd0fc80,
+ 0x3f21f4e0,
+ 0xfffe77f1,
+ 0xffff73f1,
+ 0xf4026eb9,
+ 0xd8b90421,
+ 0x0487fd02,
+ 0x80f960f9,
+ 0xe0fcd0fc,
+ 0xf13f21f4,
+ 0xb926f067,
+ 0x21f4026e,
+ 0x02d8b904,
+ 0xf90487fd,
+ 0xfc80f960,
+ 0xf4e0fcd0,
+ 0x67f03f21,
+ 0xe007f104,
+ 0x0604b607,
+ 0xbd0006d0,
+/* 0x05bd: memx_func_enter_wait */
+ 0xc067f104,
+ 0x0664b607,
+ 0xf00066cf,
+ 0x0bf40464,
+ 0x2c67f0f3,
+ 0xcf0664b6,
+ 0x06800066,
+/* 0x05db: memx_func_leave */
+ 0xf000f8f1,
+ 0x64b62c67,
+ 0x0066cf06,
+ 0xf0f20680,
+ 0x07f10467,
+ 0x04b607e4,
+ 0x0006d006,
+/* 0x05f6: memx_func_leave_wait */
+ 0x67f104bd,
+ 0x64b607c0,
+ 0x0066cf06,
+ 0xf40464f0,
+ 0x67f1f31b,
+ 0x77f126f0,
+ 0x73f00001,
+ 0x026eb900,
+ 0xb90421f4,
+ 0x87fd02d8,
+ 0xf960f905,
+ 0xfcd0fc80,
+ 0x3f21f4e0,
+ 0x162067f1,
+ 0xf4026eb9,
+ 0xd8b90421,
+ 0x0587fd02,
+ 0x80f960f9,
+ 0xe0fcd0fc,
+ 0xf13f21f4,
+ 0xf00aa277,
+ 0x6eb90073,
+ 0x0421f402,
+ 0xfd02d8b9,
+ 0x60f90587,
+ 0xd0fc80f9,
+ 0x21f4e0fc,
+/* 0x0663: memx_func_wait_vblank */
+ 0x9800f83f,
+ 0x66b00016,
+ 0x130bf400,
+ 0xf40166b0,
+ 0x0ef4060b,
+/* 0x0675: memx_func_wait_vblank_head1 */
+ 0x2077f12e,
+ 0x070ef400,
+/* 0x067c: memx_func_wait_vblank_head0 */
+ 0x000877f1,
+/* 0x0680: memx_func_wait_vblank_0 */
+ 0x07c467f1,
+ 0xcf0664b6,
+ 0x67fd0066,
+ 0xf31bf404,
+/* 0x0690: memx_func_wait_vblank_1 */
+ 0x07c467f1,
+ 0xcf0664b6,
+ 0x67fd0066,
+ 0xf30bf404,
+/* 0x06a0: memx_func_wait_vblank_fini */
+ 0xf80410b6,
+/* 0x06a5: memx_func_wr32 */
+ 0x00169800,
+ 0xb6011598,
+ 0x60f90810,
+ 0xd0fc50f9,
+ 0x21f4e0fc,
+ 0x0242b63f,
+ 0xf8e91bf4,
+/* 0x06c1: memx_func_wait */
+ 0x2c87f000,
+ 0xcf0684b6,
+ 0x1e980088,
+ 0x011d9800,
+ 0x98021c98,
+ 0x10b6031b,
+ 0xa421f410,
+/* 0x06de: memx_func_delay */
+ 0x1e9800f8,
+ 0x0410b600,
+ 0xf87f21f4,
+/* 0x06e9: memx_func_train */
+/* 0x06eb: memx_exec */
+ 0xf900f800,
+ 0xb9d0f9e0,
+ 0xb2b902c1,
+/* 0x06f5: memx_exec_next */
+ 0x00139802,
+ 0xe70410b6,
+ 0xe701f034,
+ 0xb601e033,
+ 0x30f00132,
+ 0xde35980c,
+ 0x12b855f9,
+ 0xe41ef406,
+ 0x98f10b98,
+ 0xcbbbf20c,
+ 0xc4b7f102,
+ 0x06b4b607,
+ 0xfc00bbcf,
+ 0xf5e0fcd0,
+ 0xf8034221,
+/* 0x0731: memx_info */
+ 0x01c67000,
+/* 0x0737: memx_info_data */
+ 0xf10e0bf4,
+ 0xf103ccc7,
+ 0xf40800b7,
+/* 0x0742: memx_info_train */
+ 0xc7f10b0e,
+ 0xb7f10bcc,
+/* 0x074a: memx_info_send */
+ 0x21f50100,
+ 0x00f80342,
+/* 0x0750: memx_recv */
+ 0xf401d6b0,
+ 0xd6b0980b,
+ 0xd80bf400,
+/* 0x075e: memx_init */
+ 0x00f800f8,
+/* 0x0760: perf_recv */
+/* 0x0762: perf_init */
+ 0x00f800f8,
+/* 0x0764: i2c_drive_scl */
+ 0xf40036b0,
+ 0x07f1110b,
+ 0x04b607e0,
+ 0x0001d006,
+ 0x00f804bd,
+/* 0x0778: i2c_drive_scl_lo */
+ 0x07e407f1,
+ 0xd00604b6,
+ 0x04bd0001,
+/* 0x0786: i2c_drive_sda */
+ 0x36b000f8,
+ 0x110bf400,
+ 0x07e007f1,
+ 0xd00604b6,
+ 0x04bd0002,
+/* 0x079a: i2c_drive_sda_lo */
+ 0x07f100f8,
+ 0x04b607e4,
+ 0x0002d006,
+ 0x00f804bd,
+/* 0x07a8: i2c_sense_scl */
+ 0xf10132f4,
+ 0xb607c437,
+ 0x33cf0634,
+ 0x0431fd00,
+ 0xf4060bf4,
+/* 0x07be: i2c_sense_scl_done */
+ 0x00f80131,
+/* 0x07c0: i2c_sense_sda */
+ 0xf10132f4,
+ 0xb607c437,
+ 0x33cf0634,
+ 0x0432fd00,
+ 0xf4060bf4,
+/* 0x07d6: i2c_sense_sda_done */
+ 0x00f80131,
+/* 0x07d8: i2c_raise_scl */
+ 0x47f140f9,
+ 0x37f00898,
+ 0x6421f501,
+/* 0x07e5: i2c_raise_scl_wait */
+ 0xe8e7f107,
+ 0x7f21f403,
+ 0x07a821f5,
+ 0xb60901f4,
+ 0x1bf40142,
+/* 0x07f9: i2c_raise_scl_done */
+ 0xf840fcef,
+/* 0x07fd: i2c_start */
+ 0xa821f500,
+ 0x0d11f407,
+ 0x07c021f5,
+ 0xf40611f4,
+/* 0x080e: i2c_start_rep */
+ 0x37f0300e,
+ 0x6421f500,
+ 0x0137f007,
+ 0x078621f5,
+ 0xb60076bb,
+ 0x50f90465,
+ 0xbb046594,
+ 0x50bd0256,
+ 0xfc0475fd,
+ 0xd821f550,
+ 0x0464b607,
+/* 0x083b: i2c_start_send */
+ 0xf01f11f4,
+ 0x21f50037,
+ 0xe7f10786,
+ 0x21f41388,
+ 0x0037f07f,
+ 0x076421f5,
+ 0x1388e7f1,
+/* 0x0857: i2c_start_out */
+ 0xf87f21f4,
+/* 0x0859: i2c_stop */
+ 0x0037f000,
+ 0x076421f5,
+ 0xf50037f0,
+ 0xf1078621,
+ 0xf403e8e7,
+ 0x37f07f21,
+ 0x6421f501,
+ 0x88e7f107,
+ 0x7f21f413,
+ 0xf50137f0,
+ 0xf1078621,
+ 0xf41388e7,
+ 0x00f87f21,
+/* 0x088c: i2c_bitw */
+ 0x078621f5,
+ 0x03e8e7f1,
+ 0xbb7f21f4,
+ 0x65b60076,
+ 0x9450f904,
+ 0x56bb0465,
+ 0xfd50bd02,
+ 0x50fc0475,
+ 0x07d821f5,
+ 0xf40464b6,
+ 0xe7f11811,
+ 0x21f41388,
+ 0x0037f07f,
+ 0x076421f5,
+ 0x1388e7f1,
+/* 0x08cb: i2c_bitw_out */
+ 0xf87f21f4,
+/* 0x08cd: i2c_bitr */
+ 0x0137f000,
+ 0x078621f5,
+ 0x03e8e7f1,
+ 0xbb7f21f4,
+ 0x65b60076,
+ 0x9450f904,
+ 0x56bb0465,
+ 0xfd50bd02,
+ 0x50fc0475,
+ 0x07d821f5,
+ 0xf40464b6,
+ 0x21f51b11,
+ 0x37f007c0,
+ 0x6421f500,
+ 0x88e7f107,
+ 0x7f21f413,
+ 0xf4013cf0,
+/* 0x0912: i2c_bitr_done */
+ 0x00f80131,
+/* 0x0914: i2c_get_byte */
+ 0xf00057f0,
+/* 0x091a: i2c_get_byte_next */
+ 0x54b60847,
+ 0x0076bb01,
+ 0xf90465b6,
+ 0x04659450,
+ 0xbd0256bb,
+ 0x0475fd50,
+ 0x21f550fc,
+ 0x64b608cd,
+ 0x2b11f404,
+ 0xb60553fd,
+ 0x1bf40142,
+ 0x0137f0d8,
+ 0xb60076bb,
+ 0x50f90465,
+ 0xbb046594,
+ 0x50bd0256,
+ 0xfc0475fd,
+ 0x8c21f550,
+ 0x0464b608,
+/* 0x0964: i2c_get_byte_done */
+/* 0x0966: i2c_put_byte */
+ 0x47f000f8,
+/* 0x0969: i2c_put_byte_next */
+ 0x0142b608,
+ 0xbb3854ff,
+ 0x65b60076,
+ 0x9450f904,
+ 0x56bb0465,
+ 0xfd50bd02,
+ 0x50fc0475,
+ 0x088c21f5,
+ 0xf40464b6,
+ 0x46b03411,
+ 0xd81bf400,
+ 0xb60076bb,
+ 0x50f90465,
+ 0xbb046594,
+ 0x50bd0256,
+ 0xfc0475fd,
+ 0xcd21f550,
+ 0x0464b608,
+ 0xbb0f11f4,
+ 0x36b00076,
+ 0x061bf401,
+/* 0x09bf: i2c_put_byte_done */
+ 0xf80132f4,
+/* 0x09c1: i2c_addr */
+ 0x0076bb00,
+ 0xf90465b6,
+ 0x04659450,
+ 0xbd0256bb,
+ 0x0475fd50,
+ 0x21f550fc,
+ 0x64b607fd,
+ 0x2911f404,
+ 0x012ec3e7,
+ 0xfd0134b6,
+ 0x76bb0553,
+ 0x0465b600,
+ 0x659450f9,
+ 0x0256bb04,
+ 0x75fd50bd,
+ 0xf550fc04,
+ 0xb6096621,
+/* 0x0a06: i2c_addr_done */
+ 0x00f80464,
+/* 0x0a08: i2c_acquire_addr */
+ 0xb6f8cec7,
+ 0xe0b702e4,
+ 0xee980d1c,
+/* 0x0a17: i2c_acquire */
+ 0xf500f800,
+ 0xf40a0821,
+ 0xd9f00421,
+ 0x3f21f403,
+/* 0x0a26: i2c_release */
+ 0x21f500f8,
+ 0x21f40a08,
+ 0x03daf004,
+ 0xf83f21f4,
+/* 0x0a35: i2c_recv */
+ 0x0132f400,
+ 0xb6f8c1c7,
+ 0x16b00214,
+ 0x3a1ff528,
+ 0xf413a001,
+ 0x0032980c,
+ 0x0ccc13a0,
+ 0xf4003198,
+ 0xd0f90231,
+ 0xd0f9e0f9,
+ 0x000067f1,
+ 0x100063f1,
+ 0xbb016792,
+ 0x65b60076,
+ 0x9450f904,
+ 0x56bb0465,
+ 0xfd50bd02,
+ 0x50fc0475,
+ 0x0a1721f5,
+ 0xfc0464b6,
+ 0x00d6b0d0,
+ 0x00b31bf5,
+ 0xbb0057f0,
+ 0x65b60076,
+ 0x9450f904,
+ 0x56bb0465,
+ 0xfd50bd02,
+ 0x50fc0475,
+ 0x09c121f5,
+ 0xf50464b6,
+ 0xc700d011,
+ 0x76bbe0c5,
+ 0x0465b600,
+ 0x659450f9,
+ 0x0256bb04,
+ 0x75fd50bd,
+ 0xf550fc04,
+ 0xb6096621,
+ 0x11f50464,
+ 0x57f000ad,
+ 0x0076bb01,
+ 0xf90465b6,
+ 0x04659450,
+ 0xbd0256bb,
+ 0x0475fd50,
+ 0x21f550fc,
+ 0x64b609c1,
+ 0x8a11f504,
+ 0x0076bb00,
+ 0xf90465b6,
+ 0x04659450,
+ 0xbd0256bb,
+ 0x0475fd50,
+ 0x21f550fc,
+ 0x64b60914,
+ 0x6a11f404,
+ 0xbbe05bcb,
+ 0x65b60076,
+ 0x9450f904,
+ 0x56bb0465,
+ 0xfd50bd02,
+ 0x50fc0475,
+ 0x085921f5,
+ 0xb90464b6,
+ 0x74bd025b,
+/* 0x0b3b: i2c_recv_not_rd08 */
+ 0xb0430ef4,
+ 0x1bf401d6,
+ 0x0057f03d,
+ 0x09c121f5,
+ 0xc73311f4,
+ 0x21f5e0c5,
+ 0x11f40966,
+ 0x0057f029,
+ 0x09c121f5,
+ 0xc71f11f4,
+ 0x21f5e0b5,
+ 0x11f40966,
+ 0x5921f515,
+ 0xc774bd08,
+ 0x1bf408c5,
+ 0x0232f409,
+/* 0x0b7b: i2c_recv_not_wr08 */
+/* 0x0b7b: i2c_recv_done */
+ 0xc7030ef4,
+ 0x21f5f8ce,
+ 0xe0fc0a26,
+ 0x12f4d0fc,
+ 0x027cb90a,
+ 0x034221f5,
+/* 0x0b90: i2c_recv_exit */
+/* 0x0b92: i2c_init */
+ 0x00f800f8,
+/* 0x0b94: test_recv */
+ 0x05d817f1,
+ 0xcf0614b6,
+ 0x10b60011,
+ 0xd807f101,
+ 0x0604b605,
+ 0xbd0001d0,
+ 0x00e7f104,
+ 0x4fe3f1d9,
+ 0x6221f513,
+/* 0x0bbb: test_init */
+ 0xf100f802,
+ 0xf50800e7,
+ 0xf8026221,
+/* 0x0bc5: idle_recv */
+/* 0x0bc7: idle */
+ 0xf400f800,
+ 0x17f10031,
+ 0x14b605d4,
+ 0x0011cf06,
+ 0xf10110b6,
+ 0xb605d407,
+ 0x01d00604,
+/* 0x0be3: idle_loop */
+ 0xf004bd00,
+ 0x32f45817,
+/* 0x0be9: idle_proc */
+/* 0x0be9: idle_proc_exec */
+ 0xb910f902,
+ 0x21f5021e,
+ 0x10fc034b,
+ 0xf40911f4,
+ 0x0ef40231,
+/* 0x0bfd: idle_proc_next */
+ 0x5810b6ef,
+ 0xf4061fb8,
+ 0x02f4e61b,
+ 0x0028f4dd,
+ 0x00bb0ef4,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+};
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gf110.fuc4 b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gf110.fuc4
new file mode 100644
index 000000000000..ae9c3f18ae01
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gf110.fuc4
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#define NVKM_PPWR_CHIPSET GF119
+#define HW_TICKS_PER_US 324
+
+//#define NVKM_FALCON_PC24
+#define NVKM_FALCON_UNSHIFTED_IO
+//#define NVKM_FALCON_MMIO_UAS
+//#define NVKM_FALCON_MMIO_TRAP
+
+#include "macros.fuc"
+
+.section #gf110_pmu_data
+#define INCLUDE_PROC
+#include "kernel.fuc"
+#include "arith.fuc"
+#include "host.fuc"
+#include "memx.fuc"
+#include "perf.fuc"
+#include "i2c_.fuc"
+#include "test.fuc"
+#include "idle.fuc"
+#undef INCLUDE_PROC
+
+#define INCLUDE_DATA
+#include "kernel.fuc"
+#include "arith.fuc"
+#include "host.fuc"
+#include "memx.fuc"
+#include "perf.fuc"
+#include "i2c_.fuc"
+#include "test.fuc"
+#include "idle.fuc"
+#undef INCLUDE_DATA
+.align 256
+
+.section #gf110_pmu_code
+#define INCLUDE_CODE
+#include "kernel.fuc"
+#include "arith.fuc"
+#include "host.fuc"
+#include "memx.fuc"
+#include "perf.fuc"
+#include "i2c_.fuc"
+#include "test.fuc"
+#include "idle.fuc"
+#undef INCLUDE_CODE
+.align 256
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gf110.fuc4.h b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gf110.fuc4.h
new file mode 100644
index 000000000000..a0c499e4543c
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gf110.fuc4.h
@@ -0,0 +1,1795 @@
+uint32_t gf110_pmu_data[] = {
+/* 0x0000: proc_kern */
+ 0x52544e49,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+/* 0x0058: proc_list_head */
+ 0x54534f48,
+ 0x0000049d,
+ 0x00000446,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x584d454d,
+ 0x0000068b,
+ 0x0000067d,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x46524550,
+ 0x0000068f,
+ 0x0000068d,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x5f433249,
+ 0x00000aaa,
+ 0x0000094d,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x54534554,
+ 0x00000acd,
+ 0x00000aac,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x454c4449,
+ 0x00000ad9,
+ 0x00000ad7,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+/* 0x0268: proc_list_tail */
+/* 0x0268: time_prev */
+ 0x00000000,
+/* 0x026c: time_next */
+ 0x00000000,
+/* 0x0270: fifo_queue */
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+/* 0x02f0: rfifo_queue */
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+/* 0x0370: memx_func_head */
+ 0x00000001,
+ 0x00000000,
+ 0x000004d3,
+/* 0x037c: memx_func_next */
+ 0x00000002,
+ 0x00000000,
+ 0x00000554,
+ 0x00000003,
+ 0x00000002,
+ 0x000005d8,
+ 0x00040004,
+ 0x00000000,
+ 0x000005f4,
+ 0x00010005,
+ 0x00000000,
+ 0x0000060e,
+ 0x00010006,
+ 0x00000000,
+ 0x000005d3,
+ 0x00000007,
+ 0x00000000,
+ 0x00000619,
+/* 0x03c4: memx_func_tail */
+/* 0x03c4: memx_ts_start */
+ 0x00000000,
+/* 0x03c8: memx_ts_end */
+ 0x00000000,
+/* 0x03cc: memx_data_head */
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+/* 0x0bcc: memx_data_tail */
+/* 0x0bcc: memx_train_head */
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+/* 0x0ccc: memx_train_tail */
+/* 0x0ccc: i2c_scl_map */
+ 0x00000400,
+ 0x00000800,
+ 0x00001000,
+ 0x00002000,
+ 0x00004000,
+ 0x00008000,
+ 0x00010000,
+ 0x00020000,
+ 0x00040000,
+ 0x00080000,
+/* 0x0cf4: i2c_sda_map */
+ 0x00100000,
+ 0x00200000,
+ 0x00400000,
+ 0x00800000,
+ 0x01000000,
+ 0x02000000,
+ 0x04000000,
+ 0x08000000,
+ 0x10000000,
+ 0x20000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+};
+
+uint32_t gf110_pmu_code[] = {
+ 0x034d0ef5,
+/* 0x0004: rd32 */
+ 0x07a007f1,
+ 0xbd000ed0,
+ 0x01d7f004,
+ 0xf101d3f0,
+ 0xd007ac07,
+ 0x04bd000d,
+/* 0x001c: rd32_wait */
+ 0x07acd7f1,
+ 0xf100ddcf,
+ 0xf47000d4,
+ 0xd7f1f51b,
+ 0xddcf07a4,
+/* 0x0033: wr32 */
+ 0xf100f800,
+ 0xd007a007,
+ 0x04bd000e,
+ 0x07a407f1,
+ 0xbd000dd0,
+ 0x02d7f004,
+ 0xf0f0d5f0,
+ 0x07f101d3,
+ 0x0dd007ac,
+/* 0x0057: wr32_wait */
+ 0xf104bd00,
+ 0xcf07acd7,
+ 0xd4f100dd,
+ 0x1bf47000,
+/* 0x0067: nsec */
+ 0xf900f8f5,
+ 0xf080f990,
+ 0x88cf2c87,
+/* 0x0071: nsec_loop */
+ 0x2c97f000,
+ 0xbb0099cf,
+ 0x9eb80298,
+ 0xf41ef406,
+ 0x90fc80fc,
+/* 0x0086: wait */
+ 0x90f900f8,
+ 0x87f080f9,
+ 0x0088cf2c,
+/* 0x0090: wait_loop */
+ 0xf402eeb9,
+ 0xdab90421,
+ 0x04adfd02,
+ 0xf406acb8,
+ 0x97f0120b,
+ 0x0099cf2c,
+ 0xb80298bb,
+ 0x1ef4069b,
+/* 0x00b1: wait_done */
+ 0xfc80fce2,
+/* 0x00b7: intr_watchdog */
+ 0x9800f890,
+ 0x96b003e9,
+ 0x2a0bf400,
+ 0xbb9a0a98,
+ 0x1cf4029a,
+ 0x01d7f00f,
+ 0x028c21f5,
+ 0x0ef494bd,
+/* 0x00d5: intr_watchdog_next_time */
+ 0x9b0a9815,
+ 0xf400a6b0,
+ 0x9ab8090b,
+ 0x061cf406,
+/* 0x00e4: intr_watchdog_next_time_set */
+/* 0x00e7: intr_watchdog_next_proc */
+ 0x809b0980,
+ 0xe0b603e9,
+ 0x68e6b158,
+ 0xc61bf402,
+/* 0x00f6: intr */
+ 0x00f900f8,
+ 0x80f904bd,
+ 0xa0f990f9,
+ 0xc0f9b0f9,
+ 0xe0f9d0f9,
+ 0xf7f0f0f9,
+ 0x0188fe00,
+ 0x87f180f9,
+ 0x88cf05d0,
+ 0x0180b600,
+ 0x05d007f1,
+ 0xbd0008d0,
+ 0x0887f004,
+ 0xc40088cf,
+ 0x0bf40289,
+ 0x9b008020,
+ 0xf458e7f0,
+ 0x0998b721,
+ 0x0096b09b,
+ 0xf00e0bf4,
+ 0x09d03407,
+ 0x8004bd00,
+/* 0x014e: intr_skip_watchdog */
+ 0x89e49a09,
+ 0x0bf40800,
+ 0x8897f13c,
+ 0x0099cf06,
+ 0xf4029ac4,
+ 0xc7f1260b,
+ 0xcccf04c0,
+ 0xf1c0f900,
+ 0xf14f48e7,
+ 0xf05453e3,
+ 0x21f500d7,
+ 0xc0fc02f1,
+ 0x04c007f1,
+ 0xbd000cd0,
+/* 0x0185: intr_subintr_skip_fifo */
+ 0x8807f104,
+ 0x0009d006,
+/* 0x018e: intr_skip_subintr */
+ 0x89c404bd,
+ 0x070bf420,
+ 0xffbfa4f1,
+/* 0x0198: intr_skip_pause */
+ 0xf44089c4,
+ 0xa4f1070b,
+/* 0x01a2: intr_skip_user0 */
+ 0x07f0ffbf,
+ 0x0008d004,
+ 0x80fc04bd,
+ 0xfc0088fe,
+ 0xfce0fcf0,
+ 0xfcc0fcd0,
+ 0xfca0fcb0,
+ 0xfc80fc90,
+ 0x0032f400,
+/* 0x01c6: ticks_from_ns */
+ 0xc0f901f8,
+ 0xd7f1b0f9,
+ 0xd3f00144,
+ 0xb321f500,
+ 0xe8ccec03,
+ 0x00b4b003,
+ 0xec120bf4,
+ 0xf103e8ee,
+ 0xf00144d7,
+ 0x21f500d3,
+/* 0x01ee: ticks_from_ns_quit */
+ 0xceb903b3,
+ 0xfcb0fc02,
+/* 0x01f7: ticks_from_us */
+ 0xf900f8c0,
+ 0xf1b0f9c0,
+ 0xf00144d7,
+ 0x21f500d3,
+ 0xceb903b3,
+ 0x00b4b002,
+ 0xbd050bf4,
+/* 0x0211: ticks_from_us_quit */
+ 0xfcb0fce4,
+/* 0x0217: ticks_to_us */
+ 0xf100f8c0,
+ 0xf00144d7,
+ 0xedff00d3,
+/* 0x0223: timer */
+ 0xf900f8ec,
+ 0xf480f990,
+ 0xf8981032,
+ 0x0086b003,
+ 0xbd531cf4,
+ 0x3807f084,
+ 0xbd0008d0,
+ 0x3487f004,
+ 0x980088cf,
+ 0x98bb9a09,
+ 0x00e9bb02,
+ 0xf003fe80,
+ 0x88cf0887,
+ 0x0284f000,
+ 0xf0201bf4,
+ 0x88cf3487,
+ 0x06e0b800,
+ 0xb8090bf4,
+ 0x1cf406e8,
+/* 0x026d: timer_reset */
+ 0x3407f00e,
+ 0xbd000ed0,
+ 0x9a0e8004,
+/* 0x0278: timer_enable */
+ 0xf00187f0,
+ 0x08d03807,
+/* 0x0283: timer_done */
+ 0xf404bd00,
+ 0x80fc1031,
+ 0x00f890fc,
+/* 0x028c: send_proc */
+ 0x90f980f9,
+ 0x9805e898,
+ 0x86f004e9,
+ 0x0689b804,
+ 0xc42a0bf4,
+ 0x88940398,
+ 0x1880b604,
+ 0x98008ebb,
+ 0x8a8000fa,
+ 0x018d8000,
+ 0x80028c80,
+ 0x90b6038b,
+ 0x0794f001,
+ 0xf404e980,
+/* 0x02c6: send_done */
+ 0x90fc0231,
+ 0x00f880fc,
+/* 0x02cc: find */
+ 0x87f080f9,
+ 0x0131f458,
+/* 0x02d4: find_loop */
+ 0xb8008a98,
+ 0x0bf406ae,
+ 0x5880b610,
+ 0x026886b1,
+ 0xf4f01bf4,
+/* 0x02ea: find_done */
+ 0x8eb90132,
+ 0xf880fc02,
+/* 0x02f1: send */
+ 0xcc21f500,
+ 0x9701f402,
+/* 0x02fa: recv */
+ 0x90f900f8,
+ 0xe89880f9,
+ 0x04e99805,
+ 0xb80132f4,
+ 0x0bf40689,
+ 0x0389c43d,
+ 0xf00180b6,
+ 0xe8800784,
+ 0x02ea9805,
+ 0x8ffef0f9,
+ 0xb9f0f901,
+ 0x999402ef,
+ 0x00e9bb04,
+ 0x9818e0b6,
+ 0xec9803eb,
+ 0x01ed9802,
+ 0xf900ee98,
+ 0xfef0fca5,
+ 0x31f400f8,
+/* 0x0347: recv_done */
+ 0xfcf0fc01,
+ 0xf890fc80,
+/* 0x034d: init */
+ 0x0817f100,
+ 0x0011cf01,
+ 0x010911e7,
+ 0xfe0814b6,
+ 0x17f10014,
+ 0x13f000e0,
+ 0x1c07f000,
+ 0xbd0001d0,
+ 0xff17f004,
+ 0xd01407f0,
+ 0x04bd0001,
+ 0xf10217f0,
+ 0xf0080015,
+ 0x01d01007,
+ 0xf104bd00,
+ 0xf000f617,
+ 0x10fe0013,
+ 0x1031f400,
+ 0xf00117f0,
+ 0x01d03807,
+ 0xf004bd00,
+/* 0x03a2: init_proc */
+ 0xf19858f7,
+ 0x0016b001,
+ 0xf9fa0bf4,
+ 0x58f0b615,
+/* 0x03b3: mulu32_32_64 */
+ 0xf9f20ef4,
+ 0xf920f910,
+ 0x9540f930,
+ 0xd29510e1,
+ 0xbdc4bd10,
+ 0xc0edffb4,
+ 0xb9301dff,
+ 0x34f10234,
+ 0x34b6ffff,
+ 0x1045b610,
+ 0xbb00c3bb,
+ 0xe2ff01b4,
+ 0x0234b930,
+ 0xffff34f1,
+ 0xb61034b6,
+ 0xc3bb1045,
+ 0x01b4bb00,
+ 0xbb3012ff,
+ 0x40fc00b3,
+ 0x20fc30fc,
+ 0x00f810fc,
+/* 0x0404: host_send */
+ 0x04b017f1,
+ 0xf10011cf,
+ 0xcf04a027,
+ 0x12b80022,
+ 0x2f0bf406,
+ 0x94071ec4,
+ 0xe0b704ee,
+ 0xeb980270,
+ 0x02ec9803,
+ 0x9801ed98,
+ 0x21f500ee,
+ 0x10b602f1,
+ 0x0f1ec401,
+ 0x04b007f1,
+ 0xbd000ed0,
+ 0xc30ef404,
+/* 0x0444: host_send_done */
+/* 0x0446: host_recv */
+ 0x17f100f8,
+ 0x13f14e49,
+ 0xe1b85254,
+ 0xb30bf406,
+/* 0x0454: host_recv_wait */
+ 0x04cc17f1,
+ 0xf10011cf,
+ 0xcf04c827,
+ 0x16f00022,
+ 0x0612b808,
+ 0xc4ec0bf4,
+ 0x34b60723,
+ 0xf030b704,
+ 0x033b8002,
+ 0x80023c80,
+ 0x3e80013d,
+ 0x0120b600,
+ 0xf10f24f0,
+ 0xd004c807,
+ 0x04bd0002,
+ 0xf04027f0,
+ 0x02d00007,
+ 0xf804bd00,
+/* 0x049d: host_init */
+ 0x8017f100,
+ 0x1014b600,
+ 0x027015f1,
+ 0x04d007f1,
+ 0xbd0001d0,
+ 0x8017f104,
+ 0x1014b600,
+ 0x02f015f1,
+ 0x04dc07f1,
+ 0xbd0001d0,
+ 0x0117f004,
+ 0x04c407f1,
+ 0xbd0001d0,
+/* 0x04d3: memx_func_enter */
+ 0xf100f804,
+ 0xf1162067,
+ 0xf1f55d77,
+ 0xb9ffff73,
+ 0x21f4026e,
+ 0x02d8b904,
+ 0xf90487fd,
+ 0xfc80f960,
+ 0xf4e0fcd0,
+ 0x77f13321,
+ 0x73f1fffe,
+ 0x6eb9ffff,
+ 0x0421f402,
+ 0xfd02d8b9,
+ 0x60f90487,
+ 0xd0fc80f9,
+ 0x21f4e0fc,
+ 0xf067f133,
+ 0x026eb926,
+ 0xb90421f4,
+ 0x87fd02d8,
+ 0xf960f904,
+ 0xfcd0fc80,
+ 0x3321f4e0,
+ 0xf10467f0,
+ 0xd007e007,
+ 0x04bd0006,
+/* 0x053c: memx_func_enter_wait */
+ 0x07c067f1,
+ 0xf00066cf,
+ 0x0bf40464,
+ 0x2c67f0f6,
+ 0x800066cf,
+ 0x00f8f106,
+/* 0x0554: memx_func_leave */
+ 0xcf2c67f0,
+ 0x06800066,
+ 0x0467f0f2,
+ 0x07e407f1,
+ 0xbd0006d0,
+/* 0x0569: memx_func_leave_wait */
+ 0xc067f104,
+ 0x0066cf07,
+ 0xf40464f0,
+ 0x67f1f61b,
+ 0x77f126f0,
+ 0x73f00001,
+ 0x026eb900,
+ 0xb90421f4,
+ 0x87fd02d8,
+ 0xf960f905,
+ 0xfcd0fc80,
+ 0x3321f4e0,
+ 0x162067f1,
+ 0xf4026eb9,
+ 0xd8b90421,
+ 0x0587fd02,
+ 0x80f960f9,
+ 0xe0fcd0fc,
+ 0xf13321f4,
+ 0xf00aa277,
+ 0x6eb90073,
+ 0x0421f402,
+ 0xfd02d8b9,
+ 0x60f90587,
+ 0xd0fc80f9,
+ 0x21f4e0fc,
+/* 0x05d3: memx_func_wait_vblank */
+ 0xb600f833,
+ 0x00f80410,
+/* 0x05d8: memx_func_wr32 */
+ 0x98001698,
+ 0x10b60115,
+ 0xf960f908,
+ 0xfcd0fc50,
+ 0x3321f4e0,
+ 0xf40242b6,
+ 0x00f8e91b,
+/* 0x05f4: memx_func_wait */
+ 0xcf2c87f0,
+ 0x1e980088,
+ 0x011d9800,
+ 0x98021c98,
+ 0x10b6031b,
+ 0x8621f410,
+/* 0x060e: memx_func_delay */
+ 0x1e9800f8,
+ 0x0410b600,
+ 0xf86721f4,
+/* 0x0619: memx_func_train */
+/* 0x061b: memx_exec */
+ 0xf900f800,
+ 0xb9d0f9e0,
+ 0xb2b902c1,
+/* 0x0625: memx_exec_next */
+ 0x00139802,
+ 0xe70410b6,
+ 0xe701f034,
+ 0xb601e033,
+ 0x30f00132,
+ 0xde35980c,
+ 0x12b855f9,
+ 0xe41ef406,
+ 0x98f10b98,
+ 0xcbbbf20c,
+ 0xc4b7f102,
+ 0x00bbcf07,
+ 0xe0fcd0fc,
+ 0x02f121f5,
+/* 0x065e: memx_info */
+ 0xc67000f8,
+ 0x0e0bf401,
+/* 0x0664: memx_info_data */
+ 0x03ccc7f1,
+ 0x0800b7f1,
+/* 0x066f: memx_info_train */
+ 0xf10b0ef4,
+ 0xf10bccc7,
+/* 0x0677: memx_info_send */
+ 0xf50100b7,
+ 0xf802f121,
+/* 0x067d: memx_recv */
+ 0x01d6b000,
+ 0xb09b0bf4,
+ 0x0bf400d6,
+/* 0x068b: memx_init */
+ 0xf800f8d8,
+/* 0x068d: perf_recv */
+/* 0x068f: perf_init */
+ 0xf800f800,
+/* 0x0691: i2c_drive_scl */
+ 0x0036b000,
+ 0xf10e0bf4,
+ 0xd007e007,
+ 0x04bd0001,
+/* 0x06a2: i2c_drive_scl_lo */
+ 0x07f100f8,
+ 0x01d007e4,
+ 0xf804bd00,
+/* 0x06ad: i2c_drive_sda */
+ 0x0036b000,
+ 0xf10e0bf4,
+ 0xd007e007,
+ 0x04bd0002,
+/* 0x06be: i2c_drive_sda_lo */
+ 0x07f100f8,
+ 0x02d007e4,
+ 0xf804bd00,
+/* 0x06c9: i2c_sense_scl */
+ 0x0132f400,
+ 0x07c437f1,
+ 0xfd0033cf,
+ 0x0bf40431,
+ 0x0131f406,
+/* 0x06dc: i2c_sense_scl_done */
+/* 0x06de: i2c_sense_sda */
+ 0x32f400f8,
+ 0xc437f101,
+ 0x0033cf07,
+ 0xf40432fd,
+ 0x31f4060b,
+/* 0x06f1: i2c_sense_sda_done */
+/* 0x06f3: i2c_raise_scl */
+ 0xf900f801,
+ 0x9847f140,
+ 0x0137f008,
+ 0x069121f5,
+/* 0x0700: i2c_raise_scl_wait */
+ 0x03e8e7f1,
+ 0xf56721f4,
+ 0xf406c921,
+ 0x42b60901,
+ 0xef1bf401,
+/* 0x0714: i2c_raise_scl_done */
+ 0x00f840fc,
+/* 0x0718: i2c_start */
+ 0x06c921f5,
+ 0xf50d11f4,
+ 0xf406de21,
+ 0x0ef40611,
+/* 0x0729: i2c_start_rep */
+ 0x0037f030,
+ 0x069121f5,
+ 0xf50137f0,
+ 0xbb06ad21,
+ 0x65b60076,
+ 0x9450f904,
+ 0x56bb0465,
+ 0xfd50bd02,
+ 0x50fc0475,
+ 0x06f321f5,
+ 0xf40464b6,
+/* 0x0756: i2c_start_send */
+ 0x37f01f11,
+ 0xad21f500,
+ 0x88e7f106,
+ 0x6721f413,
+ 0xf50037f0,
+ 0xf1069121,
+ 0xf41388e7,
+/* 0x0772: i2c_start_out */
+ 0x00f86721,
+/* 0x0774: i2c_stop */
+ 0xf50037f0,
+ 0xf0069121,
+ 0x21f50037,
+ 0xe7f106ad,
+ 0x21f403e8,
+ 0x0137f067,
+ 0x069121f5,
+ 0x1388e7f1,
+ 0xf06721f4,
+ 0x21f50137,
+ 0xe7f106ad,
+ 0x21f41388,
+/* 0x07a7: i2c_bitw */
+ 0xf500f867,
+ 0xf106ad21,
+ 0xf403e8e7,
+ 0x76bb6721,
+ 0x0465b600,
+ 0x659450f9,
+ 0x0256bb04,
+ 0x75fd50bd,
+ 0xf550fc04,
+ 0xb606f321,
+ 0x11f40464,
+ 0x88e7f118,
+ 0x6721f413,
+ 0xf50037f0,
+ 0xf1069121,
+ 0xf41388e7,
+/* 0x07e6: i2c_bitw_out */
+ 0x00f86721,
+/* 0x07e8: i2c_bitr */
+ 0xf50137f0,
+ 0xf106ad21,
+ 0xf403e8e7,
+ 0x76bb6721,
+ 0x0465b600,
+ 0x659450f9,
+ 0x0256bb04,
+ 0x75fd50bd,
+ 0xf550fc04,
+ 0xb606f321,
+ 0x11f40464,
+ 0xde21f51b,
+ 0x0037f006,
+ 0x069121f5,
+ 0x1388e7f1,
+ 0xf06721f4,
+ 0x31f4013c,
+/* 0x082d: i2c_bitr_done */
+/* 0x082f: i2c_get_byte */
+ 0xf000f801,
+ 0x47f00057,
+/* 0x0835: i2c_get_byte_next */
+ 0x0154b608,
+ 0xb60076bb,
+ 0x50f90465,
+ 0xbb046594,
+ 0x50bd0256,
+ 0xfc0475fd,
+ 0xe821f550,
+ 0x0464b607,
+ 0xfd2b11f4,
+ 0x42b60553,
+ 0xd81bf401,
+ 0xbb0137f0,
+ 0x65b60076,
+ 0x9450f904,
+ 0x56bb0465,
+ 0xfd50bd02,
+ 0x50fc0475,
+ 0x07a721f5,
+/* 0x087f: i2c_get_byte_done */
+ 0xf80464b6,
+/* 0x0881: i2c_put_byte */
+ 0x0847f000,
+/* 0x0884: i2c_put_byte_next */
+ 0xff0142b6,
+ 0x76bb3854,
+ 0x0465b600,
+ 0x659450f9,
+ 0x0256bb04,
+ 0x75fd50bd,
+ 0xf550fc04,
+ 0xb607a721,
+ 0x11f40464,
+ 0x0046b034,
+ 0xbbd81bf4,
+ 0x65b60076,
+ 0x9450f904,
+ 0x56bb0465,
+ 0xfd50bd02,
+ 0x50fc0475,
+ 0x07e821f5,
+ 0xf40464b6,
+ 0x76bb0f11,
+ 0x0136b000,
+ 0xf4061bf4,
+/* 0x08da: i2c_put_byte_done */
+ 0x00f80132,
+/* 0x08dc: i2c_addr */
+ 0xb60076bb,
+ 0x50f90465,
+ 0xbb046594,
+ 0x50bd0256,
+ 0xfc0475fd,
+ 0x1821f550,
+ 0x0464b607,
+ 0xe72911f4,
+ 0xb6012ec3,
+ 0x53fd0134,
+ 0x0076bb05,
+ 0xf90465b6,
+ 0x04659450,
+ 0xbd0256bb,
+ 0x0475fd50,
+ 0x21f550fc,
+ 0x64b60881,
+/* 0x0921: i2c_addr_done */
+/* 0x0923: i2c_acquire_addr */
+ 0xc700f804,
+ 0xe4b6f8ce,
+ 0x14e0b705,
+/* 0x092f: i2c_acquire */
+ 0xf500f8d0,
+ 0xf4092321,
+ 0xd9f00421,
+ 0x3321f403,
+/* 0x093e: i2c_release */
+ 0x21f500f8,
+ 0x21f40923,
+ 0x03daf004,
+ 0xf83321f4,
+/* 0x094d: i2c_recv */
+ 0x0132f400,
+ 0xb6f8c1c7,
+ 0x16b00214,
+ 0x3a1ff528,
+ 0xf413a001,
+ 0x0032980c,
+ 0x0ccc13a0,
+ 0xf4003198,
+ 0xd0f90231,
+ 0xd0f9e0f9,
+ 0x000067f1,
+ 0x100063f1,
+ 0xbb016792,
+ 0x65b60076,
+ 0x9450f904,
+ 0x56bb0465,
+ 0xfd50bd02,
+ 0x50fc0475,
+ 0x092f21f5,
+ 0xfc0464b6,
+ 0x00d6b0d0,
+ 0x00b31bf5,
+ 0xbb0057f0,
+ 0x65b60076,
+ 0x9450f904,
+ 0x56bb0465,
+ 0xfd50bd02,
+ 0x50fc0475,
+ 0x08dc21f5,
+ 0xf50464b6,
+ 0xc700d011,
+ 0x76bbe0c5,
+ 0x0465b600,
+ 0x659450f9,
+ 0x0256bb04,
+ 0x75fd50bd,
+ 0xf550fc04,
+ 0xb6088121,
+ 0x11f50464,
+ 0x57f000ad,
+ 0x0076bb01,
+ 0xf90465b6,
+ 0x04659450,
+ 0xbd0256bb,
+ 0x0475fd50,
+ 0x21f550fc,
+ 0x64b608dc,
+ 0x8a11f504,
+ 0x0076bb00,
+ 0xf90465b6,
+ 0x04659450,
+ 0xbd0256bb,
+ 0x0475fd50,
+ 0x21f550fc,
+ 0x64b6082f,
+ 0x6a11f404,
+ 0xbbe05bcb,
+ 0x65b60076,
+ 0x9450f904,
+ 0x56bb0465,
+ 0xfd50bd02,
+ 0x50fc0475,
+ 0x077421f5,
+ 0xb90464b6,
+ 0x74bd025b,
+/* 0x0a53: i2c_recv_not_rd08 */
+ 0xb0430ef4,
+ 0x1bf401d6,
+ 0x0057f03d,
+ 0x08dc21f5,
+ 0xc73311f4,
+ 0x21f5e0c5,
+ 0x11f40881,
+ 0x0057f029,
+ 0x08dc21f5,
+ 0xc71f11f4,
+ 0x21f5e0b5,
+ 0x11f40881,
+ 0x7421f515,
+ 0xc774bd07,
+ 0x1bf408c5,
+ 0x0232f409,
+/* 0x0a93: i2c_recv_not_wr08 */
+/* 0x0a93: i2c_recv_done */
+ 0xc7030ef4,
+ 0x21f5f8ce,
+ 0xe0fc093e,
+ 0x12f4d0fc,
+ 0x027cb90a,
+ 0x02f121f5,
+/* 0x0aa8: i2c_recv_exit */
+/* 0x0aaa: i2c_init */
+ 0x00f800f8,
+/* 0x0aac: test_recv */
+ 0x05d817f1,
+ 0xb60011cf,
+ 0x07f10110,
+ 0x01d005d8,
+ 0xf104bd00,
+ 0xf1d900e7,
+ 0xf5134fe3,
+ 0xf8022321,
+/* 0x0acd: test_init */
+ 0x00e7f100,
+ 0x2321f508,
+/* 0x0ad7: idle_recv */
+ 0xf800f802,
+/* 0x0ad9: idle */
+ 0x0031f400,
+ 0x05d417f1,
+ 0xb60011cf,
+ 0x07f10110,
+ 0x01d005d4,
+/* 0x0aef: idle_loop */
+ 0xf004bd00,
+ 0x32f45817,
+/* 0x0af5: idle_proc */
+/* 0x0af5: idle_proc_exec */
+ 0xb910f902,
+ 0x21f5021e,
+ 0x10fc02fa,
+ 0xf40911f4,
+ 0x0ef40231,
+/* 0x0b09: idle_proc_next */
+ 0x5810b6ef,
+ 0xf4061fb8,
+ 0x02f4e61b,
+ 0x0028f4dd,
+ 0x00c10ef4,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+};
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gk208.fuc5 b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gk208.fuc5
new file mode 100644
index 000000000000..093dc81880f4
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gk208.fuc5
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#define NVKM_PPWR_CHIPSET GK208
+#define HW_TICKS_PER_US 324
+
+#define NVKM_FALCON_PC24
+#define NVKM_FALCON_UNSHIFTED_IO
+//#define NVKM_FALCON_MMIO_UAS
+//#define NVKM_FALCON_MMIO_TRAP
+
+#include "macros.fuc"
+
+.section #gk208_pmu_data
+#define INCLUDE_PROC
+#include "kernel.fuc"
+#include "arith.fuc"
+#include "host.fuc"
+#include "memx.fuc"
+#include "perf.fuc"
+#include "i2c_.fuc"
+#include "test.fuc"
+#include "idle.fuc"
+#undef INCLUDE_PROC
+
+#define INCLUDE_DATA
+#include "kernel.fuc"
+#include "arith.fuc"
+#include "host.fuc"
+#include "memx.fuc"
+#include "perf.fuc"
+#include "i2c_.fuc"
+#include "test.fuc"
+#include "idle.fuc"
+#undef INCLUDE_DATA
+.align 256
+
+.section #gk208_pmu_code
+#define INCLUDE_CODE
+#include "kernel.fuc"
+#include "arith.fuc"
+#include "host.fuc"
+#include "memx.fuc"
+#include "perf.fuc"
+#include "i2c_.fuc"
+#include "test.fuc"
+#include "idle.fuc"
+#undef INCLUDE_CODE
+.align 256
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gk208.fuc5.h b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gk208.fuc5.h
new file mode 100644
index 000000000000..fe4f63deeaab
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gk208.fuc5.h
@@ -0,0 +1,1731 @@
+uint32_t gk208_pmu_data[] = {
+/* 0x0000: proc_kern */
+ 0x52544e49,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+/* 0x0058: proc_list_head */
+ 0x54534f48,
+ 0x00000453,
+ 0x00000404,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x584d454d,
+ 0x0000062d,
+ 0x0000061f,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x46524550,
+ 0x00000631,
+ 0x0000062f,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x5f433249,
+ 0x00000a35,
+ 0x000008dc,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x54534554,
+ 0x00000a56,
+ 0x00000a37,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x454c4449,
+ 0x00000a61,
+ 0x00000a5f,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+/* 0x0268: proc_list_tail */
+/* 0x0268: time_prev */
+ 0x00000000,
+/* 0x026c: time_next */
+ 0x00000000,
+/* 0x0270: fifo_queue */
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+/* 0x02f0: rfifo_queue */
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+/* 0x0370: memx_func_head */
+ 0x00000001,
+ 0x00000000,
+ 0x00000483,
+/* 0x037c: memx_func_next */
+ 0x00000002,
+ 0x00000000,
+ 0x00000500,
+ 0x00000003,
+ 0x00000002,
+ 0x00000580,
+ 0x00040004,
+ 0x00000000,
+ 0x0000059d,
+ 0x00010005,
+ 0x00000000,
+ 0x000005b7,
+ 0x00010006,
+ 0x00000000,
+ 0x0000057b,
+ 0x00000007,
+ 0x00000000,
+ 0x000005c3,
+/* 0x03c4: memx_func_tail */
+/* 0x03c4: memx_ts_start */
+ 0x00000000,
+/* 0x03c8: memx_ts_end */
+ 0x00000000,
+/* 0x03cc: memx_data_head */
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+/* 0x0bcc: memx_data_tail */
+/* 0x0bcc: memx_train_head */
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+/* 0x0ccc: memx_train_tail */
+/* 0x0ccc: i2c_scl_map */
+ 0x00000400,
+ 0x00000800,
+ 0x00001000,
+ 0x00002000,
+ 0x00004000,
+ 0x00008000,
+ 0x00010000,
+ 0x00020000,
+ 0x00040000,
+ 0x00080000,
+/* 0x0cf4: i2c_sda_map */
+ 0x00100000,
+ 0x00200000,
+ 0x00400000,
+ 0x00800000,
+ 0x01000000,
+ 0x02000000,
+ 0x04000000,
+ 0x08000000,
+ 0x10000000,
+ 0x20000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+};
+
+uint32_t gk208_pmu_code[] = {
+ 0x031c0ef5,
+/* 0x0004: rd32 */
+ 0xf607a040,
+ 0x04bd000e,
+ 0xd3f0010d,
+ 0x07ac4001,
+ 0xbd000df6,
+/* 0x0019: rd32_wait */
+ 0x07ac4d04,
+ 0xf100ddcf,
+ 0xf47000d4,
+ 0xa44df61b,
+ 0x00ddcf07,
+/* 0x002e: wr32 */
+ 0xa04000f8,
+ 0x000ef607,
+ 0xa44004bd,
+ 0x000df607,
+ 0x020d04bd,
+ 0xf0f0d5f0,
+ 0xac4001d3,
+ 0x000df607,
+/* 0x004e: wr32_wait */
+ 0xac4d04bd,
+ 0x00ddcf07,
+ 0x7000d4f1,
+ 0xf8f61bf4,
+/* 0x005d: nsec */
+ 0xf990f900,
+ 0xcf2c0880,
+/* 0x0066: nsec_loop */
+ 0x2c090088,
+ 0xbb0099cf,
+ 0x9ea60298,
+ 0xfcf61ef4,
+ 0xf890fc80,
+/* 0x0079: wait */
+ 0xf990f900,
+ 0xcf2c0880,
+/* 0x0082: wait_loop */
+ 0xeeb20088,
+ 0x0000047e,
+ 0xadfddab2,
+ 0xf4aca604,
+ 0x2c09100b,
+ 0xbb0099cf,
+ 0x9ba60298,
+/* 0x009f: wait_done */
+ 0xfce61ef4,
+ 0xf890fc80,
+/* 0x00a5: intr_watchdog */
+ 0x03e99800,
+ 0xf40096b0,
+ 0x0a98280b,
+ 0x029abb9a,
+ 0x0d0e1cf4,
+ 0x02617e01,
+ 0xf494bd00,
+/* 0x00c2: intr_watchdog_next_time */
+ 0x0a98140e,
+ 0x00a6b09b,
+ 0xa6080bf4,
+ 0x061cf49a,
+/* 0x00d0: intr_watchdog_next_time_set */
+/* 0x00d3: intr_watchdog_next_proc */
+ 0xb59b09b5,
+ 0xe0b603e9,
+ 0x68e6b158,
+ 0xc81bf402,
+/* 0x00e2: intr */
+ 0x00f900f8,
+ 0x80f904bd,
+ 0xa0f990f9,
+ 0xc0f9b0f9,
+ 0xe0f9d0f9,
+ 0x000ff0f9,
+ 0xf90188fe,
+ 0x04504880,
+ 0xb60088cf,
+ 0x50400180,
+ 0x0008f604,
+ 0x080804bd,
+ 0xc40088cf,
+ 0x0bf40289,
+ 0x9b00b51f,
+ 0xa57e580e,
+ 0x09980000,
+ 0x0096b09b,
+ 0x000d0bf4,
+ 0x0009f634,
+ 0x09b504bd,
+/* 0x0135: intr_skip_watchdog */
+ 0x0089e49a,
+ 0x360bf408,
+ 0xcf068849,
+ 0x9ac40099,
+ 0x220bf402,
+ 0xcf04c04c,
+ 0xc0f900cc,
+ 0xf14f484e,
+ 0x0d5453e3,
+ 0x02c27e00,
+ 0x40c0fc00,
+ 0x0cf604c0,
+/* 0x0167: intr_subintr_skip_fifo */
+ 0x4004bd00,
+ 0x09f60688,
+/* 0x016f: intr_skip_subintr */
+ 0xc404bd00,
+ 0x0bf42089,
+ 0xbfa4f107,
+/* 0x0179: intr_skip_pause */
+ 0x4089c4ff,
+ 0xf1070bf4,
+/* 0x0183: intr_skip_user0 */
+ 0x00ffbfa4,
+ 0x0008f604,
+ 0x80fc04bd,
+ 0xfc0088fe,
+ 0xfce0fcf0,
+ 0xfcc0fcd0,
+ 0xfca0fcb0,
+ 0xfc80fc90,
+ 0x0032f400,
+/* 0x01a6: ticks_from_ns */
+ 0xc0f901f8,
+ 0xd7f1b0f9,
+ 0xd3f00144,
+ 0x7721f500,
+ 0xe8ccec03,
+ 0x00b4b003,
+ 0xec120bf4,
+ 0xf103e8ee,
+ 0xf00144d7,
+ 0x21f500d3,
+/* 0x01ce: ticks_from_ns_quit */
+ 0xceb20377,
+ 0xc0fcb0fc,
+/* 0x01d6: ticks_from_us */
+ 0xc0f900f8,
+ 0xd7f1b0f9,
+ 0xd3f00144,
+ 0x7721f500,
+ 0xb0ceb203,
+ 0x0bf400b4,
+/* 0x01ef: ticks_from_us_quit */
+ 0xfce4bd05,
+ 0xf8c0fcb0,
+/* 0x01f5: ticks_to_us */
+ 0x44d7f100,
+ 0x00d3f001,
+ 0xf8ecedff,
+/* 0x0201: timer */
+ 0xf990f900,
+ 0x1032f480,
+ 0xb003f898,
+ 0x1cf40086,
+ 0x0084bd4a,
+ 0x0008f638,
+ 0x340804bd,
+ 0x980088cf,
+ 0x98bb9a09,
+ 0x00e9bb02,
+ 0x0803feb5,
+ 0x0088cf08,
+ 0xf40284f0,
+ 0x34081c1b,
+ 0xa60088cf,
+ 0x080bf4e0,
+ 0x1cf4e8a6,
+/* 0x0245: timer_reset */
+ 0xf634000d,
+ 0x04bd000e,
+/* 0x024f: timer_enable */
+ 0x089a0eb5,
+ 0xf6380001,
+ 0x04bd0008,
+/* 0x0258: timer_done */
+ 0xfc1031f4,
+ 0xf890fc80,
+/* 0x0261: send_proc */
+ 0xf980f900,
+ 0x05e89890,
+ 0xf004e998,
+ 0x89a60486,
+ 0xc42a0bf4,
+ 0x88940398,
+ 0x1880b604,
+ 0x98008ebb,
+ 0x8ab500fa,
+ 0x018db500,
+ 0xb5028cb5,
+ 0x90b6038b,
+ 0x0794f001,
+ 0xf404e9b5,
+/* 0x029a: send_done */
+ 0x90fc0231,
+ 0x00f880fc,
+/* 0x02a0: find */
+ 0x580880f9,
+/* 0x02a7: find_loop */
+ 0x980131f4,
+ 0xaea6008a,
+ 0xb6100bf4,
+ 0x86b15880,
+ 0x1bf40268,
+ 0x0132f4f1,
+/* 0x02bc: find_done */
+ 0x80fc8eb2,
+/* 0x02c2: send */
+ 0xa07e00f8,
+ 0x01f40002,
+/* 0x02cb: recv */
+ 0xf900f89b,
+ 0x9880f990,
+ 0xe99805e8,
+ 0x0132f404,
+ 0x0bf489a6,
+ 0x0389c43c,
+ 0xf00180b6,
+ 0xe8b50784,
+ 0x02ea9805,
+ 0x8ffef0f9,
+ 0xb2f0f901,
+ 0x049994ef,
+ 0xb600e9bb,
+ 0xeb9818e0,
+ 0x02ec9803,
+ 0x9801ed98,
+ 0xa5f900ee,
+ 0xf8fef0fc,
+ 0x0131f400,
+/* 0x0316: recv_done */
+ 0x80fcf0fc,
+ 0x00f890fc,
+/* 0x031c: init */
+ 0xcf010841,
+ 0x11e70011,
+ 0x14b60109,
+ 0x0014fe08,
+ 0xf000e041,
+ 0x1c000013,
+ 0xbd0001f6,
+ 0x00ff0104,
+ 0x0001f614,
+ 0x020104bd,
+ 0x080015f1,
+ 0x01f61000,
+ 0x4104bd00,
+ 0x13f000e2,
+ 0x0010fe00,
+ 0x011031f4,
+ 0xf6380001,
+ 0x04bd0001,
+/* 0x0366: init_proc */
+ 0xf198580f,
+ 0x0016b001,
+ 0xf9fa0bf4,
+ 0x58f0b615,
+/* 0x0377: mulu32_32_64 */
+ 0xf9f20ef4,
+ 0xf920f910,
+ 0x9540f930,
+ 0xd29510e1,
+ 0xbdc4bd10,
+ 0xc0edffb4,
+ 0xb2301dff,
+ 0xff34f134,
+ 0x1034b6ff,
+ 0xbb1045b6,
+ 0xb4bb00c3,
+ 0x30e2ff01,
+ 0x34f134b2,
+ 0x34b6ffff,
+ 0x1045b610,
+ 0xbb00c3bb,
+ 0x12ff01b4,
+ 0x00b3bb30,
+ 0x30fc40fc,
+ 0x10fc20fc,
+/* 0x03c6: host_send */
+ 0xb04100f8,
+ 0x0011cf04,
+ 0xcf04a042,
+ 0x12a60022,
+ 0xc42e0bf4,
+ 0xee94071e,
+ 0x70e0b704,
+ 0x03eb9802,
+ 0x9802ec98,
+ 0xee9801ed,
+ 0x02c27e00,
+ 0x0110b600,
+ 0x400f1ec4,
+ 0x0ef604b0,
+ 0xf404bd00,
+/* 0x0402: host_send_done */
+ 0x00f8c70e,
+/* 0x0404: host_recv */
+ 0xf14e4941,
+ 0xa6525413,
+ 0xb90bf4e1,
+/* 0x0410: host_recv_wait */
+ 0xcf04cc41,
+ 0xc8420011,
+ 0x0022cf04,
+ 0xa60816f0,
+ 0xef0bf412,
+ 0xb60723c4,
+ 0x30b70434,
+ 0x3bb502f0,
+ 0x023cb503,
+ 0xb5013db5,
+ 0x20b6003e,
+ 0x0f24f001,
+ 0xf604c840,
+ 0x04bd0002,
+ 0x00004002,
+ 0xbd0002f6,
+/* 0x0453: host_init */
+ 0x4100f804,
+ 0x14b60080,
+ 0x7015f110,
+ 0x04d04002,
+ 0xbd0001f6,
+ 0x00804104,
+ 0xf11014b6,
+ 0x4002f015,
+ 0x01f604dc,
+ 0x0104bd00,
+ 0x04c44001,
+ 0xbd0001f6,
+/* 0x0483: memx_func_enter */
+ 0xf100f804,
+ 0xf1162067,
+ 0xf1f55d77,
+ 0xb2ffff73,
+ 0x00047e6e,
+ 0xfdd8b200,
+ 0x60f90487,
+ 0xd0fc80f9,
+ 0x2e7ee0fc,
+ 0x77f10000,
+ 0x73f1fffe,
+ 0x6eb2ffff,
+ 0x0000047e,
+ 0x87fdd8b2,
+ 0xf960f904,
+ 0xfcd0fc80,
+ 0x002e7ee0,
+ 0xf067f100,
+ 0x7e6eb226,
+ 0xb2000004,
+ 0x0487fdd8,
+ 0x80f960f9,
+ 0xe0fcd0fc,
+ 0x00002e7e,
+ 0xe0400406,
+ 0x0006f607,
+/* 0x04ea: memx_func_enter_wait */
+ 0xc04604bd,
+ 0x0066cf07,
+ 0xf40464f0,
+ 0x2c06f70b,
+ 0xb50066cf,
+ 0x00f8f106,
+/* 0x0500: memx_func_leave */
+ 0x66cf2c06,
+ 0xf206b500,
+ 0xe4400406,
+ 0x0006f607,
+/* 0x0512: memx_func_leave_wait */
+ 0xc04604bd,
+ 0x0066cf07,
+ 0xf40464f0,
+ 0x67f1f71b,
+ 0x77f126f0,
+ 0x73f00001,
+ 0x7e6eb200,
+ 0xb2000004,
+ 0x0587fdd8,
+ 0x80f960f9,
+ 0xe0fcd0fc,
+ 0x00002e7e,
+ 0x162067f1,
+ 0x047e6eb2,
+ 0xd8b20000,
+ 0xf90587fd,
+ 0xfc80f960,
+ 0x7ee0fcd0,
+ 0xf100002e,
+ 0xf00aa277,
+ 0x6eb20073,
+ 0x0000047e,
+ 0x87fdd8b2,
+ 0xf960f905,
+ 0xfcd0fc80,
+ 0x002e7ee0,
+/* 0x057b: memx_func_wait_vblank */
+ 0xb600f800,
+ 0x00f80410,
+/* 0x0580: memx_func_wr32 */
+ 0x98001698,
+ 0x10b60115,
+ 0xf960f908,
+ 0xfcd0fc50,
+ 0x002e7ee0,
+ 0x0242b600,
+ 0xf8e81bf4,
+/* 0x059d: memx_func_wait */
+ 0xcf2c0800,
+ 0x1e980088,
+ 0x011d9800,
+ 0x98021c98,
+ 0x10b6031b,
+ 0x00797e10,
+/* 0x05b7: memx_func_delay */
+ 0x9800f800,
+ 0x10b6001e,
+ 0x005d7e04,
+/* 0x05c3: memx_func_train */
+ 0xf800f800,
+/* 0x05c5: memx_exec */
+ 0xf9e0f900,
+ 0xb2c1b2d0,
+/* 0x05cd: memx_exec_next */
+ 0x001398b2,
+ 0xe70410b6,
+ 0xe701f034,
+ 0xb601e033,
+ 0x30f00132,
+ 0xde35980c,
+ 0x12a655f9,
+ 0x98e51ef4,
+ 0x0c98f10b,
+ 0x02cbbbf2,
+ 0xcf07c44b,
+ 0xd0fc00bb,
+ 0xc27ee0fc,
+ 0x00f80002,
+/* 0x0604: memx_info */
+ 0xf401c670,
+/* 0x060a: memx_info_data */
+ 0xcc4c0c0b,
+ 0x08004b03,
+/* 0x0613: memx_info_train */
+ 0x4c090ef4,
+ 0x004b0bcc,
+/* 0x0619: memx_info_send */
+ 0x02c27e01,
+/* 0x061f: memx_recv */
+ 0xb000f800,
+ 0x0bf401d6,
+ 0x00d6b0a3,
+ 0xf8dc0bf4,
+/* 0x062d: memx_init */
+/* 0x062f: perf_recv */
+ 0xf800f800,
+/* 0x0631: perf_init */
+/* 0x0633: i2c_drive_scl */
+ 0xb000f800,
+ 0x0bf40036,
+ 0x07e0400d,
+ 0xbd0001f6,
+/* 0x0643: i2c_drive_scl_lo */
+ 0x4000f804,
+ 0x01f607e4,
+ 0xf804bd00,
+/* 0x064d: i2c_drive_sda */
+ 0x0036b000,
+ 0x400d0bf4,
+ 0x02f607e0,
+ 0xf804bd00,
+/* 0x065d: i2c_drive_sda_lo */
+ 0x07e44000,
+ 0xbd0002f6,
+/* 0x0667: i2c_sense_scl */
+ 0xf400f804,
+ 0xc4430132,
+ 0x0033cf07,
+ 0xf40431fd,
+ 0x31f4060b,
+/* 0x0679: i2c_sense_scl_done */
+/* 0x067b: i2c_sense_sda */
+ 0xf400f801,
+ 0xc4430132,
+ 0x0033cf07,
+ 0xf40432fd,
+ 0x31f4060b,
+/* 0x068d: i2c_sense_sda_done */
+/* 0x068f: i2c_raise_scl */
+ 0xf900f801,
+ 0x08984440,
+ 0x337e0103,
+/* 0x069a: i2c_raise_scl_wait */
+ 0xe84e0006,
+ 0x005d7e03,
+ 0x06677e00,
+ 0x0901f400,
+ 0xf40142b6,
+/* 0x06ae: i2c_raise_scl_done */
+ 0x40fcef1b,
+/* 0x06b2: i2c_start */
+ 0x677e00f8,
+ 0x11f40006,
+ 0x067b7e0d,
+ 0x0611f400,
+/* 0x06c3: i2c_start_rep */
+ 0x032e0ef4,
+ 0x06337e00,
+ 0x7e010300,
+ 0xbb00064d,
+ 0x65b60076,
+ 0x9450f904,
+ 0x56bb0465,
+ 0xfd50bd02,
+ 0x50fc0475,
+ 0x00068f7e,
+ 0xf40464b6,
+/* 0x06ee: i2c_start_send */
+ 0x00031d11,
+ 0x00064d7e,
+ 0x7e13884e,
+ 0x0300005d,
+ 0x06337e00,
+ 0x13884e00,
+ 0x00005d7e,
+/* 0x0708: i2c_start_out */
+/* 0x070a: i2c_stop */
+ 0x000300f8,
+ 0x0006337e,
+ 0x4d7e0003,
+ 0xe84e0006,
+ 0x005d7e03,
+ 0x7e010300,
+ 0x4e000633,
+ 0x5d7e1388,
+ 0x01030000,
+ 0x00064d7e,
+ 0x7e13884e,
+ 0xf800005d,
+/* 0x0739: i2c_bitw */
+ 0x064d7e00,
+ 0x03e84e00,
+ 0x00005d7e,
+ 0xb60076bb,
+ 0x50f90465,
+ 0xbb046594,
+ 0x50bd0256,
+ 0xfc0475fd,
+ 0x068f7e50,
+ 0x0464b600,
+ 0x4e1711f4,
+ 0x5d7e1388,
+ 0x00030000,
+ 0x0006337e,
+ 0x7e13884e,
+/* 0x0777: i2c_bitw_out */
+ 0xf800005d,
+/* 0x0779: i2c_bitr */
+ 0x7e010300,
+ 0x4e00064d,
+ 0x5d7e03e8,
+ 0x76bb0000,
+ 0x0465b600,
+ 0x659450f9,
+ 0x0256bb04,
+ 0x75fd50bd,
+ 0x7e50fc04,
+ 0xb600068f,
+ 0x11f40464,
+ 0x067b7e1a,
+ 0x7e000300,
+ 0x4e000633,
+ 0x5d7e1388,
+ 0x3cf00000,
+ 0x0131f401,
+/* 0x07bc: i2c_bitr_done */
+/* 0x07be: i2c_get_byte */
+ 0x000500f8,
+/* 0x07c2: i2c_get_byte_next */
+ 0x54b60804,
+ 0x0076bb01,
+ 0xf90465b6,
+ 0x04659450,
+ 0xbd0256bb,
+ 0x0475fd50,
+ 0x797e50fc,
+ 0x64b60007,
+ 0x2a11f404,
+ 0xb60553fd,
+ 0x1bf40142,
+ 0xbb0103d8,
+ 0x65b60076,
+ 0x9450f904,
+ 0x56bb0465,
+ 0xfd50bd02,
+ 0x50fc0475,
+ 0x0007397e,
+/* 0x080b: i2c_get_byte_done */
+ 0xf80464b6,
+/* 0x080d: i2c_put_byte */
+/* 0x080f: i2c_put_byte_next */
+ 0xb6080400,
+ 0x54ff0142,
+ 0x0076bb38,
+ 0xf90465b6,
+ 0x04659450,
+ 0xbd0256bb,
+ 0x0475fd50,
+ 0x397e50fc,
+ 0x64b60007,
+ 0x3411f404,
+ 0xf40046b0,
+ 0x76bbd81b,
+ 0x0465b600,
+ 0x659450f9,
+ 0x0256bb04,
+ 0x75fd50bd,
+ 0x7e50fc04,
+ 0xb6000779,
+ 0x11f40464,
+ 0x0076bb0f,
+ 0xf40136b0,
+ 0x32f4061b,
+/* 0x0865: i2c_put_byte_done */
+/* 0x0867: i2c_addr */
+ 0xbb00f801,
+ 0x65b60076,
+ 0x9450f904,
+ 0x56bb0465,
+ 0xfd50bd02,
+ 0x50fc0475,
+ 0x0006b27e,
+ 0xf40464b6,
+ 0xc3e72911,
+ 0x34b6012e,
+ 0x0553fd01,
+ 0xb60076bb,
+ 0x50f90465,
+ 0xbb046594,
+ 0x50bd0256,
+ 0xfc0475fd,
+ 0x080d7e50,
+ 0x0464b600,
+/* 0x08ac: i2c_addr_done */
+/* 0x08ae: i2c_acquire_addr */
+ 0xcec700f8,
+ 0x05e4b6f8,
+ 0xd014e0b7,
+/* 0x08ba: i2c_acquire */
+ 0xae7e00f8,
+ 0x047e0008,
+ 0xd9f00000,
+ 0x002e7e03,
+/* 0x08cb: i2c_release */
+ 0x7e00f800,
+ 0x7e0008ae,
+ 0xf0000004,
+ 0x2e7e03da,
+ 0x00f80000,
+/* 0x08dc: i2c_recv */
+ 0xc70132f4,
+ 0x14b6f8c1,
+ 0x2816b002,
+ 0x01371ff5,
+ 0x0cf413b8,
+ 0x00329800,
+ 0x0ccc13b8,
+ 0x00319800,
+ 0xf90231f4,
+ 0xf9e0f9d0,
+ 0x0067f1d0,
+ 0x0063f100,
+ 0x01679210,
+ 0xb60076bb,
+ 0x50f90465,
+ 0xbb046594,
+ 0x50bd0256,
+ 0xfc0475fd,
+ 0x08ba7e50,
+ 0x0464b600,
+ 0xd6b0d0fc,
+ 0xb01bf500,
+ 0xbb000500,
+ 0x65b60076,
+ 0x9450f904,
+ 0x56bb0465,
+ 0xfd50bd02,
+ 0x50fc0475,
+ 0x0008677e,
+ 0xf50464b6,
+ 0xc700cc11,
+ 0x76bbe0c5,
+ 0x0465b600,
+ 0x659450f9,
+ 0x0256bb04,
+ 0x75fd50bd,
+ 0x7e50fc04,
+ 0xb600080d,
+ 0x11f50464,
+ 0x010500a9,
+ 0xb60076bb,
+ 0x50f90465,
+ 0xbb046594,
+ 0x50bd0256,
+ 0xfc0475fd,
+ 0x08677e50,
+ 0x0464b600,
+ 0x008711f5,
+ 0xb60076bb,
+ 0x50f90465,
+ 0xbb046594,
+ 0x50bd0256,
+ 0xfc0475fd,
+ 0x07be7e50,
+ 0x0464b600,
+ 0xcb6711f4,
+ 0x76bbe05b,
+ 0x0465b600,
+ 0x659450f9,
+ 0x0256bb04,
+ 0x75fd50bd,
+ 0x7e50fc04,
+ 0xb600070a,
+ 0x5bb20464,
+ 0x0ef474bd,
+/* 0x09e1: i2c_recv_not_rd08 */
+ 0x01d6b041,
+ 0x053b1bf4,
+ 0x08677e00,
+ 0x3211f400,
+ 0x7ee0c5c7,
+ 0xf400080d,
+ 0x00052811,
+ 0x0008677e,
+ 0xc71f11f4,
+ 0x0d7ee0b5,
+ 0x11f40008,
+ 0x070a7e15,
+ 0xc774bd00,
+ 0x1bf408c5,
+ 0x0232f409,
+/* 0x0a1f: i2c_recv_not_wr08 */
+/* 0x0a1f: i2c_recv_done */
+ 0xc7030ef4,
+ 0xcb7ef8ce,
+ 0xe0fc0008,
+ 0x12f4d0fc,
+ 0x7e7cb209,
+/* 0x0a33: i2c_recv_exit */
+ 0xf80002c2,
+/* 0x0a35: i2c_init */
+/* 0x0a37: test_recv */
+ 0x4100f800,
+ 0x11cf0458,
+ 0x0110b600,
+ 0xf6045840,
+ 0x04bd0001,
+ 0xd900e7f1,
+ 0x134fe3f1,
+ 0x0002017e,
+/* 0x0a56: test_init */
+ 0x004e00f8,
+ 0x02017e08,
+/* 0x0a5f: idle_recv */
+ 0xf800f800,
+/* 0x0a61: idle */
+ 0x0031f400,
+ 0xcf045441,
+ 0x10b60011,
+ 0x04544001,
+ 0xbd0001f6,
+/* 0x0a75: idle_loop */
+ 0xf4580104,
+/* 0x0a7a: idle_proc */
+/* 0x0a7a: idle_proc_exec */
+ 0x10f90232,
+ 0xcb7e1eb2,
+ 0x10fc0002,
+ 0xf40911f4,
+ 0x0ef40231,
+/* 0x0a8d: idle_proc_next */
+ 0x5810b6f0,
+ 0x1bf41fa6,
+ 0xe002f4e8,
+ 0xf40028f4,
+ 0x0000c60e,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+};
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gt215.fuc3 b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gt215.fuc3
new file mode 100644
index 000000000000..393049fc8b2d
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gt215.fuc3
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#define NVKM_PPWR_CHIPSET GT215
+#define HW_TICKS_PER_US 203 // should be 202.5
+
+//#define NVKM_FALCON_PC24
+//#define NVKM_FALCON_UNSHIFTED_IO
+//#define NVKM_FALCON_MMIO_UAS
+//#define NVKM_FALCON_MMIO_TRAP
+
+#include "macros.fuc"
+
+.section #gt215_pmu_data
+#define INCLUDE_PROC
+#include "kernel.fuc"
+#include "arith.fuc"
+#include "host.fuc"
+#include "memx.fuc"
+#include "perf.fuc"
+#include "i2c_.fuc"
+#include "test.fuc"
+#include "idle.fuc"
+#undef INCLUDE_PROC
+
+#define INCLUDE_DATA
+#include "kernel.fuc"
+#include "arith.fuc"
+#include "host.fuc"
+#include "memx.fuc"
+#include "perf.fuc"
+#include "i2c_.fuc"
+#include "test.fuc"
+#include "idle.fuc"
+#undef INCLUDE_DATA
+.align 256
+
+.section #gt215_pmu_code
+#define INCLUDE_CODE
+#include "kernel.fuc"
+#include "arith.fuc"
+#include "host.fuc"
+#include "memx.fuc"
+#include "perf.fuc"
+#include "i2c_.fuc"
+#include "test.fuc"
+#include "idle.fuc"
+#undef INCLUDE_CODE
+.align 256
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gt215.fuc3.h b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gt215.fuc3.h
new file mode 100644
index 000000000000..2686f8fad0f5
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gt215.fuc3.h
@@ -0,0 +1,1868 @@
+uint32_t gt215_pmu_data[] = {
+/* 0x0000: proc_kern */
+ 0x52544e49,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+/* 0x0058: proc_list_head */
+ 0x54534f48,
+ 0x00000512,
+ 0x000004af,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x584d454d,
+ 0x00000842,
+ 0x00000834,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x46524550,
+ 0x00000846,
+ 0x00000844,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x5f433249,
+ 0x00000c76,
+ 0x00000b19,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x54534554,
+ 0x00000c9f,
+ 0x00000c78,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x454c4449,
+ 0x00000cab,
+ 0x00000ca9,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+/* 0x0268: proc_list_tail */
+/* 0x0268: time_prev */
+ 0x00000000,
+/* 0x026c: time_next */
+ 0x00000000,
+/* 0x0270: fifo_queue */
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+/* 0x02f0: rfifo_queue */
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+/* 0x0370: memx_func_head */
+ 0x00000001,
+ 0x00000000,
+ 0x00000551,
+/* 0x037c: memx_func_next */
+ 0x00000002,
+ 0x00000000,
+ 0x000005a8,
+ 0x00000003,
+ 0x00000002,
+ 0x0000063a,
+ 0x00040004,
+ 0x00000000,
+ 0x00000656,
+ 0x00010005,
+ 0x00000000,
+ 0x00000673,
+ 0x00010006,
+ 0x00000000,
+ 0x000005f8,
+ 0x00000007,
+ 0x00000000,
+ 0x0000067e,
+/* 0x03c4: memx_func_tail */
+/* 0x03c4: memx_ts_start */
+ 0x00000000,
+/* 0x03c8: memx_ts_end */
+ 0x00000000,
+/* 0x03cc: memx_data_head */
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+/* 0x0bcc: memx_data_tail */
+/* 0x0bcc: memx_train_head */
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+/* 0x0ccc: memx_train_tail */
+/* 0x0ccc: i2c_scl_map */
+ 0x00001000,
+ 0x00004000,
+ 0x00010000,
+ 0x00000100,
+ 0x00040000,
+ 0x00100000,
+ 0x00400000,
+ 0x01000000,
+ 0x04000000,
+ 0x10000000,
+/* 0x0cf4: i2c_sda_map */
+ 0x00002000,
+ 0x00008000,
+ 0x00020000,
+ 0x00000200,
+ 0x00080000,
+ 0x00200000,
+ 0x00800000,
+ 0x02000000,
+ 0x08000000,
+ 0x20000000,
+/* 0x0d1c: i2c_ctrl */
+ 0x0000e138,
+ 0x0000e150,
+ 0x0000e168,
+ 0x0000e180,
+ 0x0000e254,
+ 0x0000e274,
+ 0x0000e764,
+ 0x0000e780,
+ 0x0000e79c,
+ 0x0000e7b8,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+};
+
+uint32_t gt215_pmu_code[] = {
+ 0x039e0ef5,
+/* 0x0004: rd32 */
+ 0x07a007f1,
+ 0xd00604b6,
+ 0x04bd000e,
+ 0xf001d7f0,
+ 0x07f101d3,
+ 0x04b607ac,
+ 0x000dd006,
+/* 0x0022: rd32_wait */
+ 0xd7f104bd,
+ 0xd4b607ac,
+ 0x00ddcf06,
+ 0x7000d4f1,
+ 0xf1f21bf4,
+ 0xb607a4d7,
+ 0xddcf06d4,
+/* 0x003f: wr32 */
+ 0xf100f800,
+ 0xb607a007,
+ 0x0ed00604,
+ 0xf104bd00,
+ 0xb607a407,
+ 0x0dd00604,
+ 0xf004bd00,
+ 0xd5f002d7,
+ 0x01d3f0f0,
+ 0x07ac07f1,
+ 0xd00604b6,
+ 0x04bd000d,
+/* 0x006c: wr32_wait */
+ 0x07acd7f1,
+ 0xcf06d4b6,
+ 0xd4f100dd,
+ 0x1bf47000,
+/* 0x007f: nsec */
+ 0xf900f8f2,
+ 0xf080f990,
+ 0x84b62c87,
+ 0x0088cf06,
+/* 0x008c: nsec_loop */
+ 0xb62c97f0,
+ 0x99cf0694,
+ 0x0298bb00,
+ 0xf4069eb8,
+ 0x80fcf11e,
+ 0x00f890fc,
+/* 0x00a4: wait */
+ 0x80f990f9,
+ 0xb62c87f0,
+ 0x88cf0684,
+/* 0x00b1: wait_loop */
+ 0x02eeb900,
+ 0xb90421f4,
+ 0xadfd02da,
+ 0x06acb804,
+ 0xf0150bf4,
+ 0x94b62c97,
+ 0x0099cf06,
+ 0xb80298bb,
+ 0x1ef4069b,
+/* 0x00d5: wait_done */
+ 0xfc80fcdf,
+/* 0x00db: intr_watchdog */
+ 0x9800f890,
+ 0x96b003e9,
+ 0x2a0bf400,
+ 0xbb9a0a98,
+ 0x1cf4029a,
+ 0x01d7f00f,
+ 0x02dd21f5,
+ 0x0ef494bd,
+/* 0x00f9: intr_watchdog_next_time */
+ 0x9b0a9815,
+ 0xf400a6b0,
+ 0x9ab8090b,
+ 0x061cf406,
+/* 0x0108: intr_watchdog_next_time_set */
+/* 0x010b: intr_watchdog_next_proc */
+ 0x809b0980,
+ 0xe0b603e9,
+ 0x68e6b158,
+ 0xc61bf402,
+/* 0x011a: intr */
+ 0x00f900f8,
+ 0x80f904bd,
+ 0xa0f990f9,
+ 0xc0f9b0f9,
+ 0xe0f9d0f9,
+ 0xf7f0f0f9,
+ 0x0188fe00,
+ 0x87f180f9,
+ 0x84b605d0,
+ 0x0088cf06,
+ 0xf10180b6,
+ 0xb605d007,
+ 0x08d00604,
+ 0xf004bd00,
+ 0x84b60887,
+ 0x0088cf06,
+ 0xf40289c4,
+ 0x0080230b,
+ 0x58e7f09b,
+ 0x98db21f4,
+ 0x96b09b09,
+ 0x110bf400,
+ 0xb63407f0,
+ 0x09d00604,
+ 0x8004bd00,
+/* 0x017e: intr_skip_watchdog */
+ 0x89e49a09,
+ 0x0bf40800,
+ 0x8897f148,
+ 0x0694b606,
+ 0xc40099cf,
+ 0x0bf4029a,
+ 0xc0c7f12c,
+ 0x06c4b604,
+ 0xf900cccf,
+ 0x48e7f1c0,
+ 0x53e3f14f,
+ 0x00d7f054,
+ 0x034221f5,
+ 0x07f1c0fc,
+ 0x04b604c0,
+ 0x000cd006,
+/* 0x01be: intr_subintr_skip_fifo */
+ 0x07f104bd,
+ 0x04b60688,
+ 0x0009d006,
+/* 0x01ca: intr_skip_subintr */
+ 0x89c404bd,
+ 0x070bf420,
+ 0xffbfa4f1,
+/* 0x01d4: intr_skip_pause */
+ 0xf44089c4,
+ 0xa4f1070b,
+/* 0x01de: intr_skip_user0 */
+ 0x07f0ffbf,
+ 0x0604b604,
+ 0xbd0008d0,
+ 0xfe80fc04,
+ 0xf0fc0088,
+ 0xd0fce0fc,
+ 0xb0fcc0fc,
+ 0x90fca0fc,
+ 0x00fc80fc,
+ 0xf80032f4,
+/* 0x0205: ticks_from_ns */
+ 0xf9c0f901,
+ 0xcbd7f1b0,
+ 0x00d3f000,
+ 0x041321f5,
+ 0x03e8ccec,
+ 0xf400b4b0,
+ 0xeeec120b,
+ 0xd7f103e8,
+ 0xd3f000cb,
+ 0x1321f500,
+/* 0x022d: ticks_from_ns_quit */
+ 0x02ceb904,
+ 0xc0fcb0fc,
+/* 0x0236: ticks_from_us */
+ 0xc0f900f8,
+ 0xd7f1b0f9,
+ 0xd3f000cb,
+ 0x1321f500,
+ 0x02ceb904,
+ 0xf400b4b0,
+ 0xe4bd050b,
+/* 0x0250: ticks_from_us_quit */
+ 0xc0fcb0fc,
+/* 0x0256: ticks_to_us */
+ 0xd7f100f8,
+ 0xd3f000cb,
+ 0xecedff00,
+/* 0x0262: timer */
+ 0x90f900f8,
+ 0x32f480f9,
+ 0x03f89810,
+ 0xf40086b0,
+ 0x84bd651c,
+ 0xb63807f0,
+ 0x08d00604,
+ 0xf004bd00,
+ 0x84b63487,
+ 0x0088cf06,
+ 0xbb9a0998,
+ 0xe9bb0298,
+ 0x03fe8000,
+ 0xb60887f0,
+ 0x88cf0684,
+ 0x0284f000,
+ 0xf0261bf4,
+ 0x84b63487,
+ 0x0088cf06,
+ 0xf406e0b8,
+ 0xe8b8090b,
+ 0x111cf406,
+/* 0x02b8: timer_reset */
+ 0xb63407f0,
+ 0x0ed00604,
+ 0x8004bd00,
+/* 0x02c6: timer_enable */
+ 0x87f09a0e,
+ 0x3807f001,
+ 0xd00604b6,
+ 0x04bd0008,
+/* 0x02d4: timer_done */
+ 0xfc1031f4,
+ 0xf890fc80,
+/* 0x02dd: send_proc */
+ 0xf980f900,
+ 0x05e89890,
+ 0xf004e998,
+ 0x89b80486,
+ 0x2a0bf406,
+ 0x940398c4,
+ 0x80b60488,
+ 0x008ebb18,
+ 0x8000fa98,
+ 0x8d80008a,
+ 0x028c8001,
+ 0xb6038b80,
+ 0x94f00190,
+ 0x04e98007,
+/* 0x0317: send_done */
+ 0xfc0231f4,
+ 0xf880fc90,
+/* 0x031d: find */
+ 0xf080f900,
+ 0x31f45887,
+/* 0x0325: find_loop */
+ 0x008a9801,
+ 0xf406aeb8,
+ 0x80b6100b,
+ 0x6886b158,
+ 0xf01bf402,
+/* 0x033b: find_done */
+ 0xb90132f4,
+ 0x80fc028e,
+/* 0x0342: send */
+ 0x21f500f8,
+ 0x01f4031d,
+/* 0x034b: recv */
+ 0xf900f897,
+ 0x9880f990,
+ 0xe99805e8,
+ 0x0132f404,
+ 0xf40689b8,
+ 0x89c43d0b,
+ 0x0180b603,
+ 0x800784f0,
+ 0xea9805e8,
+ 0xfef0f902,
+ 0xf0f9018f,
+ 0x9402efb9,
+ 0xe9bb0499,
+ 0x18e0b600,
+ 0x9803eb98,
+ 0xed9802ec,
+ 0x00ee9801,
+ 0xf0fca5f9,
+ 0xf400f8fe,
+ 0xf0fc0131,
+/* 0x0398: recv_done */
+ 0x90fc80fc,
+/* 0x039e: init */
+ 0x17f100f8,
+ 0x14b60108,
+ 0x0011cf06,
+ 0x010911e7,
+ 0xfe0814b6,
+ 0x17f10014,
+ 0x13f000e0,
+ 0x1c07f000,
+ 0xd00604b6,
+ 0x04bd0001,
+ 0xf0ff17f0,
+ 0x04b61407,
+ 0x0001d006,
+ 0x17f004bd,
+ 0x0015f102,
+ 0x1007f008,
+ 0xd00604b6,
+ 0x04bd0001,
+ 0x011a17f1,
+ 0xfe0013f0,
+ 0x31f40010,
+ 0x0117f010,
+ 0xb63807f0,
+ 0x01d00604,
+ 0xf004bd00,
+/* 0x0402: init_proc */
+ 0xf19858f7,
+ 0x0016b001,
+ 0xf9fa0bf4,
+ 0x58f0b615,
+/* 0x0413: mulu32_32_64 */
+ 0xf9f20ef4,
+ 0xf920f910,
+ 0x9540f930,
+ 0xd29510e1,
+ 0xbdc4bd10,
+ 0xc0edffb4,
+ 0xb9301dff,
+ 0x34f10234,
+ 0x34b6ffff,
+ 0x1045b610,
+ 0xbb00c3bb,
+ 0xe2ff01b4,
+ 0x0234b930,
+ 0xffff34f1,
+ 0xb61034b6,
+ 0xc3bb1045,
+ 0x01b4bb00,
+ 0xbb3012ff,
+ 0x40fc00b3,
+ 0x20fc30fc,
+ 0x00f810fc,
+/* 0x0464: host_send */
+ 0x04b017f1,
+ 0xcf0614b6,
+ 0x27f10011,
+ 0x24b604a0,
+ 0x0022cf06,
+ 0xf40612b8,
+ 0x1ec4320b,
+ 0x04ee9407,
+ 0x0270e0b7,
+ 0x9803eb98,
+ 0xed9802ec,
+ 0x00ee9801,
+ 0x034221f5,
+ 0xc40110b6,
+ 0x07f10f1e,
+ 0x04b604b0,
+ 0x000ed006,
+ 0x0ef404bd,
+/* 0x04ad: host_send_done */
+/* 0x04af: host_recv */
+ 0xf100f8ba,
+ 0xf14e4917,
+ 0xb8525413,
+ 0x0bf406e1,
+/* 0x04bd: host_recv_wait */
+ 0xcc17f1aa,
+ 0x0614b604,
+ 0xf10011cf,
+ 0xb604c827,
+ 0x22cf0624,
+ 0x0816f000,
+ 0xf40612b8,
+ 0x23c4e60b,
+ 0x0434b607,
+ 0x02f030b7,
+ 0x80033b80,
+ 0x3d80023c,
+ 0x003e8001,
+ 0xf00120b6,
+ 0x07f10f24,
+ 0x04b604c8,
+ 0x0002d006,
+ 0x27f004bd,
+ 0x0007f040,
+ 0xd00604b6,
+ 0x04bd0002,
+/* 0x0512: host_init */
+ 0x17f100f8,
+ 0x14b60080,
+ 0x7015f110,
+ 0xd007f102,
+ 0x0604b604,
+ 0xbd0001d0,
+ 0x8017f104,
+ 0x1014b600,
+ 0x02f015f1,
+ 0x04dc07f1,
+ 0xd00604b6,
+ 0x04bd0001,
+ 0xf10117f0,
+ 0xb604c407,
+ 0x01d00604,
+ 0xf804bd00,
+/* 0x0551: memx_func_enter */
+ 0x1087f100,
+ 0x028eb916,
+ 0xb90421f4,
+ 0x67f102d7,
+ 0x63f1fffc,
+ 0x76fdffff,
+ 0x0267f104,
+ 0x0576fd00,
+ 0x70f980f9,
+ 0xe0fcd0fc,
+ 0xf03f21f4,
+ 0x07f10467,
+ 0x04b607e0,
+ 0x0006d006,
+/* 0x058a: memx_func_enter_wait */
+ 0x67f104bd,
+ 0x64b607c0,
+ 0x0066cf06,
+ 0xf40464f0,
+ 0x67f0f30b,
+ 0x0664b62c,
+ 0x800066cf,
+ 0x00f8f106,
+/* 0x05a8: memx_func_leave */
+ 0xb62c67f0,
+ 0x66cf0664,
+ 0xf2068000,
+ 0xf10467f0,
+ 0xb607e407,
+ 0x06d00604,
+/* 0x05c3: memx_func_leave_wait */
+ 0xf104bd00,
+ 0xb607c067,
+ 0x66cf0664,
+ 0x0464f000,
+ 0xf1f31bf4,
+ 0xb9161087,
+ 0x21f4028e,
+ 0x02d7b904,
+ 0xffcc67f1,
+ 0xffff63f1,
+ 0xf90476fd,
+ 0xfc70f980,
+ 0xf4e0fcd0,
+ 0x00f83f21,
+/* 0x05f8: memx_func_wait_vblank */
+ 0xb0001698,
+ 0x0bf40066,
+ 0x0166b013,
+ 0xf4060bf4,
+/* 0x060a: memx_func_wait_vblank_head1 */
+ 0x77f12e0e,
+ 0x0ef40020,
+/* 0x0611: memx_func_wait_vblank_head0 */
+ 0x0877f107,
+/* 0x0615: memx_func_wait_vblank_0 */
+ 0xc467f100,
+ 0x0664b607,
+ 0xfd0066cf,
+ 0x1bf40467,
+/* 0x0625: memx_func_wait_vblank_1 */
+ 0xc467f1f3,
+ 0x0664b607,
+ 0xfd0066cf,
+ 0x0bf40467,
+/* 0x0635: memx_func_wait_vblank_fini */
+ 0x0410b6f3,
+/* 0x063a: memx_func_wr32 */
+ 0x169800f8,
+ 0x01159800,
+ 0xf90810b6,
+ 0xfc50f960,
+ 0xf4e0fcd0,
+ 0x42b63f21,
+ 0xe91bf402,
+/* 0x0656: memx_func_wait */
+ 0x87f000f8,
+ 0x0684b62c,
+ 0x980088cf,
+ 0x1d98001e,
+ 0x021c9801,
+ 0xb6031b98,
+ 0x21f41010,
+/* 0x0673: memx_func_delay */
+ 0x9800f8a4,
+ 0x10b6001e,
+ 0x7f21f404,
+/* 0x067e: memx_func_train */
+ 0x57f100f8,
+ 0x77f10003,
+ 0x97f10000,
+ 0x93f00000,
+ 0x029eb970,
+ 0xb90421f4,
+ 0xe7f102d8,
+ 0x21f42710,
+/* 0x069d: memx_func_train_loop_outer */
+ 0x0158e07f,
+ 0x0083f101,
+ 0xe097f102,
+ 0x1193f011,
+ 0x80f990f9,
+ 0xe0fcd0fc,
+ 0xf93f21f4,
+ 0x0067f150,
+/* 0x06bd: memx_func_train_loop_inner */
+ 0x1187f100,
+ 0x9068ff11,
+ 0xfd109894,
+ 0x97f10589,
+ 0x93f00720,
+ 0xf990f910,
+ 0xfcd0fc80,
+ 0x3f21f4e0,
+ 0x008097f1,
+ 0xb91093f0,
+ 0x21f4029e,
+ 0x02d8b904,
+ 0xf92088c5,
+ 0xfc80f990,
+ 0xf4e0fcd0,
+ 0x97f13f21,
+ 0x93f0053c,
+ 0x0287f110,
+ 0x0083f130,
+ 0xf990f980,
+ 0xfcd0fc80,
+ 0x3f21f4e0,
+ 0x0560e7f1,
+ 0xf110e3f0,
+ 0xf10000d7,
+ 0x908000d3,
+ 0xb7f100dc,
+ 0xb3f08480,
+ 0xa421f41e,
+ 0x000057f1,
+ 0xffff97f1,
+ 0x830093f1,
+/* 0x073c: memx_func_train_loop_4x */
+ 0x0080a7f1,
+ 0xb910a3f0,
+ 0x21f402ae,
+ 0x02d8b904,
+ 0xffdfb7f1,
+ 0xffffb3f1,
+ 0xf9048bfd,
+ 0xfc80f9a0,
+ 0xf4e0fcd0,
+ 0xa7f13f21,
+ 0xa3f0053c,
+ 0x0287f110,
+ 0x0083f130,
+ 0xf9a0f980,
+ 0xfcd0fc80,
+ 0x3f21f4e0,
+ 0x0560e7f1,
+ 0xf110e3f0,
+ 0xf10000d7,
+ 0xb98000d3,
+ 0xb7f102dc,
+ 0xb3f02710,
+ 0xa421f400,
+ 0xf402eeb9,
+ 0xddb90421,
+ 0x949dff02,
+ 0x700150b6,
+ 0x1ef40456,
+ 0xcc7aa092,
+ 0x00a9800b,
+ 0xb60160b6,
+ 0x66700470,
+ 0x001ef510,
+ 0xb650fcff,
+ 0x56700150,
+ 0xd41ef507,
+/* 0x07cf: memx_exec */
+ 0xf900f8fe,
+ 0xb9d0f9e0,
+ 0xb2b902c1,
+/* 0x07d9: memx_exec_next */
+ 0x00139802,
+ 0xe70410b6,
+ 0xe701f034,
+ 0xb601e033,
+ 0x30f00132,
+ 0xde35980c,
+ 0x12b855f9,
+ 0xe41ef406,
+ 0x98f10b98,
+ 0xcbbbf20c,
+ 0xc4b7f102,
+ 0x06b4b607,
+ 0xfc00bbcf,
+ 0xf5e0fcd0,
+ 0xf8034221,
+/* 0x0815: memx_info */
+ 0x01c67000,
+/* 0x081b: memx_info_data */
+ 0xf10e0bf4,
+ 0xf103ccc7,
+ 0xf40800b7,
+/* 0x0826: memx_info_train */
+ 0xc7f10b0e,
+ 0xb7f10bcc,
+/* 0x082e: memx_info_send */
+ 0x21f50100,
+ 0x00f80342,
+/* 0x0834: memx_recv */
+ 0xf401d6b0,
+ 0xd6b0980b,
+ 0xd80bf400,
+/* 0x0842: memx_init */
+ 0x00f800f8,
+/* 0x0844: perf_recv */
+/* 0x0846: perf_init */
+ 0x00f800f8,
+/* 0x0848: i2c_drive_scl */
+ 0xf40036b0,
+ 0x07f1110b,
+ 0x04b607e0,
+ 0x0001d006,
+ 0x00f804bd,
+/* 0x085c: i2c_drive_scl_lo */
+ 0x07e407f1,
+ 0xd00604b6,
+ 0x04bd0001,
+/* 0x086a: i2c_drive_sda */
+ 0x36b000f8,
+ 0x110bf400,
+ 0x07e007f1,
+ 0xd00604b6,
+ 0x04bd0002,
+/* 0x087e: i2c_drive_sda_lo */
+ 0x07f100f8,
+ 0x04b607e4,
+ 0x0002d006,
+ 0x00f804bd,
+/* 0x088c: i2c_sense_scl */
+ 0xf10132f4,
+ 0xb607c437,
+ 0x33cf0634,
+ 0x0431fd00,
+ 0xf4060bf4,
+/* 0x08a2: i2c_sense_scl_done */
+ 0x00f80131,
+/* 0x08a4: i2c_sense_sda */
+ 0xf10132f4,
+ 0xb607c437,
+ 0x33cf0634,
+ 0x0432fd00,
+ 0xf4060bf4,
+/* 0x08ba: i2c_sense_sda_done */
+ 0x00f80131,
+/* 0x08bc: i2c_raise_scl */
+ 0x47f140f9,
+ 0x37f00898,
+ 0x4821f501,
+/* 0x08c9: i2c_raise_scl_wait */
+ 0xe8e7f108,
+ 0x7f21f403,
+ 0x088c21f5,
+ 0xb60901f4,
+ 0x1bf40142,
+/* 0x08dd: i2c_raise_scl_done */
+ 0xf840fcef,
+/* 0x08e1: i2c_start */
+ 0x8c21f500,
+ 0x0d11f408,
+ 0x08a421f5,
+ 0xf40611f4,
+/* 0x08f2: i2c_start_rep */
+ 0x37f0300e,
+ 0x4821f500,
+ 0x0137f008,
+ 0x086a21f5,
+ 0xb60076bb,
+ 0x50f90465,
+ 0xbb046594,
+ 0x50bd0256,
+ 0xfc0475fd,
+ 0xbc21f550,
+ 0x0464b608,
+/* 0x091f: i2c_start_send */
+ 0xf01f11f4,
+ 0x21f50037,
+ 0xe7f1086a,
+ 0x21f41388,
+ 0x0037f07f,
+ 0x084821f5,
+ 0x1388e7f1,
+/* 0x093b: i2c_start_out */
+ 0xf87f21f4,
+/* 0x093d: i2c_stop */
+ 0x0037f000,
+ 0x084821f5,
+ 0xf50037f0,
+ 0xf1086a21,
+ 0xf403e8e7,
+ 0x37f07f21,
+ 0x4821f501,
+ 0x88e7f108,
+ 0x7f21f413,
+ 0xf50137f0,
+ 0xf1086a21,
+ 0xf41388e7,
+ 0x00f87f21,
+/* 0x0970: i2c_bitw */
+ 0x086a21f5,
+ 0x03e8e7f1,
+ 0xbb7f21f4,
+ 0x65b60076,
+ 0x9450f904,
+ 0x56bb0465,
+ 0xfd50bd02,
+ 0x50fc0475,
+ 0x08bc21f5,
+ 0xf40464b6,
+ 0xe7f11811,
+ 0x21f41388,
+ 0x0037f07f,
+ 0x084821f5,
+ 0x1388e7f1,
+/* 0x09af: i2c_bitw_out */
+ 0xf87f21f4,
+/* 0x09b1: i2c_bitr */
+ 0x0137f000,
+ 0x086a21f5,
+ 0x03e8e7f1,
+ 0xbb7f21f4,
+ 0x65b60076,
+ 0x9450f904,
+ 0x56bb0465,
+ 0xfd50bd02,
+ 0x50fc0475,
+ 0x08bc21f5,
+ 0xf40464b6,
+ 0x21f51b11,
+ 0x37f008a4,
+ 0x4821f500,
+ 0x88e7f108,
+ 0x7f21f413,
+ 0xf4013cf0,
+/* 0x09f6: i2c_bitr_done */
+ 0x00f80131,
+/* 0x09f8: i2c_get_byte */
+ 0xf00057f0,
+/* 0x09fe: i2c_get_byte_next */
+ 0x54b60847,
+ 0x0076bb01,
+ 0xf90465b6,
+ 0x04659450,
+ 0xbd0256bb,
+ 0x0475fd50,
+ 0x21f550fc,
+ 0x64b609b1,
+ 0x2b11f404,
+ 0xb60553fd,
+ 0x1bf40142,
+ 0x0137f0d8,
+ 0xb60076bb,
+ 0x50f90465,
+ 0xbb046594,
+ 0x50bd0256,
+ 0xfc0475fd,
+ 0x7021f550,
+ 0x0464b609,
+/* 0x0a48: i2c_get_byte_done */
+/* 0x0a4a: i2c_put_byte */
+ 0x47f000f8,
+/* 0x0a4d: i2c_put_byte_next */
+ 0x0142b608,
+ 0xbb3854ff,
+ 0x65b60076,
+ 0x9450f904,
+ 0x56bb0465,
+ 0xfd50bd02,
+ 0x50fc0475,
+ 0x097021f5,
+ 0xf40464b6,
+ 0x46b03411,
+ 0xd81bf400,
+ 0xb60076bb,
+ 0x50f90465,
+ 0xbb046594,
+ 0x50bd0256,
+ 0xfc0475fd,
+ 0xb121f550,
+ 0x0464b609,
+ 0xbb0f11f4,
+ 0x36b00076,
+ 0x061bf401,
+/* 0x0aa3: i2c_put_byte_done */
+ 0xf80132f4,
+/* 0x0aa5: i2c_addr */
+ 0x0076bb00,
+ 0xf90465b6,
+ 0x04659450,
+ 0xbd0256bb,
+ 0x0475fd50,
+ 0x21f550fc,
+ 0x64b608e1,
+ 0x2911f404,
+ 0x012ec3e7,
+ 0xfd0134b6,
+ 0x76bb0553,
+ 0x0465b600,
+ 0x659450f9,
+ 0x0256bb04,
+ 0x75fd50bd,
+ 0xf550fc04,
+ 0xb60a4a21,
+/* 0x0aea: i2c_addr_done */
+ 0x00f80464,
+/* 0x0aec: i2c_acquire_addr */
+ 0xb6f8cec7,
+ 0xe0b702e4,
+ 0xee980d1c,
+/* 0x0afb: i2c_acquire */
+ 0xf500f800,
+ 0xf40aec21,
+ 0xd9f00421,
+ 0x3f21f403,
+/* 0x0b0a: i2c_release */
+ 0x21f500f8,
+ 0x21f40aec,
+ 0x03daf004,
+ 0xf83f21f4,
+/* 0x0b19: i2c_recv */
+ 0x0132f400,
+ 0xb6f8c1c7,
+ 0x16b00214,
+ 0x3a1ff528,
+ 0xf413a001,
+ 0x0032980c,
+ 0x0ccc13a0,
+ 0xf4003198,
+ 0xd0f90231,
+ 0xd0f9e0f9,
+ 0x000067f1,
+ 0x100063f1,
+ 0xbb016792,
+ 0x65b60076,
+ 0x9450f904,
+ 0x56bb0465,
+ 0xfd50bd02,
+ 0x50fc0475,
+ 0x0afb21f5,
+ 0xfc0464b6,
+ 0x00d6b0d0,
+ 0x00b31bf5,
+ 0xbb0057f0,
+ 0x65b60076,
+ 0x9450f904,
+ 0x56bb0465,
+ 0xfd50bd02,
+ 0x50fc0475,
+ 0x0aa521f5,
+ 0xf50464b6,
+ 0xc700d011,
+ 0x76bbe0c5,
+ 0x0465b600,
+ 0x659450f9,
+ 0x0256bb04,
+ 0x75fd50bd,
+ 0xf550fc04,
+ 0xb60a4a21,
+ 0x11f50464,
+ 0x57f000ad,
+ 0x0076bb01,
+ 0xf90465b6,
+ 0x04659450,
+ 0xbd0256bb,
+ 0x0475fd50,
+ 0x21f550fc,
+ 0x64b60aa5,
+ 0x8a11f504,
+ 0x0076bb00,
+ 0xf90465b6,
+ 0x04659450,
+ 0xbd0256bb,
+ 0x0475fd50,
+ 0x21f550fc,
+ 0x64b609f8,
+ 0x6a11f404,
+ 0xbbe05bcb,
+ 0x65b60076,
+ 0x9450f904,
+ 0x56bb0465,
+ 0xfd50bd02,
+ 0x50fc0475,
+ 0x093d21f5,
+ 0xb90464b6,
+ 0x74bd025b,
+/* 0x0c1f: i2c_recv_not_rd08 */
+ 0xb0430ef4,
+ 0x1bf401d6,
+ 0x0057f03d,
+ 0x0aa521f5,
+ 0xc73311f4,
+ 0x21f5e0c5,
+ 0x11f40a4a,
+ 0x0057f029,
+ 0x0aa521f5,
+ 0xc71f11f4,
+ 0x21f5e0b5,
+ 0x11f40a4a,
+ 0x3d21f515,
+ 0xc774bd09,
+ 0x1bf408c5,
+ 0x0232f409,
+/* 0x0c5f: i2c_recv_not_wr08 */
+/* 0x0c5f: i2c_recv_done */
+ 0xc7030ef4,
+ 0x21f5f8ce,
+ 0xe0fc0b0a,
+ 0x12f4d0fc,
+ 0x027cb90a,
+ 0x034221f5,
+/* 0x0c74: i2c_recv_exit */
+/* 0x0c76: i2c_init */
+ 0x00f800f8,
+/* 0x0c78: test_recv */
+ 0x05d817f1,
+ 0xcf0614b6,
+ 0x10b60011,
+ 0xd807f101,
+ 0x0604b605,
+ 0xbd0001d0,
+ 0x00e7f104,
+ 0x4fe3f1d9,
+ 0x6221f513,
+/* 0x0c9f: test_init */
+ 0xf100f802,
+ 0xf50800e7,
+ 0xf8026221,
+/* 0x0ca9: idle_recv */
+/* 0x0cab: idle */
+ 0xf400f800,
+ 0x17f10031,
+ 0x14b605d4,
+ 0x0011cf06,
+ 0xf10110b6,
+ 0xb605d407,
+ 0x01d00604,
+/* 0x0cc7: idle_loop */
+ 0xf004bd00,
+ 0x32f45817,
+/* 0x0ccd: idle_proc */
+/* 0x0ccd: idle_proc_exec */
+ 0xb910f902,
+ 0x21f5021e,
+ 0x10fc034b,
+ 0xf40911f4,
+ 0x0ef40231,
+/* 0x0ce1: idle_proc_next */
+ 0x5810b6ef,
+ 0xf4061fb8,
+ 0x02f4e61b,
+ 0x0028f4dd,
+ 0x00bb0ef4,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+};
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/host.fuc b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/host.fuc
new file mode 100644
index 000000000000..c2bb616a8da5
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/host.fuc
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#ifdef INCLUDE_PROC
+process(PROC_HOST, #host_init, #host_recv)
+#endif
+
+/******************************************************************************
+ * HOST data segment
+ *****************************************************************************/
+#ifdef INCLUDE_DATA
+// HOST (R)FIFO packet format
+.equ #fifo_process 0x00
+.equ #fifo_message 0x04
+.equ #fifo_data0 0x08
+.equ #fifo_data1 0x0c
+
+// HOST HOST->PWR queue description
+.equ #fifo_qlen 4 // log2(size of queue entry in bytes)
+.equ #fifo_qnum 3 // log2(max number of entries in queue)
+.equ #fifo_qmaskb (1 << #fifo_qnum) // max number of entries in queue
+.equ #fifo_qmaskp (#fifo_qmaskb - 1)
+.equ #fifo_qmaskf ((#fifo_qmaskb << 1) - 1)
+.equ #fifo_qsize (1 << (#fifo_qlen + #fifo_qnum))
+fifo_queue: .skip 128 // #fifo_qsize
+
+// HOST PWR->HOST queue description
+.equ #rfifo_qlen 4 // log2(size of queue entry in bytes)
+.equ #rfifo_qnum 3 // log2(max number of entries in queue)
+.equ #rfifo_qmaskb (1 << #rfifo_qnum) // max number of entries in queue
+.equ #rfifo_qmaskp (#rfifo_qmaskb - 1)
+.equ #rfifo_qmaskf ((#rfifo_qmaskb << 1) - 1)
+.equ #rfifo_qsize (1 << (#rfifo_qlen + #rfifo_qnum))
+rfifo_queue: .skip 128 // #rfifo_qsize
+#endif
+
+/******************************************************************************
+ * HOST code segment
+ *****************************************************************************/
+#ifdef INCLUDE_CODE
+// HOST->PWR comms - dequeue message(s) for process(es) from FIFO
+//
+// $r15 - current (host)
+// $r0 - zero
+host_send:
+ nv_iord($r1, NV_PPWR_FIFO_GET(0))
+ nv_iord($r2, NV_PPWR_FIFO_PUT(0))
+ cmp b32 $r1 $r2
+ bra e #host_send_done
+ // calculate address of message
+ and $r14 $r1 #fifo_qmaskp
+ shl b32 $r14 $r14 #fifo_qlen
+ add b32 $r14 #fifo_queue
+
+ // read message data, and pass to appropriate process
+ ld b32 $r11 D[$r14 + #fifo_data1]
+ ld b32 $r12 D[$r14 + #fifo_data0]
+ ld b32 $r13 D[$r14 + #fifo_message]
+ ld b32 $r14 D[$r14 + #fifo_process]
+ call(send)
+
+ // increment GET
+ add b32 $r1 0x1
+ and $r14 $r1 #fifo_qmaskf
+ nv_iowr(NV_PPWR_FIFO_GET(0), $r14)
+ bra #host_send
+ host_send_done:
+ ret
+
+// PWR->HOST comms - enqueue message for HOST to RFIFO
+//
+// $r15 - current (host)
+// $r14 - process
+// $r13 - message
+// $r12 - message data 0
+// $r11 - message data 1
+// $r0 - zero
+host_recv:
+ // message from intr handler == HOST->PWR comms pending
+ mov $r1 (PROC_KERN & 0x0000ffff)
+ sethi $r1 (PROC_KERN & 0xffff0000)
+ cmp b32 $r14 $r1
+ bra e #host_send
+
+ // wait for space in RFIFO
+ host_recv_wait:
+ nv_iord($r1, NV_PPWR_RFIFO_GET)
+ nv_iord($r2, NV_PPWR_RFIFO_PUT)
+ xor $r1 #rfifo_qmaskb
+ cmp b32 $r1 $r2
+ bra e #host_recv_wait
+
+ and $r3 $r2 #rfifo_qmaskp
+ shl b32 $r3 #rfifo_qlen
+ add b32 $r3 #rfifo_queue
+
+ // enqueue message
+ st b32 D[$r3 + #fifo_data1] $r11
+ st b32 D[$r3 + #fifo_data0] $r12
+ st b32 D[$r3 + #fifo_message] $r13
+ st b32 D[$r3 + #fifo_process] $r14
+
+ add b32 $r2 0x1
+ and $r2 #rfifo_qmaskf
+ nv_iowr(NV_PPWR_RFIFO_PUT, $r2)
+
+ // notify host of pending message
+ mov $r2 NV_PPWR_INTR_TRIGGER_USER0
+ nv_iowr(NV_PPWR_INTR_TRIGGER, $r2)
+ ret
+
+// $r15 - current (host)
+// $r0 - zero
+host_init:
+ // store each fifo's base/size in H2D/D2H scratch regs
+ mov $r1 #fifo_qsize
+ shl b32 $r1 16
+ or $r1 #fifo_queue
+ nv_iowr(NV_PPWR_H2D, $r1);
+
+ mov $r1 #rfifo_qsize
+ shl b32 $r1 16
+ or $r1 #rfifo_queue
+ nv_iowr(NV_PPWR_D2H, $r1);
+
+ // enable fifo subintr for first fifo
+ mov $r1 1
+ nv_iowr(NV_PPWR_FIFO_INTR_EN, $r1)
+ ret
+#endif
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/i2c_.fuc b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/i2c_.fuc
new file mode 100644
index 000000000000..757dda700024
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/i2c_.fuc
@@ -0,0 +1,393 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#define T_TIMEOUT 2200000
+#define T_RISEFALL 1000
+#define T_HOLD 5000
+
+#ifdef INCLUDE_PROC
+process(PROC_I2C_, #i2c_init, #i2c_recv)
+#endif
+
+/******************************************************************************
+ * I2C_ data segment
+ *****************************************************************************/
+#ifdef INCLUDE_DATA
+i2c_scl_map:
+.b32 NV_PPWR_OUTPUT_I2C_0_SCL
+.b32 NV_PPWR_OUTPUT_I2C_1_SCL
+.b32 NV_PPWR_OUTPUT_I2C_2_SCL
+.b32 NV_PPWR_OUTPUT_I2C_3_SCL
+.b32 NV_PPWR_OUTPUT_I2C_4_SCL
+.b32 NV_PPWR_OUTPUT_I2C_5_SCL
+.b32 NV_PPWR_OUTPUT_I2C_6_SCL
+.b32 NV_PPWR_OUTPUT_I2C_7_SCL
+.b32 NV_PPWR_OUTPUT_I2C_8_SCL
+.b32 NV_PPWR_OUTPUT_I2C_9_SCL
+i2c_sda_map:
+.b32 NV_PPWR_OUTPUT_I2C_0_SDA
+.b32 NV_PPWR_OUTPUT_I2C_1_SDA
+.b32 NV_PPWR_OUTPUT_I2C_2_SDA
+.b32 NV_PPWR_OUTPUT_I2C_3_SDA
+.b32 NV_PPWR_OUTPUT_I2C_4_SDA
+.b32 NV_PPWR_OUTPUT_I2C_5_SDA
+.b32 NV_PPWR_OUTPUT_I2C_6_SDA
+.b32 NV_PPWR_OUTPUT_I2C_7_SDA
+.b32 NV_PPWR_OUTPUT_I2C_8_SDA
+.b32 NV_PPWR_OUTPUT_I2C_9_SDA
+#if NVKM_PPWR_CHIPSET < GF119
+i2c_ctrl:
+.b32 0x00e138
+.b32 0x00e150
+.b32 0x00e168
+.b32 0x00e180
+.b32 0x00e254
+.b32 0x00e274
+.b32 0x00e764
+.b32 0x00e780
+.b32 0x00e79c
+.b32 0x00e7b8
+#endif
+#endif
+
+/******************************************************************************
+ * I2C_ code segment
+ *****************************************************************************/
+#ifdef INCLUDE_CODE
+
+// $r3 - value
+// $r2 - sda line
+// $r1 - scl line
+// $r0 - zero
+i2c_drive_scl:
+ cmp b32 $r3 0
+ bra e #i2c_drive_scl_lo
+ nv_iowr(NV_PPWR_OUTPUT_SET, $r1)
+ ret
+ i2c_drive_scl_lo:
+ nv_iowr(NV_PPWR_OUTPUT_CLR, $r1)
+ ret
+
+i2c_drive_sda:
+ cmp b32 $r3 0
+ bra e #i2c_drive_sda_lo
+ nv_iowr(NV_PPWR_OUTPUT_SET, $r2)
+ ret
+ i2c_drive_sda_lo:
+ nv_iowr(NV_PPWR_OUTPUT_CLR, $r2)
+ ret
+
+i2c_sense_scl:
+ bclr $flags $p1
+ nv_iord($r3, NV_PPWR_INPUT)
+ and $r3 $r1
+ bra z #i2c_sense_scl_done
+ bset $flags $p1
+ i2c_sense_scl_done:
+ ret
+
+i2c_sense_sda:
+ bclr $flags $p1
+ nv_iord($r3, NV_PPWR_INPUT)
+ and $r3 $r2
+ bra z #i2c_sense_sda_done
+ bset $flags $p1
+ i2c_sense_sda_done:
+ ret
+
+#define i2c_drive_scl(v) /*
+*/ mov $r3 (v) /*
+*/ call(i2c_drive_scl)
+#define i2c_drive_sda(v) /*
+*/ mov $r3 (v) /*
+*/ call(i2c_drive_sda)
+#define i2c_sense_scl() /*
+*/ call(i2c_sense_scl)
+#define i2c_sense_sda() /*
+*/ call(i2c_sense_sda)
+#define i2c_delay(v) /*
+*/ mov $r14 (v) /*
+*/ call(nsec)
+
+#define i2c_trace_init() /*
+*/ imm32($r6, 0x10000000) /*
+*/ sub b32 $r7 $r6 1 /*
+*/
+#define i2c_trace_down() /*
+*/ shr b32 $r6 4 /*
+*/ push $r5 /*
+*/ shl b32 $r5 $r6 4 /*
+*/ sub b32 $r5 $r6 /*
+*/ not b32 $r5 /*
+*/ and $r7 $r5 /*
+*/ pop $r5 /*
+*/
+#define i2c_trace_exit() /*
+*/ shl b32 $r6 4 /*
+*/
+#define i2c_trace_next() /*
+*/ add b32 $r7 $r6 /*
+*/
+#define i2c_trace_call(func) /*
+*/ i2c_trace_next() /*
+*/ i2c_trace_down() /*
+*/ call(func) /*
+*/ i2c_trace_exit() /*
+*/
+
+i2c_raise_scl:
+ push $r4
+ mov $r4 (T_TIMEOUT / T_RISEFALL)
+ i2c_drive_scl(1)
+ i2c_raise_scl_wait:
+ i2c_delay(T_RISEFALL)
+ i2c_sense_scl()
+ bra $p1 #i2c_raise_scl_done
+ sub b32 $r4 1
+ bra nz #i2c_raise_scl_wait
+ i2c_raise_scl_done:
+ pop $r4
+ ret
+
+i2c_start:
+ i2c_sense_scl()
+ bra not $p1 #i2c_start_rep
+ i2c_sense_sda()
+ bra not $p1 #i2c_start_rep
+ bra #i2c_start_send
+ i2c_start_rep:
+ i2c_drive_scl(0)
+ i2c_drive_sda(1)
+ i2c_trace_call(i2c_raise_scl)
+ bra not $p1 #i2c_start_out
+ i2c_start_send:
+ i2c_drive_sda(0)
+ i2c_delay(T_HOLD)
+ i2c_drive_scl(0)
+ i2c_delay(T_HOLD)
+ i2c_start_out:
+ ret
+
+i2c_stop:
+ i2c_drive_scl(0)
+ i2c_drive_sda(0)
+ i2c_delay(T_RISEFALL)
+ i2c_drive_scl(1)
+ i2c_delay(T_HOLD)
+ i2c_drive_sda(1)
+ i2c_delay(T_HOLD)
+ ret
+
+// $r3 - value
+// $r2 - sda line
+// $r1 - scl line
+// $r0 - zero
+i2c_bitw:
+ call(i2c_drive_sda)
+ i2c_delay(T_RISEFALL)
+ i2c_trace_call(i2c_raise_scl)
+ bra not $p1 #i2c_bitw_out
+ i2c_delay(T_HOLD)
+ i2c_drive_scl(0)
+ i2c_delay(T_HOLD)
+ i2c_bitw_out:
+ ret
+
+// $r3 - value (out)
+// $r2 - sda line
+// $r1 - scl line
+// $r0 - zero
+i2c_bitr:
+ i2c_drive_sda(1)
+ i2c_delay(T_RISEFALL)
+ i2c_trace_call(i2c_raise_scl)
+ bra not $p1 #i2c_bitr_done
+ i2c_sense_sda()
+ i2c_drive_scl(0)
+ i2c_delay(T_HOLD)
+ xbit $r3 $flags $p1
+ bset $flags $p1
+ i2c_bitr_done:
+ ret
+
+i2c_get_byte:
+ mov $r5 0
+ mov $r4 8
+ i2c_get_byte_next:
+ shl b32 $r5 1
+ i2c_trace_call(i2c_bitr)
+ bra not $p1 #i2c_get_byte_done
+ or $r5 $r3
+ sub b32 $r4 1
+ bra nz #i2c_get_byte_next
+ mov $r3 1
+ i2c_trace_call(i2c_bitw)
+ i2c_get_byte_done:
+ ret
+
+i2c_put_byte:
+ mov $r4 8
+ i2c_put_byte_next:
+ sub b32 $r4 1
+ xbit $r3 $r5 $r4
+ i2c_trace_call(i2c_bitw)
+ bra not $p1 #i2c_put_byte_done
+ cmp b32 $r4 0
+ bra ne #i2c_put_byte_next
+ i2c_trace_call(i2c_bitr)
+ bra not $p1 #i2c_put_byte_done
+ i2c_trace_next()
+ cmp b32 $r3 1
+ bra ne #i2c_put_byte_done
+ bclr $flags $p1 // nack
+ i2c_put_byte_done:
+ ret
+
+i2c_addr:
+ i2c_trace_call(i2c_start)
+ bra not $p1 #i2c_addr_done
+ extr $r3 $r12 I2C__MSG_DATA0_ADDR
+ shl b32 $r3 1
+ or $r5 $r3
+ i2c_trace_call(i2c_put_byte)
+ i2c_addr_done:
+ ret
+
+i2c_acquire_addr:
+ extr $r14 $r12 I2C__MSG_DATA0_PORT
+#if NVKM_PPWR_CHIPSET < GF119
+ shl b32 $r14 2
+ add b32 $r14 #i2c_ctrl
+ ld b32 $r14 D[$r14]
+#else
+ shl b32 $r14 5
+ add b32 $r14 0x00d014
+#endif
+ ret
+
+i2c_acquire:
+ call(i2c_acquire_addr)
+ call(rd32)
+ bset $r13 3
+ call(wr32)
+ ret
+
+i2c_release:
+ call(i2c_acquire_addr)
+ call(rd32)
+ bclr $r13 3
+ call(wr32)
+ ret
+
+// description
+//
+// $r15 - current (i2c)
+// $r14 - sender process name
+// $r13 - message
+// $r12 - data0
+// $r11 - data1
+// $r0 - zero
+i2c_recv:
+ bclr $flags $p1
+ extr $r1 $r12 I2C__MSG_DATA0_PORT
+ shl b32 $r1 2
+ cmp b32 $r1 (#i2c_sda_map - #i2c_scl_map)
+ bra ge #i2c_recv_done
+ add b32 $r3 $r1 #i2c_sda_map
+ ld b32 $r2 D[$r3]
+ add b32 $r3 $r1 #i2c_scl_map
+ ld b32 $r1 D[$r3]
+
+ bset $flags $p2
+ push $r13
+ push $r14
+
+ push $r13
+ i2c_trace_init()
+ i2c_trace_call(i2c_acquire)
+ pop $r13
+
+ cmp b32 $r13 I2C__MSG_RD08
+ bra ne #i2c_recv_not_rd08
+ mov $r5 0
+ i2c_trace_call(i2c_addr)
+ bra not $p1 #i2c_recv_done
+ extr $r5 $r12 I2C__MSG_DATA0_RD08_REG
+ i2c_trace_call(i2c_put_byte)
+ bra not $p1 #i2c_recv_done
+ mov $r5 1
+ i2c_trace_call(i2c_addr)
+ bra not $p1 #i2c_recv_done
+ i2c_trace_call(i2c_get_byte)
+ bra not $p1 #i2c_recv_done
+ ins $r11 $r5 I2C__MSG_DATA1_RD08_VAL
+ i2c_trace_call(i2c_stop)
+ mov b32 $r11 $r5
+ clear b32 $r7
+ bra #i2c_recv_done
+
+ i2c_recv_not_rd08:
+ cmp b32 $r13 I2C__MSG_WR08
+ bra ne #i2c_recv_not_wr08
+ mov $r5 0
+ call(i2c_addr)
+ bra not $p1 #i2c_recv_done
+ extr $r5 $r12 I2C__MSG_DATA0_WR08_REG
+ call(i2c_put_byte)
+ bra not $p1 #i2c_recv_done
+ mov $r5 0
+ call(i2c_addr)
+ bra not $p1 #i2c_recv_done
+ extr $r5 $r11 I2C__MSG_DATA1_WR08_VAL
+ call(i2c_put_byte)
+ bra not $p1 #i2c_recv_done
+ call(i2c_stop)
+ clear b32 $r7
+ extr $r5 $r12 I2C__MSG_DATA0_WR08_SYNC
+ bra nz #i2c_recv_done
+ bclr $flags $p2
+ bra #i2c_recv_done
+
+ i2c_recv_not_wr08:
+
+ i2c_recv_done:
+ extr $r14 $r12 I2C__MSG_DATA0_PORT
+ call(i2c_release)
+
+ pop $r14
+ pop $r13
+ bra not $p2 #i2c_recv_exit
+ mov b32 $r12 $r7
+ call(send)
+
+ i2c_recv_exit:
+ ret
+
+// description
+//
+// $r15 - current (i2c)
+// $r0 - zero
+i2c_init:
+ ret
+#endif
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/idle.fuc b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/idle.fuc
new file mode 100644
index 000000000000..98f1c3738b42
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/idle.fuc
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#ifdef INCLUDE_PROC
+process(PROC_IDLE, #idle, #idle_recv)
+#endif
+
+/******************************************************************************
+ * IDLE data segment
+ *****************************************************************************/
+#ifdef INCLUDE_DATA
+#endif
+
+/******************************************************************************
+ * IDLE code segment
+ *****************************************************************************/
+#ifdef INCLUDE_CODE
+// description
+//
+// $r15 - current (idle)
+// $r14 - message
+// $r0 - zero
+idle_recv:
+ ret
+
+// description
+//
+// $r15 - current (idle)
+// $r0 - zero
+idle:
+ // set our "no interrupt has occurred during our execution" flag
+ bset $flags $p0
+
+ // count IDLE invocations for debugging purposes
+ nv_iord($r1, NV_PPWR_DSCRATCH(1))
+ add b32 $r1 1
+ nv_iowr(NV_PPWR_DSCRATCH(1), $r1)
+
+ // keep looping while there's pending messages for any process
+ idle_loop:
+ mov $r1 #proc_list_head
+ bclr $flags $p2
+ idle_proc:
+ // process the process' messages until there's none left
+ idle_proc_exec:
+ push $r1
+ mov b32 $r14 $r1
+ call(recv)
+ pop $r1
+ bra not $p1 #idle_proc_next
+ bset $flags $p2
+ bra #idle_proc_exec
+ // next process!
+ idle_proc_next:
+ add b32 $r1 #proc_size
+ cmp b32 $r1 $r15
+ bra ne #idle_proc
+ bra $p2 #idle_loop
+
+ // sleep if no interrupts have occurred
+ sleep $p0
+ bra #idle
+#endif
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/kernel.fuc b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/kernel.fuc
new file mode 100644
index 000000000000..5cf5be63cbef
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/kernel.fuc
@@ -0,0 +1,556 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+/******************************************************************************
+ * kernel data segment
+ *****************************************************************************/
+#ifdef INCLUDE_PROC
+proc_kern:
+process(PROC_KERN, 0, 0)
+proc_list_head:
+#endif
+
+#ifdef INCLUDE_DATA
+proc_list_tail:
+time_prev: .b32 0
+time_next: .b32 0
+#endif
+
+/******************************************************************************
+ * kernel code segment
+ *****************************************************************************/
+#ifdef INCLUDE_CODE
+ bra #init
+
+// read nv register
+//
+// $r15 - current
+// $r14 - addr
+// $r13 - data (return)
+// $r0 - zero
+rd32:
+ nv_iowr(NV_PPWR_MMIO_ADDR, $r14)
+ mov $r13 NV_PPWR_MMIO_CTRL_OP_RD
+ sethi $r13 NV_PPWR_MMIO_CTRL_TRIGGER
+ nv_iowr(NV_PPWR_MMIO_CTRL, $r13)
+ rd32_wait:
+ nv_iord($r13, NV_PPWR_MMIO_CTRL)
+ and $r13 NV_PPWR_MMIO_CTRL_STATUS
+ bra nz #rd32_wait
+ nv_iord($r13, NV_PPWR_MMIO_DATA)
+ ret
+
+// write nv register
+//
+// $r15 - current
+// $r14 - addr
+// $r13 - data
+// $r0 - zero
+wr32:
+ nv_iowr(NV_PPWR_MMIO_ADDR, $r14)
+ nv_iowr(NV_PPWR_MMIO_DATA, $r13)
+ mov $r13 NV_PPWR_MMIO_CTRL_OP_WR
+ or $r13 NV_PPWR_MMIO_CTRL_MASK_B32_0
+ sethi $r13 NV_PPWR_MMIO_CTRL_TRIGGER
+
+#ifdef NVKM_FALCON_MMIO_TRAP
+ push $r13
+ mov $r13 NV_PPWR_INTR_TRIGGER_USER1
+ nv_iowr(NV_PPWR_INTR_TRIGGER, $r13)
+ wr32_host:
+ nv_iord($r13, NV_PPWR_INTR)
+ and $r13 NV_PPWR_INTR_USER1
+ bra nz #wr32_host
+ pop $r13
+#endif
+
+ nv_iowr(NV_PPWR_MMIO_CTRL, $r13)
+ wr32_wait:
+ nv_iord($r13, NV_PPWR_MMIO_CTRL)
+ and $r13 NV_PPWR_MMIO_CTRL_STATUS
+ bra nz #wr32_wait
+ ret
+
+// busy-wait for a period of time
+//
+// $r15 - current
+// $r14 - ns
+// $r0 - zero
+nsec:
+ push $r9
+ push $r8
+ nv_iord($r8, NV_PPWR_TIMER_LOW)
+ nsec_loop:
+ nv_iord($r9, NV_PPWR_TIMER_LOW)
+ sub b32 $r9 $r8
+ cmp b32 $r9 $r14
+ bra l #nsec_loop
+ pop $r8
+ pop $r9
+ ret
+
+// busy-wait for a period of time
+//
+// $r15 - current
+// $r14 - addr
+// $r13 - mask
+// $r12 - data
+// $r11 - timeout (ns)
+// $r0 - zero
+wait:
+ push $r9
+ push $r8
+ nv_iord($r8, NV_PPWR_TIMER_LOW)
+ wait_loop:
+ nv_rd32($r10, $r14)
+ and $r10 $r13
+ cmp b32 $r10 $r12
+ bra e #wait_done
+ nv_iord($r9, NV_PPWR_TIMER_LOW)
+ sub b32 $r9 $r8
+ cmp b32 $r9 $r11
+ bra l #wait_loop
+ wait_done:
+ pop $r8
+ pop $r9
+ ret
+
+// $r15 - current (kern)
+// $r14 - process
+// $r8 - NV_PPWR_INTR
+intr_watchdog:
+ // read process' timer status, skip if not enabled
+ ld b32 $r9 D[$r14 + #proc_time]
+ cmp b32 $r9 0
+ bra z #intr_watchdog_next_proc
+
+ // subtract last timer's value from process' timer,
+ // if it's <= 0 then the timer has expired
+ ld b32 $r10 D[$r0 + #time_prev]
+ sub b32 $r9 $r10
+ bra g #intr_watchdog_next_time
+ mov $r13 KMSG_ALARM
+ call(send_proc)
+ clear b32 $r9
+ bra #intr_watchdog_next_proc
+
+ // otherwise, update the next timer's value if this
+ // process' timer is the soonest
+ intr_watchdog_next_time:
+ // ... or if there's no next timer yet
+ ld b32 $r10 D[$r0 + #time_next]
+ cmp b32 $r10 0
+ bra z #intr_watchdog_next_time_set
+
+ cmp b32 $r9 $r10
+ bra g #intr_watchdog_next_proc
+ intr_watchdog_next_time_set:
+ st b32 D[$r0 + #time_next] $r9
+
+ // update process' timer status, and advance
+ intr_watchdog_next_proc:
+ st b32 D[$r14 + #proc_time] $r9
+ add b32 $r14 #proc_size
+ cmp b32 $r14 #proc_list_tail
+ bra ne #intr_watchdog
+ ret
+
+intr:
+ push $r0
+ clear b32 $r0
+ push $r8
+ push $r9
+ push $r10
+ push $r11
+ push $r12
+ push $r13
+ push $r14
+ push $r15
+ mov $r15 #proc_kern
+ mov $r8 $flags
+ push $r8
+
+ nv_iord($r8, NV_PPWR_DSCRATCH(0))
+ add b32 $r8 1
+ nv_iowr(NV_PPWR_DSCRATCH(0), $r8)
+
+ nv_iord($r8, NV_PPWR_INTR)
+ and $r9 $r8 NV_PPWR_INTR_WATCHDOG
+ bra z #intr_skip_watchdog
+ st b32 D[$r0 + #time_next] $r0
+ mov $r14 #proc_list_head
+ call(intr_watchdog)
+ ld b32 $r9 D[$r0 + #time_next]
+ cmp b32 $r9 0
+ bra z #intr_skip_watchdog
+ nv_iowr(NV_PPWR_WATCHDOG_TIME, $r9)
+ st b32 D[$r0 + #time_prev] $r9
+
+ intr_skip_watchdog:
+ and $r9 $r8 NV_PPWR_INTR_SUBINTR
+ bra z #intr_skip_subintr
+ nv_iord($r9, NV_PPWR_SUBINTR)
+ and $r10 $r9 NV_PPWR_SUBINTR_FIFO
+ bra z #intr_subintr_skip_fifo
+ nv_iord($r12, NV_PPWR_FIFO_INTR)
+ push $r12
+ mov $r14 (PROC_HOST & 0x0000ffff)
+ sethi $r14 (PROC_HOST & 0xffff0000)
+ mov $r13 KMSG_FIFO
+ call(send)
+ pop $r12
+ nv_iowr(NV_PPWR_FIFO_INTR, $r12)
+ intr_subintr_skip_fifo:
+ nv_iowr(NV_PPWR_SUBINTR, $r9)
+
+ intr_skip_subintr:
+ and $r9 $r8 NV_PPWR_INTR_PAUSE
+ bra z #intr_skip_pause
+ and $r10 0xffbf
+
+ intr_skip_pause:
+ and $r9 $r8 NV_PPWR_INTR_USER0
+ bra z #intr_skip_user0
+ and $r10 0xffbf
+
+ intr_skip_user0:
+ nv_iowr(NV_PPWR_INTR_ACK, $r8)
+ pop $r8
+ mov $flags $r8
+ pop $r15
+ pop $r14
+ pop $r13
+ pop $r12
+ pop $r11
+ pop $r10
+ pop $r9
+ pop $r8
+ pop $r0
+ bclr $flags $p0
+ iret
+
+// calculate the number of ticks in the specified nanoseconds delay
+//
+// $r15 - current
+// $r14 - ns
+// $r14 - ticks (return)
+// $r0 - zero
+ticks_from_ns:
+ push $r12
+ push $r11
+
+ /* try not losing precision (multiply then divide) */
+ imm32($r13, HW_TICKS_PER_US)
+ call #mulu32_32_64
+
+ /* use an immeditate, it's ok because HW_TICKS_PER_US < 16 bits */
+ div $r12 $r12 1000
+
+ /* check if there wasn't any overflow */
+ cmpu b32 $r11 0
+ bra e #ticks_from_ns_quit
+
+ /* let's divide then multiply, too bad for the precision! */
+ div $r14 $r14 1000
+ imm32($r13, HW_TICKS_PER_US)
+ call #mulu32_32_64
+
+ /* this cannot overflow as long as HW_TICKS_PER_US < 1000 */
+
+ticks_from_ns_quit:
+ mov b32 $r14 $r12
+ pop $r11
+ pop $r12
+ ret
+
+// calculate the number of ticks in the specified microsecond delay
+//
+// $r15 - current
+// $r14 - us
+// $r14 - ticks (return)
+// $r0 - zero
+ticks_from_us:
+ push $r12
+ push $r11
+
+ /* simply multiply $us by HW_TICKS_PER_US */
+ imm32($r13, HW_TICKS_PER_US)
+ call #mulu32_32_64
+ mov b32 $r14 $r12
+
+ /* check if there wasn't any overflow */
+ cmpu b32 $r11 0
+ bra e #ticks_from_us_quit
+
+ /* Overflow! */
+ clear b32 $r14
+
+ticks_from_us_quit:
+ pop $r11
+ pop $r12
+ ret
+
+// calculate the number of ticks in the specified microsecond delay
+//
+// $r15 - current
+// $r14 - ticks
+// $r14 - us (return)
+// $r0 - zero
+ticks_to_us:
+ /* simply divide $ticks by HW_TICKS_PER_US */
+ imm32($r13, HW_TICKS_PER_US)
+ div $r14 $r14 $r13
+
+ ret
+
+// request the current process be sent a message after a timeout expires
+//
+// $r15 - current
+// $r14 - ticks (make sure it is < 2^31 to avoid any possible overflow)
+// $r0 - zero
+timer:
+ push $r9
+ push $r8
+
+ // interrupts off to prevent racing with timer isr
+ bclr $flags ie0
+
+ // if current process already has a timer set, bail
+ ld b32 $r8 D[$r15 + #proc_time]
+ cmp b32 $r8 0
+ bra g #timer_done
+
+ // halt watchdog timer temporarily
+ clear b32 $r8
+ nv_iowr(NV_PPWR_WATCHDOG_ENABLE, $r8)
+
+ // find out how much time elapsed since the last update
+ // of the watchdog and add this time to the wanted ticks
+ nv_iord($r8, NV_PPWR_WATCHDOG_TIME)
+ ld b32 $r9 D[$r0 + #time_prev]
+ sub b32 $r9 $r8
+ add b32 $r14 $r9
+ st b32 D[$r15 + #proc_time] $r14
+
+ // check for a pending interrupt. if there's one already
+ // pending, we can just bail since the timer isr will
+ // queue the next soonest right after it's done
+ nv_iord($r8, NV_PPWR_INTR)
+ and $r8 NV_PPWR_INTR_WATCHDOG
+ bra nz #timer_enable
+
+ // update the watchdog if this timer should expire first,
+ // or if there's no timeout already set
+ nv_iord($r8, NV_PPWR_WATCHDOG_TIME)
+ cmp b32 $r14 $r0
+ bra e #timer_reset
+ cmp b32 $r14 $r8
+ bra g #timer_enable
+ timer_reset:
+ nv_iowr(NV_PPWR_WATCHDOG_TIME, $r14)
+ st b32 D[$r0 + #time_prev] $r14
+
+ // re-enable the watchdog timer
+ timer_enable:
+ mov $r8 1
+ nv_iowr(NV_PPWR_WATCHDOG_ENABLE, $r8)
+
+ // interrupts back on
+ timer_done:
+ bset $flags ie0
+
+ pop $r8
+ pop $r9
+ ret
+
+// send message to another process
+//
+// $r15 - current
+// $r14 - process
+// $r13 - message
+// $r12 - message data 0
+// $r11 - message data 1
+// $r0 - zero
+send_proc:
+ push $r8
+ push $r9
+ // check for space in queue
+ ld b32 $r8 D[$r14 + #proc_qget]
+ ld b32 $r9 D[$r14 + #proc_qput]
+ xor $r8 #proc_qmaskb
+ cmp b32 $r8 $r9
+ bra e #send_done
+
+ // enqueue message
+ and $r8 $r9 #proc_qmaskp
+ shl b32 $r8 $r8 #proc_qlen
+ add b32 $r8 #proc_queue
+ add b32 $r8 $r14
+
+ ld b32 $r10 D[$r15 + #proc_id]
+ st b32 D[$r8 + #msg_process] $r10
+ st b32 D[$r8 + #msg_message] $r13
+ st b32 D[$r8 + #msg_data0] $r12
+ st b32 D[$r8 + #msg_data1] $r11
+
+ // increment PUT
+ add b32 $r9 1
+ and $r9 #proc_qmaskf
+ st b32 D[$r14 + #proc_qput] $r9
+ bset $flags $p2
+ send_done:
+ pop $r9
+ pop $r8
+ ret
+
+// lookup process structure by its name
+//
+// $r15 - current
+// $r14 - process name
+// $r0 - zero
+//
+// $r14 - process
+// $p1 - success
+find:
+ push $r8
+ mov $r8 #proc_list_head
+ bset $flags $p1
+ find_loop:
+ ld b32 $r10 D[$r8 + #proc_id]
+ cmp b32 $r10 $r14
+ bra e #find_done
+ add b32 $r8 #proc_size
+ cmp b32 $r8 #proc_list_tail
+ bra ne #find_loop
+ bclr $flags $p1
+ find_done:
+ mov b32 $r14 $r8
+ pop $r8
+ ret
+
+// send message to another process
+//
+// $r15 - current
+// $r14 - process id
+// $r13 - message
+// $r12 - message data 0
+// $r11 - message data 1
+// $r0 - zero
+send:
+ call(find)
+ bra $p1 #send_proc
+ ret
+
+// process single message for a given process
+//
+// $r15 - current
+// $r14 - process
+// $r0 - zero
+recv:
+ push $r9
+ push $r8
+
+ ld b32 $r8 D[$r14 + #proc_qget]
+ ld b32 $r9 D[$r14 + #proc_qput]
+ bclr $flags $p1
+ cmp b32 $r8 $r9
+ bra e #recv_done
+ // dequeue message
+ and $r9 $r8 #proc_qmaskp
+ add b32 $r8 1
+ and $r8 #proc_qmaskf
+ st b32 D[$r14 + #proc_qget] $r8
+ ld b32 $r10 D[$r14 + #proc_recv]
+
+ push $r15
+ mov $r15 $flags
+ push $r15
+ mov b32 $r15 $r14
+
+ shl b32 $r9 $r9 #proc_qlen
+ add b32 $r14 $r9
+ add b32 $r14 #proc_queue
+ ld b32 $r11 D[$r14 + #msg_data1]
+ ld b32 $r12 D[$r14 + #msg_data0]
+ ld b32 $r13 D[$r14 + #msg_message]
+ ld b32 $r14 D[$r14 + #msg_process]
+
+ // process it
+ call $r10
+ pop $r15
+ mov $flags $r15
+ bset $flags $p1
+ pop $r15
+ recv_done:
+ pop $r8
+ pop $r9
+ ret
+
+init:
+ // setup stack
+ nv_iord($r1, NV_PPWR_CAPS)
+ extr $r1 $r1 9:17
+ shl b32 $r1 8
+ mov $sp $r1
+
+#ifdef NVKM_FALCON_MMIO_UAS
+ // somehow allows the magic "access mmio via D[]" stuff that's
+ // used by the nv_rd32/nv_wr32 macros to work
+ mov $r1 0x0010
+ sethi $r1 NV_PPWR_UAS_CONFIG_ENABLE
+ nv_iowrs(NV_PPWR_UAS_CONFIG, $r1)
+#endif
+
+ // route all interrupts except user0/1 and pause to fuc
+ mov $r1 0x00e0
+ sethi $r1 0x00000000
+ nv_iowr(NV_PPWR_INTR_ROUTE, $r1)
+
+ // enable watchdog and subintr intrs
+ mov $r1 NV_PPWR_INTR_EN_CLR_MASK
+ nv_iowr(NV_PPWR_INTR_EN_CLR, $r1)
+ mov $r1 NV_PPWR_INTR_EN_SET_WATCHDOG
+ or $r1 NV_PPWR_INTR_EN_SET_SUBINTR
+ nv_iowr(NV_PPWR_INTR_EN_SET, $r1)
+
+ // enable interrupts globally
+ mov $r1 #intr
+ sethi $r1 0x00000000
+ mov $iv0 $r1
+ bset $flags ie0
+
+ // enable watchdog timer
+ mov $r1 1
+ nv_iowr(NV_PPWR_WATCHDOG_ENABLE, $r1)
+
+ // bootstrap processes, idle process will be last, and not return
+ mov $r15 #proc_list_head
+ init_proc:
+ ld b32 $r1 D[$r15 + #proc_init]
+ cmp b32 $r1 0
+ bra z #init_proc
+ call $r1
+ add b32 $r15 #proc_size
+ bra #init_proc
+#endif
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/macros.fuc b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/macros.fuc
new file mode 100644
index 000000000000..96fc984dafdc
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/macros.fuc
@@ -0,0 +1,272 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#define GT215 0xa3
+#define GF100 0xc0
+#define GF119 0xd9
+#define GK208 0x108
+
+#include "os.h"
+
+// IO addresses
+#define NV_PPWR_INTR_TRIGGER 0x0000
+#define NV_PPWR_INTR_TRIGGER_USER1 0x00000080
+#define NV_PPWR_INTR_TRIGGER_USER0 0x00000040
+#define NV_PPWR_INTR_ACK 0x0004
+#define NV_PPWR_INTR_ACK_SUBINTR 0x00000800
+#define NV_PPWR_INTR_ACK_WATCHDOG 0x00000002
+#define NV_PPWR_INTR 0x0008
+#define NV_PPWR_INTR_SUBINTR 0x00000800
+#define NV_PPWR_INTR_USER1 0x00000080
+#define NV_PPWR_INTR_USER0 0x00000040
+#define NV_PPWR_INTR_PAUSE 0x00000020
+#define NV_PPWR_INTR_WATCHDOG 0x00000002
+#define NV_PPWR_INTR_EN_SET 0x0010
+#define NV_PPWR_INTR_EN_SET_SUBINTR 0x00000800
+#define NV_PPWR_INTR_EN_SET_WATCHDOG 0x00000002
+#define NV_PPWR_INTR_EN_CLR 0x0014
+#define NV_PPWR_INTR_EN_CLR_MASK /* fuck i hate envyas */ -1
+#define NV_PPWR_INTR_ROUTE 0x001c
+#define NV_PPWR_TIMER_LOW 0x002c
+#define NV_PPWR_WATCHDOG_TIME 0x0034
+#define NV_PPWR_WATCHDOG_ENABLE 0x0038
+#define NV_PPWR_CAPS 0x0108
+#define NV_PPWR_UAS_CONFIG 0x0164
+#define NV_PPWR_UAS_CONFIG_ENABLE 0x00010000
+#if NVKM_PPWR_CHIPSET >= GK208
+#define NV_PPWR_DSCRATCH(i) (4 * (i) + 0x0450)
+#endif
+#define NV_PPWR_FIFO_PUT(i) (4 * (i) + 0x04a0)
+#define NV_PPWR_FIFO_GET(i) (4 * (i) + 0x04b0)
+#define NV_PPWR_FIFO_INTR 0x04c0
+#define NV_PPWR_FIFO_INTR_EN 0x04c4
+#define NV_PPWR_RFIFO_PUT 0x04c8
+#define NV_PPWR_RFIFO_GET 0x04cc
+#define NV_PPWR_H2D 0x04d0
+#define NV_PPWR_D2H 0x04dc
+#if NVKM_PPWR_CHIPSET < GK208
+#define NV_PPWR_DSCRATCH(i) (4 * (i) + 0x05d0)
+#endif
+#define NV_PPWR_SUBINTR 0x0688
+#define NV_PPWR_SUBINTR_FIFO 0x00000002
+#define NV_PPWR_MMIO_ADDR 0x07a0
+#define NV_PPWR_MMIO_DATA 0x07a4
+#define NV_PPWR_MMIO_CTRL 0x07ac
+#define NV_PPWR_MMIO_CTRL_TRIGGER 0x00010000
+#define NV_PPWR_MMIO_CTRL_STATUS 0x00007000
+#define NV_PPWR_MMIO_CTRL_STATUS_IDLE 0x00000000
+#define NV_PPWR_MMIO_CTRL_MASK 0x000000f0
+#define NV_PPWR_MMIO_CTRL_MASK_B32_0 0x000000f0
+#define NV_PPWR_MMIO_CTRL_OP 0x00000003
+#define NV_PPWR_MMIO_CTRL_OP_RD 0x00000001
+#define NV_PPWR_MMIO_CTRL_OP_WR 0x00000002
+#define NV_PPWR_OUTPUT 0x07c0
+#define NV_PPWR_OUTPUT_FB_PAUSE 0x00000004
+#if NVKM_PPWR_CHIPSET < GF119
+#define NV_PPWR_OUTPUT_I2C_3_SCL 0x00000100
+#define NV_PPWR_OUTPUT_I2C_3_SDA 0x00000200
+#define NV_PPWR_OUTPUT_I2C_0_SCL 0x00001000
+#define NV_PPWR_OUTPUT_I2C_0_SDA 0x00002000
+#define NV_PPWR_OUTPUT_I2C_1_SCL 0x00004000
+#define NV_PPWR_OUTPUT_I2C_1_SDA 0x00008000
+#define NV_PPWR_OUTPUT_I2C_2_SCL 0x00010000
+#define NV_PPWR_OUTPUT_I2C_2_SDA 0x00020000
+#define NV_PPWR_OUTPUT_I2C_4_SCL 0x00040000
+#define NV_PPWR_OUTPUT_I2C_4_SDA 0x00080000
+#define NV_PPWR_OUTPUT_I2C_5_SCL 0x00100000
+#define NV_PPWR_OUTPUT_I2C_5_SDA 0x00200000
+#define NV_PPWR_OUTPUT_I2C_6_SCL 0x00400000
+#define NV_PPWR_OUTPUT_I2C_6_SDA 0x00800000
+#define NV_PPWR_OUTPUT_I2C_7_SCL 0x01000000
+#define NV_PPWR_OUTPUT_I2C_7_SDA 0x02000000
+#define NV_PPWR_OUTPUT_I2C_8_SCL 0x04000000
+#define NV_PPWR_OUTPUT_I2C_8_SDA 0x08000000
+#define NV_PPWR_OUTPUT_I2C_9_SCL 0x10000000
+#define NV_PPWR_OUTPUT_I2C_9_SDA 0x20000000
+#else
+#define NV_PPWR_OUTPUT_I2C_0_SCL 0x00000400
+#define NV_PPWR_OUTPUT_I2C_1_SCL 0x00000800
+#define NV_PPWR_OUTPUT_I2C_2_SCL 0x00001000
+#define NV_PPWR_OUTPUT_I2C_3_SCL 0x00002000
+#define NV_PPWR_OUTPUT_I2C_4_SCL 0x00004000
+#define NV_PPWR_OUTPUT_I2C_5_SCL 0x00008000
+#define NV_PPWR_OUTPUT_I2C_6_SCL 0x00010000
+#define NV_PPWR_OUTPUT_I2C_7_SCL 0x00020000
+#define NV_PPWR_OUTPUT_I2C_8_SCL 0x00040000
+#define NV_PPWR_OUTPUT_I2C_9_SCL 0x00080000
+#define NV_PPWR_OUTPUT_I2C_0_SDA 0x00100000
+#define NV_PPWR_OUTPUT_I2C_1_SDA 0x00200000
+#define NV_PPWR_OUTPUT_I2C_2_SDA 0x00400000
+#define NV_PPWR_OUTPUT_I2C_3_SDA 0x00800000
+#define NV_PPWR_OUTPUT_I2C_4_SDA 0x01000000
+#define NV_PPWR_OUTPUT_I2C_5_SDA 0x02000000
+#define NV_PPWR_OUTPUT_I2C_6_SDA 0x04000000
+#define NV_PPWR_OUTPUT_I2C_7_SDA 0x08000000
+#define NV_PPWR_OUTPUT_I2C_8_SDA 0x10000000
+#define NV_PPWR_OUTPUT_I2C_9_SDA 0x20000000
+#endif
+#define NV_PPWR_INPUT 0x07c4
+#define NV_PPWR_OUTPUT_SET 0x07e0
+#define NV_PPWR_OUTPUT_SET_FB_PAUSE 0x00000004
+#define NV_PPWR_OUTPUT_CLR 0x07e4
+#define NV_PPWR_OUTPUT_CLR_FB_PAUSE 0x00000004
+
+// Inter-process message format
+.equ #msg_process 0x00 /* send() target, recv() sender */
+.equ #msg_message 0x04
+.equ #msg_data0 0x08
+.equ #msg_data1 0x0c
+
+// Kernel message IDs
+#define KMSG_FIFO 0x00000000
+#define KMSG_ALARM 0x00000001
+
+// Process message queue description
+.equ #proc_qlen 4 // log2(size of queue entry in bytes)
+.equ #proc_qnum 2 // log2(max number of entries in queue)
+.equ #proc_qmaskb (1 << #proc_qnum) // max number of entries in queue
+.equ #proc_qmaskp (#proc_qmaskb - 1)
+.equ #proc_qmaskf ((#proc_qmaskb << 1) - 1)
+.equ #proc_qsize (1 << (#proc_qlen + #proc_qnum))
+
+// Process table entry
+.equ #proc_id 0x00
+.equ #proc_init 0x04
+.equ #proc_recv 0x08
+.equ #proc_time 0x0c
+.equ #proc_qput 0x10
+.equ #proc_qget 0x14
+.equ #proc_queue 0x18
+.equ #proc_size (0x18 + #proc_qsize)
+
+#define process(id,init,recv) /*
+*/ .b32 id /*
+*/ .b32 init /*
+*/ .b32 recv /*
+*/ .b32 0 /*
+*/ .b32 0 /*
+*/ .b32 0 /*
+*/ .skip 64
+
+#if NV_PPWR_CHIPSET < GK208
+#define imm32(reg,val) /*
+*/ movw reg ((val) & 0x0000ffff) /*
+*/ sethi reg ((val) & 0xffff0000)
+#else
+#define imm32(reg,val) /*
+*/ mov reg (val)
+#endif
+
+#ifndef NVKM_FALCON_UNSHIFTED_IO
+#define nv_iord(reg,ior) /*
+*/ mov reg ior /*
+*/ shl b32 reg 6 /*
+*/ iord reg I[reg + 0x000]
+#else
+#define nv_iord(reg,ior) /*
+*/ mov reg ior /*
+*/ iord reg I[reg + 0x000]
+#endif
+
+#ifndef NVKM_FALCON_UNSHIFTED_IO
+#define nv_iowr(ior,reg) /*
+*/ mov $r0 ior /*
+*/ shl b32 $r0 6 /*
+*/ iowr I[$r0 + 0x000] reg /*
+*/ clear b32 $r0
+#else
+#define nv_iowr(ior,reg) /*
+*/ mov $r0 ior /*
+*/ iowr I[$r0 + 0x000] reg /*
+*/ clear b32 $r0
+#endif
+
+#ifndef NVKM_FALCON_UNSHIFTED_IO
+#define nv_iowrs(ior,reg) /*
+*/ mov $r0 ior /*
+*/ shl b32 $r0 6 /*
+*/ iowrs I[$r0 + 0x000] reg /*
+*/ clear b32 $r0
+#else
+#define nv_iowrs(ior,reg) /*
+*/ mov $r0 ior /*
+*/ iowrs I[$r0 + 0x000] reg /*
+*/ clear b32 $r0
+#endif
+
+#define hash #
+#define fn(a) a
+#ifndef NVKM_FALCON_PC24
+#define call(a) call fn(hash)a
+#else
+#define call(a) lcall fn(hash)a
+#endif
+
+#ifndef NVKM_FALCON_MMIO_UAS
+#define nv_rd32(reg,addr) /*
+*/ mov b32 $r14 addr /*
+*/ call(rd32) /*
+*/ mov b32 reg $r13
+#else
+#define nv_rd32(reg,addr) /*
+*/ sethi $r0 0x14000000 /*
+*/ or $r0 addr /*
+*/ ld b32 reg D[$r0] /*
+*/ clear b32 $r0
+#endif
+
+#if !defined(NVKM_FALCON_MMIO_UAS) || defined(NVKM_FALCON_MMIO_TRAP)
+#define nv_wr32(addr,reg) /*
+*/ push addr /*
+*/ push reg /*
+*/ pop $r13 /*
+*/ pop $r14 /*
+*/ call(wr32)
+#else
+#define nv_wr32(addr,reg) /*
+*/ sethi $r0 0x14000000 /*
+*/ or $r0 addr /*
+*/ st b32 D[$r0] reg /*
+*/ clear b32 $r0
+#endif
+
+#define st(size, addr, reg) /*
+*/ movw $r0 addr /*
+*/ st size D[$r0] reg /*
+*/ clear b32 $r0
+
+#define ld(size, reg, addr) /*
+*/ movw $r0 addr /*
+*/ ld size reg D[$r0] /*
+*/ clear b32 $r0
+
+// does a 64+64 -> 64 unsigned addition (C = A + B)
+#define addu64(reg_a_c_hi, reg_a_c_lo, b_hi, b_lo) /*
+*/ add b32 reg_a_c_lo b_lo /*
+*/ adc b32 reg_a_c_hi b_hi
+
+// does a 64+64 -> 64 substraction (C = A - B)
+#define subu64(reg_a_c_hi, reg_a_c_lo, b_hi, b_lo) /*
+*/ sub b32 reg_a_c_lo b_lo /*
+*/ sbb b32 reg_a_c_hi b_hi
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/memx.fuc b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/memx.fuc
new file mode 100644
index 000000000000..ec03f9a4290b
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/memx.fuc
@@ -0,0 +1,447 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#ifdef INCLUDE_PROC
+process(PROC_MEMX, #memx_init, #memx_recv)
+#endif
+
+/******************************************************************************
+ * MEMX data segment
+ *****************************************************************************/
+#ifdef INCLUDE_DATA
+.equ #memx_opcode 0
+.equ #memx_header 2
+.equ #memx_length 4
+.equ #memx_func 8
+
+#define handler(cmd,hdr,len,func) /*
+*/ .b16 MEMX_##cmd /*
+*/ .b16 hdr /*
+*/ .b16 len /*
+*/ .b16 0 /*
+*/ .b32 func
+
+memx_func_head:
+handler(ENTER , 0x0000, 0x0000, #memx_func_enter)
+memx_func_next:
+handler(LEAVE , 0x0000, 0x0000, #memx_func_leave)
+handler(WR32 , 0x0000, 0x0002, #memx_func_wr32)
+handler(WAIT , 0x0004, 0x0000, #memx_func_wait)
+handler(DELAY , 0x0001, 0x0000, #memx_func_delay)
+handler(VBLANK, 0x0001, 0x0000, #memx_func_wait_vblank)
+handler(TRAIN , 0x0000, 0x0000, #memx_func_train)
+memx_func_tail:
+
+.equ #memx_func_size #memx_func_next - #memx_func_head
+.equ #memx_func_num (#memx_func_tail - #memx_func_head) / #memx_func_size
+
+memx_ts_start:
+.b32 0
+memx_ts_end:
+.b32 0
+
+memx_data_head:
+.skip 0x0800
+memx_data_tail:
+
+memx_train_head:
+.skip 0x0100
+memx_train_tail:
+#endif
+
+/******************************************************************************
+ * MEMX code segment
+ *****************************************************************************/
+#ifdef INCLUDE_CODE
+// description
+//
+// $r15 - current (memx)
+// $r4 - packet length
+// $r3 - opcode desciption
+// $r0 - zero
+memx_func_enter:
+#if NVKM_PPWR_CHIPSET == GT215
+ movw $r8 0x1610
+ nv_rd32($r7, $r8)
+ imm32($r6, 0xfffffffc)
+ and $r7 $r6
+ movw $r6 0x2
+ or $r7 $r6
+ nv_wr32($r8, $r7)
+#else
+ movw $r6 0x001620
+ imm32($r7, ~0x00000aa2);
+ nv_rd32($r8, $r6)
+ and $r8 $r7
+ nv_wr32($r6, $r8)
+
+ imm32($r7, ~0x00000001)
+ nv_rd32($r8, $r6)
+ and $r8 $r7
+ nv_wr32($r6, $r8)
+
+ movw $r6 0x0026f0
+ nv_rd32($r8, $r6)
+ and $r8 $r7
+ nv_wr32($r6, $r8)
+#endif
+
+ mov $r6 NV_PPWR_OUTPUT_SET_FB_PAUSE
+ nv_iowr(NV_PPWR_OUTPUT_SET, $r6)
+ memx_func_enter_wait:
+ nv_iord($r6, NV_PPWR_OUTPUT)
+ and $r6 NV_PPWR_OUTPUT_FB_PAUSE
+ bra z #memx_func_enter_wait
+
+ nv_iord($r6, NV_PPWR_TIMER_LOW)
+ st b32 D[$r0 + #memx_ts_start] $r6
+ ret
+
+// description
+//
+// $r15 - current (memx)
+// $r4 - packet length
+// $r3 - opcode desciption
+// $r0 - zero
+memx_func_leave:
+ nv_iord($r6, NV_PPWR_TIMER_LOW)
+ st b32 D[$r0 + #memx_ts_end] $r6
+
+ mov $r6 NV_PPWR_OUTPUT_CLR_FB_PAUSE
+ nv_iowr(NV_PPWR_OUTPUT_CLR, $r6)
+ memx_func_leave_wait:
+ nv_iord($r6, NV_PPWR_OUTPUT)
+ and $r6 NV_PPWR_OUTPUT_FB_PAUSE
+ bra nz #memx_func_leave_wait
+
+#if NVKM_PPWR_CHIPSET == GT215
+ movw $r8 0x1610
+ nv_rd32($r7, $r8)
+ imm32($r6, 0xffffffcc)
+ and $r7 $r6
+ nv_wr32($r8, $r7)
+#else
+ movw $r6 0x0026f0
+ imm32($r7, 0x00000001)
+ nv_rd32($r8, $r6)
+ or $r8 $r7
+ nv_wr32($r6, $r8)
+
+ movw $r6 0x001620
+ nv_rd32($r8, $r6)
+ or $r8 $r7
+ nv_wr32($r6, $r8)
+
+ imm32($r7, 0x00000aa2);
+ nv_rd32($r8, $r6)
+ or $r8 $r7
+ nv_wr32($r6, $r8)
+#endif
+ ret
+
+#if NVKM_PPWR_CHIPSET < GF119
+// description
+//
+// $r15 - current (memx)
+// $r4 - packet length
+// +00: head to wait for vblank on
+// $r3 - opcode desciption
+// $r0 - zero
+memx_func_wait_vblank:
+ ld b32 $r6 D[$r1 + 0x00]
+ cmp b32 $r6 0x0
+ bra z #memx_func_wait_vblank_head0
+ cmp b32 $r6 0x1
+ bra z #memx_func_wait_vblank_head1
+ bra #memx_func_wait_vblank_fini
+
+ memx_func_wait_vblank_head1:
+ movw $r7 0x20
+ bra #memx_func_wait_vblank_0
+
+ memx_func_wait_vblank_head0:
+ movw $r7 0x8
+
+ memx_func_wait_vblank_0:
+ nv_iord($r6, NV_PPWR_INPUT)
+ and $r6 $r7
+ bra nz #memx_func_wait_vblank_0
+
+ memx_func_wait_vblank_1:
+ nv_iord($r6, NV_PPWR_INPUT)
+ and $r6 $r7
+ bra z #memx_func_wait_vblank_1
+
+ memx_func_wait_vblank_fini:
+ add b32 $r1 0x4
+ ret
+
+#else
+
+// XXX: currently no-op
+//
+// $r15 - current (memx)
+// $r4 - packet length
+// +00: head to wait for vblank on
+// $r3 - opcode desciption
+// $r0 - zero
+memx_func_wait_vblank:
+ add b32 $r1 0x4
+ ret
+
+#endif
+
+// description
+//
+// $r15 - current (memx)
+// $r4 - packet length
+// +00*n: addr
+// +04*n: data
+// $r3 - opcode desciption
+// $r0 - zero
+memx_func_wr32:
+ ld b32 $r6 D[$r1 + 0x00]
+ ld b32 $r5 D[$r1 + 0x04]
+ add b32 $r1 0x08
+ nv_wr32($r6, $r5)
+ sub b32 $r4 0x02
+ bra nz #memx_func_wr32
+ ret
+
+// description
+//
+// $r15 - current (memx)
+// $r4 - packet length
+// +00: addr
+// +04: mask
+// +08: data
+// +0c: timeout (ns)
+// $r3 - opcode desciption
+// $r0 - zero
+memx_func_wait:
+ nv_iord($r8, NV_PPWR_TIMER_LOW)
+ ld b32 $r14 D[$r1 + 0x00]
+ ld b32 $r13 D[$r1 + 0x04]
+ ld b32 $r12 D[$r1 + 0x08]
+ ld b32 $r11 D[$r1 + 0x0c]
+ add b32 $r1 0x10
+ call(wait)
+ ret
+
+// description
+//
+// $r15 - current (memx)
+// $r4 - packet length
+// +00: time (ns)
+// $r3 - opcode desciption
+// $r0 - zero
+memx_func_delay:
+ ld b32 $r14 D[$r1 + 0x00]
+ add b32 $r1 0x04
+ call(nsec)
+ ret
+
+// description
+//
+// $r15 - current (memx)
+// $r4 - packet length
+// $r3 - opcode desciption
+// $r0 - zero
+memx_func_train:
+#if NVKM_PPWR_CHIPSET == GT215
+// $r5 - outer loop counter
+// $r6 - inner loop counter
+// $r7 - entry counter (#memx_train_head + $r7)
+ movw $r5 0x3
+ movw $r7 0x0
+
+// Read random memory to wake up... things
+ imm32($r9, 0x700000)
+ nv_rd32($r8,$r9)
+ movw $r14 0x2710
+ call(nsec)
+
+ memx_func_train_loop_outer:
+ mulu $r8 $r5 0x101
+ sethi $r8 0x02000000
+ imm32($r9, 0x1111e0)
+ nv_wr32($r9, $r8)
+ push $r5
+
+ movw $r6 0x0
+ memx_func_train_loop_inner:
+ movw $r8 0x1111
+ mulu $r9 $r6 $r8
+ shl b32 $r8 $r9 0x10
+ or $r8 $r9
+ imm32($r9, 0x100720)
+ nv_wr32($r9, $r8)
+
+ imm32($r9, 0x100080)
+ nv_rd32($r8, $r9)
+ or $r8 $r8 0x20
+ nv_wr32($r9, $r8)
+
+ imm32($r9, 0x10053c)
+ imm32($r8, 0x80003002)
+ nv_wr32($r9, $r8)
+
+ imm32($r14, 0x100560)
+ imm32($r13, 0x80000000)
+ add b32 $r12 $r13 0
+ imm32($r11, 0x001e8480)
+ call(wait)
+
+ // $r5 - inner inner loop counter
+ // $r9 - result
+ movw $r5 0
+ imm32($r9, 0x8300ffff)
+ memx_func_train_loop_4x:
+ imm32($r10, 0x100080)
+ nv_rd32($r8, $r10)
+ imm32($r11, 0xffffffdf)
+ and $r8 $r11
+ nv_wr32($r10, $r8)
+
+ imm32($r10, 0x10053c)
+ imm32($r8, 0x80003002)
+ nv_wr32($r10, $r8)
+
+ imm32($r14, 0x100560)
+ imm32($r13, 0x80000000)
+ mov b32 $r12 $r13
+ imm32($r11, 0x00002710)
+ call(wait)
+
+ nv_rd32($r13, $r14)
+ and $r9 $r9 $r13
+
+ add b32 $r5 1
+ cmp b16 $r5 0x4
+ bra l #memx_func_train_loop_4x
+
+ add b32 $r10 $r7 #memx_train_head
+ st b32 D[$r10 + 0] $r9
+ add b32 $r6 1
+ add b32 $r7 4
+
+ cmp b16 $r6 0x10
+ bra l #memx_func_train_loop_inner
+
+ pop $r5
+ add b32 $r5 1
+ cmp b16 $r5 7
+ bra l #memx_func_train_loop_outer
+
+#endif
+ ret
+
+// description
+//
+// $r15 - current (memx)
+// $r14 - sender process name
+// $r13 - message (exec)
+// $r12 - head of script
+// $r11 - tail of script
+// $r0 - zero
+memx_exec:
+ push $r14
+ push $r13
+ mov b32 $r1 $r12
+ mov b32 $r2 $r11
+
+ memx_exec_next:
+ // fetch the packet header
+ ld b32 $r3 D[$r1]
+ add b32 $r1 4
+ extr $r4 $r3 16:31
+ extr $r3 $r3 0:15
+
+ // execute the opcode handler
+ sub b32 $r3 1
+ mulu $r3 #memx_func_size
+ ld b32 $r5 D[$r3 + #memx_func_head + #memx_func]
+ call $r5
+
+ // keep going, if we haven't reached the end
+ cmp b32 $r1 $r2
+ bra l #memx_exec_next
+
+ // send completion reply
+ ld b32 $r11 D[$r0 + #memx_ts_start]
+ ld b32 $r12 D[$r0 + #memx_ts_end]
+ sub b32 $r12 $r11
+ nv_iord($r11, NV_PPWR_INPUT)
+ pop $r13
+ pop $r14
+ call(send)
+ ret
+
+// description
+//
+// $r15 - current (memx)
+// $r14 - sender process name
+// $r13 - message
+// $r12 - data0
+// $r11 - data1
+// $r0 - zero
+memx_info:
+ cmp b16 $r12 0x1
+ bra e #memx_info_train
+
+ memx_info_data:
+ mov $r12 #memx_data_head
+ mov $r11 #memx_data_tail - #memx_data_head
+ bra #memx_info_send
+
+ memx_info_train:
+ mov $r12 #memx_train_head
+ mov $r11 #memx_train_tail - #memx_train_head
+
+ memx_info_send:
+ call(send)
+ ret
+
+// description
+//
+// $r15 - current (memx)
+// $r14 - sender process name
+// $r13 - message
+// $r12 - data0
+// $r11 - data1
+// $r0 - zero
+memx_recv:
+ cmp b32 $r13 MEMX_MSG_EXEC
+ bra e #memx_exec
+ cmp b32 $r13 MEMX_MSG_INFO
+ bra e #memx_info
+ ret
+
+// description
+//
+// $r15 - current (memx)
+// $r0 - zero
+memx_init:
+ ret
+#endif
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/os.h b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/os.h
new file mode 100644
index 000000000000..c8b06cb77e72
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/os.h
@@ -0,0 +1,52 @@
+#ifndef __NVKM_PWR_OS_H__
+#define __NVKM_PWR_OS_H__
+
+/* Process names */
+#define PROC_KERN 0x52544e49
+#define PROC_IDLE 0x454c4449
+#define PROC_HOST 0x54534f48
+#define PROC_MEMX 0x584d454d
+#define PROC_PERF 0x46524550
+#define PROC_I2C_ 0x5f433249
+#define PROC_TEST 0x54534554
+
+/* KERN: message identifiers */
+#define KMSG_FIFO 0x00000000
+#define KMSG_ALARM 0x00000001
+
+/* MEMX: message identifiers */
+#define MEMX_MSG_INFO 0
+#define MEMX_MSG_EXEC 1
+
+/* MEMX: info types */
+#define MEMX_INFO_DATA 0
+#define MEMX_INFO_TRAIN 1
+
+/* MEMX: script opcode definitions */
+#define MEMX_ENTER 1
+#define MEMX_LEAVE 2
+#define MEMX_WR32 3
+#define MEMX_WAIT 4
+#define MEMX_DELAY 5
+#define MEMX_VBLANK 6
+#define MEMX_TRAIN 7
+
+/* I2C_: message identifiers */
+#define I2C__MSG_RD08 0
+#define I2C__MSG_WR08 1
+
+#define I2C__MSG_DATA0_PORT 24:31
+#define I2C__MSG_DATA0_ADDR 14:23
+
+#define I2C__MSG_DATA0_RD08_PORT I2C__MSG_DATA0_PORT
+#define I2C__MSG_DATA0_RD08_ADDR I2C__MSG_DATA0_ADDR
+#define I2C__MSG_DATA0_RD08_REG 0:7
+#define I2C__MSG_DATA1_RD08_VAL 0:7
+
+#define I2C__MSG_DATA0_WR08_PORT I2C__MSG_DATA0_PORT
+#define I2C__MSG_DATA0_WR08_ADDR I2C__MSG_DATA0_ADDR
+#define I2C__MSG_DATA0_WR08_SYNC 8:8
+#define I2C__MSG_DATA0_WR08_REG 0:7
+#define I2C__MSG_DATA1_WR08_VAL 0:7
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/perf.fuc b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/perf.fuc
new file mode 100644
index 000000000000..38eadf705cbf
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/perf.fuc
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#ifdef INCLUDE_PROC
+process(PROC_PERF, #perf_init, #perf_recv)
+#endif
+
+/******************************************************************************
+ * PERF data segment
+ *****************************************************************************/
+#ifdef INCLUDE_DATA
+#endif
+
+/******************************************************************************
+ * PERF code segment
+ *****************************************************************************/
+#ifdef INCLUDE_CODE
+
+// description
+//
+// $r15 - current (perf)
+// $r14 - sender process name
+// $r13 - message
+// $r12 - data0
+// $r11 - data1
+// $r0 - zero
+perf_recv:
+ ret
+
+// description
+//
+// $r15 - current (perf)
+// $r0 - zero
+perf_init:
+ ret
+#endif
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/test.fuc b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/test.fuc
new file mode 100644
index 000000000000..0c3a71bf5459
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/test.fuc
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#ifdef INCLUDE_PROC
+process(PROC_TEST, #test_init, #test_recv)
+#endif
+
+/******************************************************************************
+ * TEST data segment
+ *****************************************************************************/
+#ifdef INCLUDE_DATA
+#endif
+
+/******************************************************************************
+ * TEST code segment
+ *****************************************************************************/
+#ifdef INCLUDE_CODE
+// description
+//
+// $r15 - current (test)
+// $r14 - sender process name
+// $r13 - message
+// $r12 - data0
+// $r11 - data1
+// $r0 - zero
+test_recv:
+ nv_iord($r1, NV_PPWR_DSCRATCH(2))
+ add b32 $r1 1
+ nv_iowr(NV_PPWR_DSCRATCH(2), $r1)
+ mov $r14 -0x2700 /* 0xd900, envyas grrr! */
+ sethi $r14 0x134f0000
+ call(timer)
+ ret
+
+// description
+//
+// $r15 - current (test)
+// $r0 - zero
+test_init:
+ mov $r14 0x800
+ call(timer)
+ ret
+#endif
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gf100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gf100.c
new file mode 100644
index 000000000000..78a4ea0101f1
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gf100.c
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "priv.h"
+#include "fuc/gf100.fuc3.h"
+
+struct nvkm_oclass *
+gf100_pmu_oclass = &(struct nvkm_pmu_impl) {
+ .base.handle = NV_SUBDEV(PMU, 0xc0),
+ .base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = _nvkm_pmu_ctor,
+ .dtor = _nvkm_pmu_dtor,
+ .init = _nvkm_pmu_init,
+ .fini = _nvkm_pmu_fini,
+ },
+ .code.data = gf100_pmu_code,
+ .code.size = sizeof(gf100_pmu_code),
+ .data.data = gf100_pmu_data,
+ .data.size = sizeof(gf100_pmu_data),
+}.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gf110.c b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gf110.c
new file mode 100644
index 000000000000..6b3a23839ff0
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gf110.c
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "priv.h"
+#include "fuc/gf110.fuc4.h"
+
+struct nvkm_oclass *
+gf110_pmu_oclass = &(struct nvkm_pmu_impl) {
+ .base.handle = NV_SUBDEV(PMU, 0xd0),
+ .base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = _nvkm_pmu_ctor,
+ .dtor = _nvkm_pmu_dtor,
+ .init = _nvkm_pmu_init,
+ .fini = _nvkm_pmu_fini,
+ },
+ .code.data = gf110_pmu_code,
+ .code.size = sizeof(gf110_pmu_code),
+ .data.data = gf110_pmu_data,
+ .data.size = sizeof(gf110_pmu_data),
+}.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gk104.c b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gk104.c
new file mode 100644
index 000000000000..28fdb8ea9ed8
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gk104.c
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#define gf110_pmu_code gk104_pmu_code
+#define gf110_pmu_data gk104_pmu_data
+#include "priv.h"
+#include "fuc/gf110.fuc4.h"
+
+static void
+gk104_pmu_pgob(struct nvkm_pmu *pmu, bool enable)
+{
+ nv_mask(pmu, 0x000200, 0x00001000, 0x00000000);
+ nv_rd32(pmu, 0x000200);
+ nv_mask(pmu, 0x000200, 0x08000000, 0x08000000);
+ msleep(50);
+
+ nv_mask(pmu, 0x10a78c, 0x00000002, 0x00000002);
+ nv_mask(pmu, 0x10a78c, 0x00000001, 0x00000001);
+ nv_mask(pmu, 0x10a78c, 0x00000001, 0x00000000);
+
+ nv_mask(pmu, 0x020004, 0xc0000000, enable ? 0xc0000000 : 0x40000000);
+ msleep(50);
+
+ nv_mask(pmu, 0x10a78c, 0x00000002, 0x00000000);
+ nv_mask(pmu, 0x10a78c, 0x00000001, 0x00000001);
+ nv_mask(pmu, 0x10a78c, 0x00000001, 0x00000000);
+
+ nv_mask(pmu, 0x000200, 0x08000000, 0x00000000);
+ nv_mask(pmu, 0x000200, 0x00001000, 0x00001000);
+ nv_rd32(pmu, 0x000200);
+}
+
+struct nvkm_oclass *
+gk104_pmu_oclass = &(struct nvkm_pmu_impl) {
+ .base.handle = NV_SUBDEV(PMU, 0xe4),
+ .base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = _nvkm_pmu_ctor,
+ .dtor = _nvkm_pmu_dtor,
+ .init = _nvkm_pmu_init,
+ .fini = _nvkm_pmu_fini,
+ },
+ .code.data = gk104_pmu_code,
+ .code.size = sizeof(gk104_pmu_code),
+ .data.data = gk104_pmu_data,
+ .data.size = sizeof(gk104_pmu_data),
+ .pgob = gk104_pmu_pgob,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gk208.c b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gk208.c
new file mode 100644
index 000000000000..6f9c09af1a49
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gk208.c
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "priv.h"
+#include "fuc/gk208.fuc5.h"
+
+struct nvkm_oclass *
+gk208_pmu_oclass = &(struct nvkm_pmu_impl) {
+ .base.handle = NV_SUBDEV(PMU, 0x00),
+ .base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = _nvkm_pmu_ctor,
+ .dtor = _nvkm_pmu_dtor,
+ .init = _nvkm_pmu_init,
+ .fini = _nvkm_pmu_fini,
+ },
+ .code.data = gk208_pmu_code,
+ .code.size = sizeof(gk208_pmu_code),
+ .data.data = gk208_pmu_data,
+ .data.size = sizeof(gk208_pmu_data),
+}.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gk20a.c b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gk20a.c
new file mode 100644
index 000000000000..a49934bbe637
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gk20a.c
@@ -0,0 +1,229 @@
+/*
+ * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+#include "priv.h"
+
+#include <subdev/clk.h>
+#include <subdev/timer.h>
+#include <subdev/volt.h>
+
+#define BUSY_SLOT 0
+#define CLK_SLOT 7
+
+struct gk20a_pmu_dvfs_data {
+ int p_load_target;
+ int p_load_max;
+ int p_smooth;
+ unsigned int avg_load;
+};
+
+struct gk20a_pmu_priv {
+ struct nvkm_pmu base;
+ struct nvkm_alarm alarm;
+ struct gk20a_pmu_dvfs_data *data;
+};
+
+struct gk20a_pmu_dvfs_dev_status {
+ unsigned long total;
+ unsigned long busy;
+ int cur_state;
+};
+
+static int
+gk20a_pmu_dvfs_target(struct gk20a_pmu_priv *priv, int *state)
+{
+ struct nvkm_clk *clk = nvkm_clk(priv);
+
+ return nvkm_clk_astate(clk, *state, 0, false);
+}
+
+static int
+gk20a_pmu_dvfs_get_cur_state(struct gk20a_pmu_priv *priv, int *state)
+{
+ struct nvkm_clk *clk = nvkm_clk(priv);
+
+ *state = clk->pstate;
+ return 0;
+}
+
+static int
+gk20a_pmu_dvfs_get_target_state(struct gk20a_pmu_priv *priv,
+ int *state, int load)
+{
+ struct gk20a_pmu_dvfs_data *data = priv->data;
+ struct nvkm_clk *clk = nvkm_clk(priv);
+ int cur_level, level;
+
+ /* For GK20A, the performance level is directly mapped to pstate */
+ level = cur_level = clk->pstate;
+
+ if (load > data->p_load_max) {
+ level = min(clk->state_nr - 1, level + (clk->state_nr / 3));
+ } else {
+ level += ((load - data->p_load_target) * 10 /
+ data->p_load_target) / 2;
+ level = max(0, level);
+ level = min(clk->state_nr - 1, level);
+ }
+
+ nv_trace(priv, "cur level = %d, new level = %d\n", cur_level, level);
+
+ *state = level;
+
+ if (level == cur_level)
+ return 0;
+ else
+ return 1;
+}
+
+static int
+gk20a_pmu_dvfs_get_dev_status(struct gk20a_pmu_priv *priv,
+ struct gk20a_pmu_dvfs_dev_status *status)
+{
+ status->busy = nv_rd32(priv, 0x10a508 + (BUSY_SLOT * 0x10));
+ status->total= nv_rd32(priv, 0x10a508 + (CLK_SLOT * 0x10));
+ return 0;
+}
+
+static void
+gk20a_pmu_dvfs_reset_dev_status(struct gk20a_pmu_priv *priv)
+{
+ nv_wr32(priv, 0x10a508 + (BUSY_SLOT * 0x10), 0x80000000);
+ nv_wr32(priv, 0x10a508 + (CLK_SLOT * 0x10), 0x80000000);
+}
+
+static void
+gk20a_pmu_dvfs_work(struct nvkm_alarm *alarm)
+{
+ struct gk20a_pmu_priv *priv =
+ container_of(alarm, struct gk20a_pmu_priv, alarm);
+ struct gk20a_pmu_dvfs_data *data = priv->data;
+ struct gk20a_pmu_dvfs_dev_status status;
+ struct nvkm_clk *clk = nvkm_clk(priv);
+ struct nvkm_volt *volt = nvkm_volt(priv);
+ u32 utilization = 0;
+ int state, ret;
+
+ /*
+ * The PMU is initialized before CLK and VOLT, so we have to make sure the
+ * CLK and VOLT are ready here.
+ */
+ if (!clk || !volt)
+ goto resched;
+
+ ret = gk20a_pmu_dvfs_get_dev_status(priv, &status);
+ if (ret) {
+ nv_warn(priv, "failed to get device status\n");
+ goto resched;
+ }
+
+ if (status.total)
+ utilization = div_u64((u64)status.busy * 100, status.total);
+
+ data->avg_load = (data->p_smooth * data->avg_load) + utilization;
+ data->avg_load /= data->p_smooth + 1;
+ nv_trace(priv, "utilization = %d %%, avg_load = %d %%\n",
+ utilization, data->avg_load);
+
+ ret = gk20a_pmu_dvfs_get_cur_state(priv, &state);
+ if (ret) {
+ nv_warn(priv, "failed to get current state\n");
+ goto resched;
+ }
+
+ if (gk20a_pmu_dvfs_get_target_state(priv, &state, data->avg_load)) {
+ nv_trace(priv, "set new state to %d\n", state);
+ gk20a_pmu_dvfs_target(priv, &state);
+ }
+
+resched:
+ gk20a_pmu_dvfs_reset_dev_status(priv);
+ nvkm_timer_alarm(priv, 100000000, alarm);
+}
+
+int
+gk20a_pmu_fini(struct nvkm_object *object, bool suspend)
+{
+ struct nvkm_pmu *pmu = (void *)object;
+ struct gk20a_pmu_priv *priv = (void *)pmu;
+
+ nvkm_timer_alarm_cancel(priv, &priv->alarm);
+
+ return nvkm_subdev_fini(&pmu->base, suspend);
+}
+
+int
+gk20a_pmu_init(struct nvkm_object *object)
+{
+ struct nvkm_pmu *pmu = (void *)object;
+ struct gk20a_pmu_priv *priv = (void *)pmu;
+ int ret;
+
+ ret = nvkm_subdev_init(&pmu->base);
+ if (ret)
+ return ret;
+
+ pmu->pgob = nvkm_pmu_pgob;
+
+ /* init pwr perf counter */
+ nv_wr32(pmu, 0x10a504 + (BUSY_SLOT * 0x10), 0x00200001);
+ nv_wr32(pmu, 0x10a50c + (BUSY_SLOT * 0x10), 0x00000002);
+ nv_wr32(pmu, 0x10a50c + (CLK_SLOT * 0x10), 0x00000003);
+
+ nvkm_timer_alarm(pmu, 2000000000, &priv->alarm);
+ return ret;
+}
+
+struct gk20a_pmu_dvfs_data gk20a_dvfs_data= {
+ .p_load_target = 70,
+ .p_load_max = 90,
+ .p_smooth = 1,
+};
+
+static int
+gk20a_pmu_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 size,
+ struct nvkm_object **pobject)
+{
+ struct gk20a_pmu_priv *priv;
+ int ret;
+
+ ret = nvkm_pmu_create(parent, engine, oclass, &priv);
+ *pobject = nv_object(priv);
+ if (ret)
+ return ret;
+
+ priv->data = &gk20a_dvfs_data;
+
+ nvkm_alarm_init(&priv->alarm, gk20a_pmu_dvfs_work);
+ return 0;
+}
+
+struct nvkm_oclass *
+gk20a_pmu_oclass = &(struct nvkm_pmu_impl) {
+ .base.handle = NV_SUBDEV(PMU, 0xea),
+ .base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = gk20a_pmu_ctor,
+ .dtor = _nvkm_pmu_dtor,
+ .init = gk20a_pmu_init,
+ .fini = gk20a_pmu_fini,
+ },
+}.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gt215.c b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gt215.c
new file mode 100644
index 000000000000..30aaeb21de41
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gt215.c
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "priv.h"
+#include "fuc/gt215.fuc3.h"
+
+static int
+gt215_pmu_init(struct nvkm_object *object)
+{
+ struct nvkm_pmu *pmu = (void *)object;
+ nv_mask(pmu, 0x022210, 0x00000001, 0x00000000);
+ nv_mask(pmu, 0x022210, 0x00000001, 0x00000001);
+ return nvkm_pmu_init(pmu);
+}
+
+struct nvkm_oclass *
+gt215_pmu_oclass = &(struct nvkm_pmu_impl) {
+ .base.handle = NV_SUBDEV(PMU, 0xa3),
+ .base.ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = _nvkm_pmu_ctor,
+ .dtor = _nvkm_pmu_dtor,
+ .init = gt215_pmu_init,
+ .fini = _nvkm_pmu_fini,
+ },
+ .code.data = gt215_pmu_code,
+ .code.size = sizeof(gt215_pmu_code),
+ .data.data = gt215_pmu_data,
+ .data.size = sizeof(gt215_pmu_data),
+}.base;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/memx.c b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/memx.c
new file mode 100644
index 000000000000..b75c5b885980
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/memx.c
@@ -0,0 +1,200 @@
+#ifndef __NVKM_PMU_MEMX_H__
+#define __NVKM_PMU_MEMX_H__
+#include "priv.h"
+
+#include <core/device.h>
+
+struct nvkm_memx {
+ struct nvkm_pmu *pmu;
+ u32 base;
+ u32 size;
+ struct {
+ u32 mthd;
+ u32 size;
+ u32 data[64];
+ } c;
+};
+
+static void
+memx_out(struct nvkm_memx *memx)
+{
+ struct nvkm_pmu *pmu = memx->pmu;
+ int i;
+
+ if (memx->c.mthd) {
+ nv_wr32(pmu, 0x10a1c4, (memx->c.size << 16) | memx->c.mthd);
+ for (i = 0; i < memx->c.size; i++)
+ nv_wr32(pmu, 0x10a1c4, memx->c.data[i]);
+ memx->c.mthd = 0;
+ memx->c.size = 0;
+ }
+}
+
+static void
+memx_cmd(struct nvkm_memx *memx, u32 mthd, u32 size, u32 data[])
+{
+ if ((memx->c.size + size >= ARRAY_SIZE(memx->c.data)) ||
+ (memx->c.mthd && memx->c.mthd != mthd))
+ memx_out(memx);
+ memcpy(&memx->c.data[memx->c.size], data, size * sizeof(data[0]));
+ memx->c.size += size;
+ memx->c.mthd = mthd;
+}
+
+int
+nvkm_memx_init(struct nvkm_pmu *pmu, struct nvkm_memx **pmemx)
+{
+ struct nvkm_memx *memx;
+ u32 reply[2];
+ int ret;
+
+ ret = pmu->message(pmu, reply, PROC_MEMX, MEMX_MSG_INFO,
+ MEMX_INFO_DATA, 0);
+ if (ret)
+ return ret;
+
+ memx = *pmemx = kzalloc(sizeof(*memx), GFP_KERNEL);
+ if (!memx)
+ return -ENOMEM;
+ memx->pmu = pmu;
+ memx->base = reply[0];
+ memx->size = reply[1];
+
+ /* acquire data segment access */
+ do {
+ nv_wr32(pmu, 0x10a580, 0x00000003);
+ } while (nv_rd32(pmu, 0x10a580) != 0x00000003);
+ nv_wr32(pmu, 0x10a1c0, 0x01000000 | memx->base);
+ return 0;
+}
+
+int
+nvkm_memx_fini(struct nvkm_memx **pmemx, bool exec)
+{
+ struct nvkm_memx *memx = *pmemx;
+ struct nvkm_pmu *pmu = memx->pmu;
+ u32 finish, reply[2];
+
+ /* flush the cache... */
+ memx_out(memx);
+
+ /* release data segment access */
+ finish = nv_rd32(pmu, 0x10a1c0) & 0x00ffffff;
+ nv_wr32(pmu, 0x10a580, 0x00000000);
+
+ /* call MEMX process to execute the script, and wait for reply */
+ if (exec) {
+ pmu->message(pmu, reply, PROC_MEMX, MEMX_MSG_EXEC,
+ memx->base, finish);
+ }
+
+ nv_debug(memx->pmu, "Exec took %uns, PMU_IN %08x\n",
+ reply[0], reply[1]);
+ kfree(memx);
+ return 0;
+}
+
+void
+nvkm_memx_wr32(struct nvkm_memx *memx, u32 addr, u32 data)
+{
+ nv_debug(memx->pmu, "R[%06x] = 0x%08x\n", addr, data);
+ memx_cmd(memx, MEMX_WR32, 2, (u32[]){ addr, data });
+}
+
+void
+nvkm_memx_wait(struct nvkm_memx *memx,
+ u32 addr, u32 mask, u32 data, u32 nsec)
+{
+ nv_debug(memx->pmu, "R[%06x] & 0x%08x == 0x%08x, %d us\n",
+ addr, mask, data, nsec);
+ memx_cmd(memx, MEMX_WAIT, 4, (u32[]){ addr, mask, data, nsec });
+ memx_out(memx); /* fuc can't handle multiple */
+}
+
+void
+nvkm_memx_nsec(struct nvkm_memx *memx, u32 nsec)
+{
+ nv_debug(memx->pmu, " DELAY = %d ns\n", nsec);
+ memx_cmd(memx, MEMX_DELAY, 1, (u32[]){ nsec });
+ memx_out(memx); /* fuc can't handle multiple */
+}
+
+void
+nvkm_memx_wait_vblank(struct nvkm_memx *memx)
+{
+ struct nvkm_pmu *pmu = memx->pmu;
+ u32 heads, x, y, px = 0;
+ int i, head_sync;
+
+ if (nv_device(pmu)->chipset < 0xd0) {
+ heads = nv_rd32(pmu, 0x610050);
+ for (i = 0; i < 2; i++) {
+ /* Heuristic: sync to head with biggest resolution */
+ if (heads & (2 << (i << 3))) {
+ x = nv_rd32(pmu, 0x610b40 + (0x540 * i));
+ y = (x & 0xffff0000) >> 16;
+ x &= 0x0000ffff;
+ if ((x * y) > px) {
+ px = (x * y);
+ head_sync = i;
+ }
+ }
+ }
+ }
+
+ if (px == 0) {
+ nv_debug(memx->pmu, "WAIT VBLANK !NO ACTIVE HEAD\n");
+ return;
+ }
+
+ nv_debug(memx->pmu, "WAIT VBLANK HEAD%d\n", head_sync);
+ memx_cmd(memx, MEMX_VBLANK, 1, (u32[]){ head_sync });
+ memx_out(memx); /* fuc can't handle multiple */
+}
+
+void
+nvkm_memx_train(struct nvkm_memx *memx)
+{
+ nv_debug(memx->pmu, " MEM TRAIN\n");
+ memx_cmd(memx, MEMX_TRAIN, 0, NULL);
+}
+
+int
+nvkm_memx_train_result(struct nvkm_pmu *pmu, u32 *res, int rsize)
+{
+ u32 reply[2], base, size, i;
+ int ret;
+
+ ret = pmu->message(pmu, reply, PROC_MEMX, MEMX_MSG_INFO,
+ MEMX_INFO_TRAIN, 0);
+ if (ret)
+ return ret;
+
+ base = reply[0];
+ size = reply[1] >> 2;
+ if (size > rsize)
+ return -ENOMEM;
+
+ /* read the packet */
+ nv_wr32(pmu, 0x10a1c0, 0x02000000 | base);
+
+ for (i = 0; i < size; i++)
+ res[i] = nv_rd32(pmu, 0x10a1c4);
+
+ return 0;
+}
+
+void
+nvkm_memx_block(struct nvkm_memx *memx)
+{
+ nv_debug(memx->pmu, " HOST BLOCKED\n");
+ memx_cmd(memx, MEMX_ENTER, 0, NULL);
+}
+
+void
+nvkm_memx_unblock(struct nvkm_memx *memx)
+{
+ nv_debug(memx->pmu, " HOST UNBLOCKED\n");
+ memx_cmd(memx, MEMX_LEAVE, 0, NULL);
+}
+#endif
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/priv.h b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/priv.h
new file mode 100644
index 000000000000..998410563bfd
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/priv.h
@@ -0,0 +1,43 @@
+#ifndef __NVKM_PMU_PRIV_H__
+#define __NVKM_PMU_PRIV_H__
+#include <subdev/pmu.h>
+#include <subdev/pmu/fuc/os.h>
+
+#define nvkm_pmu_create(p, e, o, d) \
+ nvkm_pmu_create_((p), (e), (o), sizeof(**d), (void **)d)
+#define nvkm_pmu_destroy(p) \
+ nvkm_subdev_destroy(&(p)->base)
+#define nvkm_pmu_init(p) ({ \
+ struct nvkm_pmu *_pmu = (p); \
+ _nvkm_pmu_init(nv_object(_pmu)); \
+})
+#define nvkm_pmu_fini(p,s) ({ \
+ struct nvkm_pmu *_pmu = (p); \
+ _nvkm_pmu_fini(nv_object(_pmu), (s)); \
+})
+
+int nvkm_pmu_create_(struct nvkm_object *, struct nvkm_object *,
+ struct nvkm_oclass *, int, void **);
+
+int _nvkm_pmu_ctor(struct nvkm_object *, struct nvkm_object *,
+ struct nvkm_oclass *, void *, u32,
+ struct nvkm_object **);
+#define _nvkm_pmu_dtor _nvkm_subdev_dtor
+int _nvkm_pmu_init(struct nvkm_object *);
+int _nvkm_pmu_fini(struct nvkm_object *, bool);
+void nvkm_pmu_pgob(struct nvkm_pmu *pmu, bool enable);
+
+struct nvkm_pmu_impl {
+ struct nvkm_oclass base;
+ struct {
+ u32 *data;
+ u32 size;
+ } code;
+ struct {
+ u32 *data;
+ u32 size;
+ } data;
+
+ void (*pgob)(struct nvkm_pmu *, bool);
+};
+#endif
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/Kbuild b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/Kbuild
new file mode 100644
index 000000000000..5837cf1292d9
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/Kbuild
@@ -0,0 +1,13 @@
+nvkm-y += nvkm/subdev/therm/base.o
+nvkm-y += nvkm/subdev/therm/fan.o
+nvkm-y += nvkm/subdev/therm/fannil.o
+nvkm-y += nvkm/subdev/therm/fanpwm.o
+nvkm-y += nvkm/subdev/therm/fantog.o
+nvkm-y += nvkm/subdev/therm/ic.o
+nvkm-y += nvkm/subdev/therm/temp.o
+nvkm-y += nvkm/subdev/therm/nv40.o
+nvkm-y += nvkm/subdev/therm/nv50.o
+nvkm-y += nvkm/subdev/therm/g84.o
+nvkm-y += nvkm/subdev/therm/gt215.o
+nvkm-y += nvkm/subdev/therm/gf110.o
+nvkm-y += nvkm/subdev/therm/gm107.o
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/base.c
new file mode 100644
index 000000000000..ec327cb64a0d
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/base.c
@@ -0,0 +1,367 @@
+/*
+ * Copyright 2012 The Nouveau community
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Martin Peres
+ */
+#include "priv.h"
+
+#include <core/device.h>
+
+static int
+nvkm_therm_update_trip(struct nvkm_therm *therm)
+{
+ struct nvkm_therm_priv *priv = (void *)therm;
+ struct nvbios_therm_trip_point *trip = priv->fan->bios.trip,
+ *cur_trip = NULL,
+ *last_trip = priv->last_trip;
+ u8 temp = therm->temp_get(therm);
+ u16 duty, i;
+
+ /* look for the trip point corresponding to the current temperature */
+ cur_trip = NULL;
+ for (i = 0; i < priv->fan->bios.nr_fan_trip; i++) {
+ if (temp >= trip[i].temp)
+ cur_trip = &trip[i];
+ }
+
+ /* account for the hysteresis cycle */
+ if (last_trip && temp <= (last_trip->temp) &&
+ temp > (last_trip->temp - last_trip->hysteresis))
+ cur_trip = last_trip;
+
+ if (cur_trip) {
+ duty = cur_trip->fan_duty;
+ priv->last_trip = cur_trip;
+ } else {
+ duty = 0;
+ priv->last_trip = NULL;
+ }
+
+ return duty;
+}
+
+static int
+nvkm_therm_update_linear(struct nvkm_therm *therm)
+{
+ struct nvkm_therm_priv *priv = (void *)therm;
+ u8 linear_min_temp = priv->fan->bios.linear_min_temp;
+ u8 linear_max_temp = priv->fan->bios.linear_max_temp;
+ u8 temp = therm->temp_get(therm);
+ u16 duty;
+
+ /* handle the non-linear part first */
+ if (temp < linear_min_temp)
+ return priv->fan->bios.min_duty;
+ else if (temp > linear_max_temp)
+ return priv->fan->bios.max_duty;
+
+ /* we are in the linear zone */
+ duty = (temp - linear_min_temp);
+ duty *= (priv->fan->bios.max_duty - priv->fan->bios.min_duty);
+ duty /= (linear_max_temp - linear_min_temp);
+ duty += priv->fan->bios.min_duty;
+ return duty;
+}
+
+static void
+nvkm_therm_update(struct nvkm_therm *therm, int mode)
+{
+ struct nvkm_timer *ptimer = nvkm_timer(therm);
+ struct nvkm_therm_priv *priv = (void *)therm;
+ unsigned long flags;
+ bool immd = true;
+ bool poll = true;
+ int duty = -1;
+
+ spin_lock_irqsave(&priv->lock, flags);
+ if (mode < 0)
+ mode = priv->mode;
+ priv->mode = mode;
+
+ switch (mode) {
+ case NVKM_THERM_CTRL_MANUAL:
+ ptimer->alarm_cancel(ptimer, &priv->alarm);
+ duty = nvkm_therm_fan_get(therm);
+ if (duty < 0)
+ duty = 100;
+ poll = false;
+ break;
+ case NVKM_THERM_CTRL_AUTO:
+ switch(priv->fan->bios.fan_mode) {
+ case NVBIOS_THERM_FAN_TRIP:
+ duty = nvkm_therm_update_trip(therm);
+ break;
+ case NVBIOS_THERM_FAN_LINEAR:
+ duty = nvkm_therm_update_linear(therm);
+ break;
+ case NVBIOS_THERM_FAN_OTHER:
+ if (priv->cstate)
+ duty = priv->cstate;
+ poll = false;
+ break;
+ }
+ immd = false;
+ break;
+ case NVKM_THERM_CTRL_NONE:
+ default:
+ ptimer->alarm_cancel(ptimer, &priv->alarm);
+ poll = false;
+ }
+
+ if (list_empty(&priv->alarm.head) && poll)
+ ptimer->alarm(ptimer, 1000000000ULL, &priv->alarm);
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ if (duty >= 0) {
+ nv_debug(therm, "FAN target request: %d%%\n", duty);
+ nvkm_therm_fan_set(therm, immd, duty);
+ }
+}
+
+int
+nvkm_therm_cstate(struct nvkm_therm *ptherm, int fan, int dir)
+{
+ struct nvkm_therm_priv *priv = (void *)ptherm;
+ if (!dir || (dir < 0 && fan < priv->cstate) ||
+ (dir > 0 && fan > priv->cstate)) {
+ nv_debug(ptherm, "default fan speed -> %d%%\n", fan);
+ priv->cstate = fan;
+ nvkm_therm_update(ptherm, -1);
+ }
+ return 0;
+}
+
+static void
+nvkm_therm_alarm(struct nvkm_alarm *alarm)
+{
+ struct nvkm_therm_priv *priv =
+ container_of(alarm, struct nvkm_therm_priv, alarm);
+ nvkm_therm_update(&priv->base, -1);
+}
+
+int
+nvkm_therm_fan_mode(struct nvkm_therm *therm, int mode)
+{
+ struct nvkm_therm_priv *priv = (void *)therm;
+ struct nvkm_device *device = nv_device(therm);
+ static const char *name[] = {
+ "disabled",
+ "manual",
+ "automatic"
+ };
+
+ /* The default PPWR ucode on fermi interferes with fan management */
+ if ((mode >= ARRAY_SIZE(name)) ||
+ (mode != NVKM_THERM_CTRL_NONE && device->card_type >= NV_C0 &&
+ !nvkm_subdev(device, NVDEV_SUBDEV_PMU)))
+ return -EINVAL;
+
+ /* do not allow automatic fan management if the thermal sensor is
+ * not available */
+ if (mode == NVKM_THERM_CTRL_AUTO && therm->temp_get(therm) < 0)
+ return -EINVAL;
+
+ if (priv->mode == mode)
+ return 0;
+
+ nv_info(therm, "fan management: %s\n", name[mode]);
+ nvkm_therm_update(therm, mode);
+ return 0;
+}
+
+int
+nvkm_therm_attr_get(struct nvkm_therm *therm,
+ enum nvkm_therm_attr_type type)
+{
+ struct nvkm_therm_priv *priv = (void *)therm;
+
+ switch (type) {
+ case NVKM_THERM_ATTR_FAN_MIN_DUTY:
+ return priv->fan->bios.min_duty;
+ case NVKM_THERM_ATTR_FAN_MAX_DUTY:
+ return priv->fan->bios.max_duty;
+ case NVKM_THERM_ATTR_FAN_MODE:
+ return priv->mode;
+ case NVKM_THERM_ATTR_THRS_FAN_BOOST:
+ return priv->bios_sensor.thrs_fan_boost.temp;
+ case NVKM_THERM_ATTR_THRS_FAN_BOOST_HYST:
+ return priv->bios_sensor.thrs_fan_boost.hysteresis;
+ case NVKM_THERM_ATTR_THRS_DOWN_CLK:
+ return priv->bios_sensor.thrs_down_clock.temp;
+ case NVKM_THERM_ATTR_THRS_DOWN_CLK_HYST:
+ return priv->bios_sensor.thrs_down_clock.hysteresis;
+ case NVKM_THERM_ATTR_THRS_CRITICAL:
+ return priv->bios_sensor.thrs_critical.temp;
+ case NVKM_THERM_ATTR_THRS_CRITICAL_HYST:
+ return priv->bios_sensor.thrs_critical.hysteresis;
+ case NVKM_THERM_ATTR_THRS_SHUTDOWN:
+ return priv->bios_sensor.thrs_shutdown.temp;
+ case NVKM_THERM_ATTR_THRS_SHUTDOWN_HYST:
+ return priv->bios_sensor.thrs_shutdown.hysteresis;
+ }
+
+ return -EINVAL;
+}
+
+int
+nvkm_therm_attr_set(struct nvkm_therm *therm,
+ enum nvkm_therm_attr_type type, int value)
+{
+ struct nvkm_therm_priv *priv = (void *)therm;
+
+ switch (type) {
+ case NVKM_THERM_ATTR_FAN_MIN_DUTY:
+ if (value < 0)
+ value = 0;
+ if (value > priv->fan->bios.max_duty)
+ value = priv->fan->bios.max_duty;
+ priv->fan->bios.min_duty = value;
+ return 0;
+ case NVKM_THERM_ATTR_FAN_MAX_DUTY:
+ if (value < 0)
+ value = 0;
+ if (value < priv->fan->bios.min_duty)
+ value = priv->fan->bios.min_duty;
+ priv->fan->bios.max_duty = value;
+ return 0;
+ case NVKM_THERM_ATTR_FAN_MODE:
+ return nvkm_therm_fan_mode(therm, value);
+ case NVKM_THERM_ATTR_THRS_FAN_BOOST:
+ priv->bios_sensor.thrs_fan_boost.temp = value;
+ priv->sensor.program_alarms(therm);
+ return 0;
+ case NVKM_THERM_ATTR_THRS_FAN_BOOST_HYST:
+ priv->bios_sensor.thrs_fan_boost.hysteresis = value;
+ priv->sensor.program_alarms(therm);
+ return 0;
+ case NVKM_THERM_ATTR_THRS_DOWN_CLK:
+ priv->bios_sensor.thrs_down_clock.temp = value;
+ priv->sensor.program_alarms(therm);
+ return 0;
+ case NVKM_THERM_ATTR_THRS_DOWN_CLK_HYST:
+ priv->bios_sensor.thrs_down_clock.hysteresis = value;
+ priv->sensor.program_alarms(therm);
+ return 0;
+ case NVKM_THERM_ATTR_THRS_CRITICAL:
+ priv->bios_sensor.thrs_critical.temp = value;
+ priv->sensor.program_alarms(therm);
+ return 0;
+ case NVKM_THERM_ATTR_THRS_CRITICAL_HYST:
+ priv->bios_sensor.thrs_critical.hysteresis = value;
+ priv->sensor.program_alarms(therm);
+ return 0;
+ case NVKM_THERM_ATTR_THRS_SHUTDOWN:
+ priv->bios_sensor.thrs_shutdown.temp = value;
+ priv->sensor.program_alarms(therm);
+ return 0;
+ case NVKM_THERM_ATTR_THRS_SHUTDOWN_HYST:
+ priv->bios_sensor.thrs_shutdown.hysteresis = value;
+ priv->sensor.program_alarms(therm);
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+int
+_nvkm_therm_init(struct nvkm_object *object)
+{
+ struct nvkm_therm *therm = (void *)object;
+ struct nvkm_therm_priv *priv = (void *)therm;
+ int ret;
+
+ ret = nvkm_subdev_init(&therm->base);
+ if (ret)
+ return ret;
+
+ if (priv->suspend >= 0) {
+ /* restore the pwm value only when on manual or auto mode */
+ if (priv->suspend > 0)
+ nvkm_therm_fan_set(therm, true, priv->fan->percent);
+
+ nvkm_therm_fan_mode(therm, priv->suspend);
+ }
+ nvkm_therm_sensor_init(therm);
+ nvkm_therm_fan_init(therm);
+ return 0;
+}
+
+int
+_nvkm_therm_fini(struct nvkm_object *object, bool suspend)
+{
+ struct nvkm_therm *therm = (void *)object;
+ struct nvkm_therm_priv *priv = (void *)therm;
+
+ nvkm_therm_fan_fini(therm, suspend);
+ nvkm_therm_sensor_fini(therm, suspend);
+ if (suspend) {
+ priv->suspend = priv->mode;
+ priv->mode = NVKM_THERM_CTRL_NONE;
+ }
+
+ return nvkm_subdev_fini(&therm->base, suspend);
+}
+
+int
+nvkm_therm_create_(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, int length, void **pobject)
+{
+ struct nvkm_therm_priv *priv;
+ int ret;
+
+ ret = nvkm_subdev_create_(parent, engine, oclass, 0, "PTHERM",
+ "therm", length, pobject);
+ priv = *pobject;
+ if (ret)
+ return ret;
+
+ nvkm_alarm_init(&priv->alarm, nvkm_therm_alarm);
+ spin_lock_init(&priv->lock);
+ spin_lock_init(&priv->sensor.alarm_program_lock);
+
+ priv->base.fan_get = nvkm_therm_fan_user_get;
+ priv->base.fan_set = nvkm_therm_fan_user_set;
+ priv->base.fan_sense = nvkm_therm_fan_sense;
+ priv->base.attr_get = nvkm_therm_attr_get;
+ priv->base.attr_set = nvkm_therm_attr_set;
+ priv->mode = priv->suspend = -1; /* undefined */
+ return 0;
+}
+
+int
+nvkm_therm_preinit(struct nvkm_therm *therm)
+{
+ nvkm_therm_sensor_ctor(therm);
+ nvkm_therm_ic_ctor(therm);
+ nvkm_therm_fan_ctor(therm);
+
+ nvkm_therm_fan_mode(therm, NVKM_THERM_CTRL_AUTO);
+ nvkm_therm_sensor_preinit(therm);
+ return 0;
+}
+
+void
+_nvkm_therm_dtor(struct nvkm_object *object)
+{
+ struct nvkm_therm_priv *priv = (void *)object;
+ kfree(priv->fan);
+ nvkm_subdev_destroy(&priv->base.base);
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fan.c b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fan.c
new file mode 100644
index 000000000000..434fa745ca40
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fan.c
@@ -0,0 +1,282 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ * Martin Peres
+ */
+#include "priv.h"
+
+#include <subdev/bios/fan.h>
+#include <subdev/gpio.h>
+#include <subdev/timer.h>
+
+static int
+nvkm_fan_update(struct nvkm_fan *fan, bool immediate, int target)
+{
+ struct nvkm_therm *therm = fan->parent;
+ struct nvkm_therm_priv *priv = (void *)therm;
+ struct nvkm_timer *ptimer = nvkm_timer(priv);
+ unsigned long flags;
+ int ret = 0;
+ int duty;
+
+ /* update target fan speed, restricting to allowed range */
+ spin_lock_irqsave(&fan->lock, flags);
+ if (target < 0)
+ target = fan->percent;
+ target = max_t(u8, target, fan->bios.min_duty);
+ target = min_t(u8, target, fan->bios.max_duty);
+ if (fan->percent != target) {
+ nv_debug(therm, "FAN target: %d\n", target);
+ fan->percent = target;
+ }
+
+ /* check that we're not already at the target duty cycle */
+ duty = fan->get(therm);
+ if (duty == target) {
+ spin_unlock_irqrestore(&fan->lock, flags);
+ return 0;
+ }
+
+ /* smooth out the fanspeed increase/decrease */
+ if (!immediate && duty >= 0) {
+ /* the constant "3" is a rough approximation taken from
+ * nvidia's behaviour.
+ * it is meant to bump the fan speed more incrementally
+ */
+ if (duty < target)
+ duty = min(duty + 3, target);
+ else if (duty > target)
+ duty = max(duty - 3, target);
+ } else {
+ duty = target;
+ }
+
+ nv_debug(therm, "FAN update: %d\n", duty);
+ ret = fan->set(therm, duty);
+ if (ret) {
+ spin_unlock_irqrestore(&fan->lock, flags);
+ return ret;
+ }
+
+ /* fan speed updated, drop the fan lock before grabbing the
+ * alarm-scheduling lock and risking a deadlock
+ */
+ spin_unlock_irqrestore(&fan->lock, flags);
+
+ /* schedule next fan update, if not at target speed already */
+ if (list_empty(&fan->alarm.head) && target != duty) {
+ u16 bump_period = fan->bios.bump_period;
+ u16 slow_down_period = fan->bios.slow_down_period;
+ u64 delay;
+
+ if (duty > target)
+ delay = slow_down_period;
+ else if (duty == target)
+ delay = min(bump_period, slow_down_period) ;
+ else
+ delay = bump_period;
+
+ ptimer->alarm(ptimer, delay * 1000 * 1000, &fan->alarm);
+ }
+
+ return ret;
+}
+
+static void
+nvkm_fan_alarm(struct nvkm_alarm *alarm)
+{
+ struct nvkm_fan *fan = container_of(alarm, struct nvkm_fan, alarm);
+ nvkm_fan_update(fan, false, -1);
+}
+
+int
+nvkm_therm_fan_get(struct nvkm_therm *therm)
+{
+ struct nvkm_therm_priv *priv = (void *)therm;
+ return priv->fan->get(therm);
+}
+
+int
+nvkm_therm_fan_set(struct nvkm_therm *therm, bool immediate, int percent)
+{
+ struct nvkm_therm_priv *priv = (void *)therm;
+ return nvkm_fan_update(priv->fan, immediate, percent);
+}
+
+int
+nvkm_therm_fan_sense(struct nvkm_therm *therm)
+{
+ struct nvkm_therm_priv *priv = (void *)therm;
+ struct nvkm_timer *ptimer = nvkm_timer(therm);
+ struct nvkm_gpio *gpio = nvkm_gpio(therm);
+ u32 cycles, cur, prev;
+ u64 start, end, tach;
+
+ if (priv->fan->tach.func == DCB_GPIO_UNUSED)
+ return -ENODEV;
+
+ /* Time a complete rotation and extrapolate to RPM:
+ * When the fan spins, it changes the value of GPIO FAN_SENSE.
+ * We get 4 changes (0 -> 1 -> 0 -> 1) per complete rotation.
+ */
+ start = ptimer->read(ptimer);
+ prev = gpio->get(gpio, 0, priv->fan->tach.func, priv->fan->tach.line);
+ cycles = 0;
+ do {
+ usleep_range(500, 1000); /* supports 0 < rpm < 7500 */
+
+ cur = gpio->get(gpio, 0, priv->fan->tach.func, priv->fan->tach.line);
+ if (prev != cur) {
+ if (!start)
+ start = ptimer->read(ptimer);
+ cycles++;
+ prev = cur;
+ }
+ } while (cycles < 5 && ptimer->read(ptimer) - start < 250000000);
+ end = ptimer->read(ptimer);
+
+ if (cycles == 5) {
+ tach = (u64)60000000000ULL;
+ do_div(tach, (end - start));
+ return tach;
+ } else
+ return 0;
+}
+
+int
+nvkm_therm_fan_user_get(struct nvkm_therm *therm)
+{
+ return nvkm_therm_fan_get(therm);
+}
+
+int
+nvkm_therm_fan_user_set(struct nvkm_therm *therm, int percent)
+{
+ struct nvkm_therm_priv *priv = (void *)therm;
+
+ if (priv->mode != NVKM_THERM_CTRL_MANUAL)
+ return -EINVAL;
+
+ return nvkm_therm_fan_set(therm, true, percent);
+}
+
+static void
+nvkm_therm_fan_set_defaults(struct nvkm_therm *therm)
+{
+ struct nvkm_therm_priv *priv = (void *)therm;
+
+ priv->fan->bios.pwm_freq = 0;
+ priv->fan->bios.min_duty = 0;
+ priv->fan->bios.max_duty = 100;
+ priv->fan->bios.bump_period = 500;
+ priv->fan->bios.slow_down_period = 2000;
+ priv->fan->bios.linear_min_temp = 40;
+ priv->fan->bios.linear_max_temp = 85;
+}
+
+static void
+nvkm_therm_fan_safety_checks(struct nvkm_therm *therm)
+{
+ struct nvkm_therm_priv *priv = (void *)therm;
+
+ if (priv->fan->bios.min_duty > 100)
+ priv->fan->bios.min_duty = 100;
+ if (priv->fan->bios.max_duty > 100)
+ priv->fan->bios.max_duty = 100;
+
+ if (priv->fan->bios.min_duty > priv->fan->bios.max_duty)
+ priv->fan->bios.min_duty = priv->fan->bios.max_duty;
+}
+
+int
+nvkm_therm_fan_init(struct nvkm_therm *therm)
+{
+ return 0;
+}
+
+int
+nvkm_therm_fan_fini(struct nvkm_therm *therm, bool suspend)
+{
+ struct nvkm_therm_priv *priv = (void *)therm;
+ struct nvkm_timer *ptimer = nvkm_timer(therm);
+
+ if (suspend)
+ ptimer->alarm_cancel(ptimer, &priv->fan->alarm);
+ return 0;
+}
+
+int
+nvkm_therm_fan_ctor(struct nvkm_therm *therm)
+{
+ struct nvkm_therm_priv *priv = (void *)therm;
+ struct nvkm_gpio *gpio = nvkm_gpio(therm);
+ struct nvkm_bios *bios = nvkm_bios(therm);
+ struct dcb_gpio_func func;
+ int ret;
+
+ /* attempt to locate a drivable fan, and determine control method */
+ ret = gpio->find(gpio, 0, DCB_GPIO_FAN, 0xff, &func);
+ if (ret == 0) {
+ /* FIXME: is this really the place to perform such checks ? */
+ if (func.line != 16 && func.log[0] & DCB_GPIO_LOG_DIR_IN) {
+ nv_debug(therm, "GPIO_FAN is in input mode\n");
+ ret = -EINVAL;
+ } else {
+ ret = nvkm_fanpwm_create(therm, &func);
+ if (ret != 0)
+ ret = nvkm_fantog_create(therm, &func);
+ }
+ }
+
+ /* no controllable fan found, create a dummy fan module */
+ if (ret != 0) {
+ ret = nvkm_fannil_create(therm);
+ if (ret)
+ return ret;
+ }
+
+ nv_info(therm, "FAN control: %s\n", priv->fan->type);
+
+ /* read the current speed, it is useful when resuming */
+ priv->fan->percent = nvkm_therm_fan_get(therm);
+
+ /* attempt to detect a tachometer connection */
+ ret = gpio->find(gpio, 0, DCB_GPIO_FAN_SENSE, 0xff, &priv->fan->tach);
+ if (ret)
+ priv->fan->tach.func = DCB_GPIO_UNUSED;
+
+ /* initialise fan bump/slow update handling */
+ priv->fan->parent = therm;
+ nvkm_alarm_init(&priv->fan->alarm, nvkm_fan_alarm);
+ spin_lock_init(&priv->fan->lock);
+
+ /* other random init... */
+ nvkm_therm_fan_set_defaults(therm);
+ nvbios_perf_fan_parse(bios, &priv->fan->perf);
+ if (!nvbios_fan_parse(bios, &priv->fan->bios)) {
+ nv_debug(therm, "parsing the fan table failed\n");
+ if (nvbios_therm_fan_parse(bios, &priv->fan->bios))
+ nv_error(therm, "parsing both fan tables failed\n");
+ }
+ nvkm_therm_fan_safety_checks(therm);
+ return 0;
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fannil.c b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fannil.c
new file mode 100644
index 000000000000..534e5970ec9c
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fannil.c
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "priv.h"
+
+static int
+nvkm_fannil_get(struct nvkm_therm *therm)
+{
+ return -ENODEV;
+}
+
+static int
+nvkm_fannil_set(struct nvkm_therm *therm, int percent)
+{
+ return -ENODEV;
+}
+
+int
+nvkm_fannil_create(struct nvkm_therm *therm)
+{
+ struct nvkm_therm_priv *tpriv = (void *)therm;
+ struct nvkm_fan *priv;
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ tpriv->fan = priv;
+ if (!priv)
+ return -ENOMEM;
+
+ priv->type = "none / external";
+ priv->get = nvkm_fannil_get;
+ priv->set = nvkm_fannil_set;
+ return 0;
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fanpwm.c b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fanpwm.c
new file mode 100644
index 000000000000..bde5ceaeb70a
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fanpwm.c
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ * Martin Peres
+ */
+#include "priv.h"
+
+#include <core/device.h>
+#include <core/option.h>
+#include <subdev/bios.h>
+#include <subdev/bios/fan.h>
+#include <subdev/gpio.h>
+
+struct nvkm_fanpwm_priv {
+ struct nvkm_fan base;
+ struct dcb_gpio_func func;
+};
+
+static int
+nvkm_fanpwm_get(struct nvkm_therm *therm)
+{
+ struct nvkm_therm_priv *tpriv = (void *)therm;
+ struct nvkm_fanpwm_priv *priv = (void *)tpriv->fan;
+ struct nvkm_gpio *gpio = nvkm_gpio(therm);
+ int card_type = nv_device(therm)->card_type;
+ u32 divs, duty;
+ int ret;
+
+ ret = therm->pwm_get(therm, priv->func.line, &divs, &duty);
+ if (ret == 0 && divs) {
+ divs = max(divs, duty);
+ if (card_type <= NV_40 || (priv->func.log[0] & 1))
+ duty = divs - duty;
+ return (duty * 100) / divs;
+ }
+
+ return gpio->get(gpio, 0, priv->func.func, priv->func.line) * 100;
+}
+
+static int
+nvkm_fanpwm_set(struct nvkm_therm *therm, int percent)
+{
+ struct nvkm_therm_priv *tpriv = (void *)therm;
+ struct nvkm_fanpwm_priv *priv = (void *)tpriv->fan;
+ int card_type = nv_device(therm)->card_type;
+ u32 divs, duty;
+ int ret;
+
+ divs = priv->base.perf.pwm_divisor;
+ if (priv->base.bios.pwm_freq) {
+ divs = 1;
+ if (therm->pwm_clock)
+ divs = therm->pwm_clock(therm, priv->func.line);
+ divs /= priv->base.bios.pwm_freq;
+ }
+
+ duty = ((divs * percent) + 99) / 100;
+ if (card_type <= NV_40 || (priv->func.log[0] & 1))
+ duty = divs - duty;
+
+ ret = therm->pwm_set(therm, priv->func.line, divs, duty);
+ if (ret == 0)
+ ret = therm->pwm_ctrl(therm, priv->func.line, true);
+ return ret;
+}
+
+int
+nvkm_fanpwm_create(struct nvkm_therm *therm, struct dcb_gpio_func *func)
+{
+ struct nvkm_device *device = nv_device(therm);
+ struct nvkm_therm_priv *tpriv = (void *)therm;
+ struct nvkm_bios *bios = nvkm_bios(therm);
+ struct nvkm_fanpwm_priv *priv;
+ struct nvbios_therm_fan fan;
+ u32 divs, duty;
+
+ nvbios_fan_parse(bios, &fan);
+
+ if (!nvkm_boolopt(device->cfgopt, "NvFanPWM", func->param) ||
+ !therm->pwm_ctrl || fan.type == NVBIOS_THERM_FAN_TOGGLE ||
+ therm->pwm_get(therm, func->line, &divs, &duty) == -ENODEV)
+ return -ENODEV;
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ tpriv->fan = &priv->base;
+ if (!priv)
+ return -ENOMEM;
+
+ priv->base.type = "PWM";
+ priv->base.get = nvkm_fanpwm_get;
+ priv->base.set = nvkm_fanpwm_set;
+ priv->func = *func;
+ return 0;
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fantog.c b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fantog.c
new file mode 100644
index 000000000000..4ce041e81371
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fantog.c
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2012 The Nouveau community
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Martin Peres
+ */
+#include "priv.h"
+
+#include <subdev/gpio.h>
+#include <subdev/timer.h>
+
+struct nvkm_fantog_priv {
+ struct nvkm_fan base;
+ struct nvkm_alarm alarm;
+ spinlock_t lock;
+ u32 period_us;
+ u32 percent;
+ struct dcb_gpio_func func;
+};
+
+static void
+nvkm_fantog_update(struct nvkm_fantog_priv *priv, int percent)
+{
+ struct nvkm_therm_priv *tpriv = (void *)priv->base.parent;
+ struct nvkm_timer *ptimer = nvkm_timer(tpriv);
+ struct nvkm_gpio *gpio = nvkm_gpio(tpriv);
+ unsigned long flags;
+ int duty;
+
+ spin_lock_irqsave(&priv->lock, flags);
+ if (percent < 0)
+ percent = priv->percent;
+ priv->percent = percent;
+
+ duty = !gpio->get(gpio, 0, DCB_GPIO_FAN, 0xff);
+ gpio->set(gpio, 0, DCB_GPIO_FAN, 0xff, duty);
+
+ if (list_empty(&priv->alarm.head) && percent != (duty * 100)) {
+ u64 next_change = (percent * priv->period_us) / 100;
+ if (!duty)
+ next_change = priv->period_us - next_change;
+ ptimer->alarm(ptimer, next_change * 1000, &priv->alarm);
+ }
+ spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+static void
+nvkm_fantog_alarm(struct nvkm_alarm *alarm)
+{
+ struct nvkm_fantog_priv *priv =
+ container_of(alarm, struct nvkm_fantog_priv, alarm);
+ nvkm_fantog_update(priv, -1);
+}
+
+static int
+nvkm_fantog_get(struct nvkm_therm *therm)
+{
+ struct nvkm_therm_priv *tpriv = (void *)therm;
+ struct nvkm_fantog_priv *priv = (void *)tpriv->fan;
+ return priv->percent;
+}
+
+static int
+nvkm_fantog_set(struct nvkm_therm *therm, int percent)
+{
+ struct nvkm_therm_priv *tpriv = (void *)therm;
+ struct nvkm_fantog_priv *priv = (void *)tpriv->fan;
+ if (therm->pwm_ctrl)
+ therm->pwm_ctrl(therm, priv->func.line, false);
+ nvkm_fantog_update(priv, percent);
+ return 0;
+}
+
+int
+nvkm_fantog_create(struct nvkm_therm *therm, struct dcb_gpio_func *func)
+{
+ struct nvkm_therm_priv *tpriv = (void *)therm;
+ struct nvkm_fantog_priv *priv;
+ int ret;
+
+ if (therm->pwm_ctrl) {
+ ret = therm->pwm_ctrl(therm, func->line, false);
+ if (ret)
+ return ret;
+ }
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ tpriv->fan = &priv->base;
+ if (!priv)
+ return -ENOMEM;
+
+ priv->base.type = "toggle";
+ priv->base.get = nvkm_fantog_get;
+ priv->base.set = nvkm_fantog_set;
+ nvkm_alarm_init(&priv->alarm, nvkm_fantog_alarm);
+ priv->period_us = 100000; /* 10Hz */
+ priv->percent = 100;
+ priv->func = *func;
+ spin_lock_init(&priv->lock);
+ return 0;
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/g84.c b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/g84.c
new file mode 100644
index 000000000000..85b5d0c18c0b
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/g84.c
@@ -0,0 +1,266 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ * Martin Peres
+ */
+#include "priv.h"
+
+#include <subdev/fuse.h>
+
+struct g84_therm_priv {
+ struct nvkm_therm_priv base;
+};
+
+int
+g84_temp_get(struct nvkm_therm *therm)
+{
+ struct nvkm_fuse *fuse = nvkm_fuse(therm);
+
+ if (nv_ro32(fuse, 0x1a8) == 1)
+ return nv_rd32(therm, 0x20400);
+ else
+ return -ENODEV;
+}
+
+void
+g84_sensor_setup(struct nvkm_therm *therm)
+{
+ struct nvkm_fuse *fuse = nvkm_fuse(therm);
+
+ /* enable temperature reading for cards with insane defaults */
+ if (nv_ro32(fuse, 0x1a8) == 1) {
+ nv_mask(therm, 0x20008, 0x80008000, 0x80000000);
+ nv_mask(therm, 0x2000c, 0x80000003, 0x00000000);
+ mdelay(20); /* wait for the temperature to stabilize */
+ }
+}
+
+static void
+g84_therm_program_alarms(struct nvkm_therm *therm)
+{
+ struct nvkm_therm_priv *priv = (void *)therm;
+ struct nvbios_therm_sensor *sensor = &priv->bios_sensor;
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->sensor.alarm_program_lock, flags);
+
+ /* enable RISING and FALLING IRQs for shutdown, THRS 0, 1, 2 and 4 */
+ nv_wr32(therm, 0x20000, 0x000003ff);
+
+ /* shutdown: The computer should be shutdown when reached */
+ nv_wr32(therm, 0x20484, sensor->thrs_shutdown.hysteresis);
+ nv_wr32(therm, 0x20480, sensor->thrs_shutdown.temp);
+
+ /* THRS_1 : fan boost*/
+ nv_wr32(therm, 0x204c4, sensor->thrs_fan_boost.temp);
+
+ /* THRS_2 : critical */
+ nv_wr32(therm, 0x204c0, sensor->thrs_critical.temp);
+
+ /* THRS_4 : down clock */
+ nv_wr32(therm, 0x20414, sensor->thrs_down_clock.temp);
+ spin_unlock_irqrestore(&priv->sensor.alarm_program_lock, flags);
+
+ nv_debug(therm,
+ "Programmed thresholds [ %d(%d), %d(%d), %d(%d), %d(%d) ]\n",
+ sensor->thrs_fan_boost.temp, sensor->thrs_fan_boost.hysteresis,
+ sensor->thrs_down_clock.temp,
+ sensor->thrs_down_clock.hysteresis,
+ sensor->thrs_critical.temp, sensor->thrs_critical.hysteresis,
+ sensor->thrs_shutdown.temp, sensor->thrs_shutdown.hysteresis);
+
+}
+
+/* must be called with alarm_program_lock taken ! */
+static void
+g84_therm_threshold_hyst_emulation(struct nvkm_therm *therm,
+ uint32_t thrs_reg, u8 status_bit,
+ const struct nvbios_therm_threshold *thrs,
+ enum nvkm_therm_thrs thrs_name)
+{
+ enum nvkm_therm_thrs_direction direction;
+ enum nvkm_therm_thrs_state prev_state, new_state;
+ int temp, cur;
+
+ prev_state = nvkm_therm_sensor_get_threshold_state(therm, thrs_name);
+ temp = nv_rd32(therm, thrs_reg);
+
+ /* program the next threshold */
+ if (temp == thrs->temp) {
+ nv_wr32(therm, thrs_reg, thrs->temp - thrs->hysteresis);
+ new_state = NVKM_THERM_THRS_HIGHER;
+ } else {
+ nv_wr32(therm, thrs_reg, thrs->temp);
+ new_state = NVKM_THERM_THRS_LOWER;
+ }
+
+ /* fix the state (in case someone reprogrammed the alarms) */
+ cur = therm->temp_get(therm);
+ if (new_state == NVKM_THERM_THRS_LOWER && cur > thrs->temp)
+ new_state = NVKM_THERM_THRS_HIGHER;
+ else if (new_state == NVKM_THERM_THRS_HIGHER &&
+ cur < thrs->temp - thrs->hysteresis)
+ new_state = NVKM_THERM_THRS_LOWER;
+ nvkm_therm_sensor_set_threshold_state(therm, thrs_name, new_state);
+
+ /* find the direction */
+ if (prev_state < new_state)
+ direction = NVKM_THERM_THRS_RISING;
+ else if (prev_state > new_state)
+ direction = NVKM_THERM_THRS_FALLING;
+ else
+ return;
+
+ /* advertise a change in direction */
+ nvkm_therm_sensor_event(therm, thrs_name, direction);
+}
+
+static void
+g84_therm_intr(struct nvkm_subdev *subdev)
+{
+ struct nvkm_therm *therm = nvkm_therm(subdev);
+ struct nvkm_therm_priv *priv = (void *)therm;
+ struct nvbios_therm_sensor *sensor = &priv->bios_sensor;
+ unsigned long flags;
+ uint32_t intr;
+
+ spin_lock_irqsave(&priv->sensor.alarm_program_lock, flags);
+
+ intr = nv_rd32(therm, 0x20100) & 0x3ff;
+
+ /* THRS_4: downclock */
+ if (intr & 0x002) {
+ g84_therm_threshold_hyst_emulation(therm, 0x20414, 24,
+ &sensor->thrs_down_clock,
+ NVKM_THERM_THRS_DOWNCLOCK);
+ intr &= ~0x002;
+ }
+
+ /* shutdown */
+ if (intr & 0x004) {
+ g84_therm_threshold_hyst_emulation(therm, 0x20480, 20,
+ &sensor->thrs_shutdown,
+ NVKM_THERM_THRS_SHUTDOWN);
+ intr &= ~0x004;
+ }
+
+ /* THRS_1 : fan boost */
+ if (intr & 0x008) {
+ g84_therm_threshold_hyst_emulation(therm, 0x204c4, 21,
+ &sensor->thrs_fan_boost,
+ NVKM_THERM_THRS_FANBOOST);
+ intr &= ~0x008;
+ }
+
+ /* THRS_2 : critical */
+ if (intr & 0x010) {
+ g84_therm_threshold_hyst_emulation(therm, 0x204c0, 22,
+ &sensor->thrs_critical,
+ NVKM_THERM_THRS_CRITICAL);
+ intr &= ~0x010;
+ }
+
+ if (intr)
+ nv_error(therm, "unhandled intr 0x%08x\n", intr);
+
+ /* ACK everything */
+ nv_wr32(therm, 0x20100, 0xffffffff);
+ nv_wr32(therm, 0x1100, 0x10000); /* PBUS */
+
+ spin_unlock_irqrestore(&priv->sensor.alarm_program_lock, flags);
+}
+
+static int
+g84_therm_init(struct nvkm_object *object)
+{
+ struct g84_therm_priv *priv = (void *)object;
+ int ret;
+
+ ret = nvkm_therm_init(&priv->base.base);
+ if (ret)
+ return ret;
+
+ g84_sensor_setup(&priv->base.base);
+ return 0;
+}
+
+static int
+g84_therm_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 size,
+ struct nvkm_object **pobject)
+{
+ struct g84_therm_priv *priv;
+ int ret;
+
+ ret = nvkm_therm_create(parent, engine, oclass, &priv);
+ *pobject = nv_object(priv);
+ if (ret)
+ return ret;
+
+ priv->base.base.pwm_ctrl = nv50_fan_pwm_ctrl;
+ priv->base.base.pwm_get = nv50_fan_pwm_get;
+ priv->base.base.pwm_set = nv50_fan_pwm_set;
+ priv->base.base.pwm_clock = nv50_fan_pwm_clock;
+ priv->base.base.temp_get = g84_temp_get;
+ priv->base.sensor.program_alarms = g84_therm_program_alarms;
+ nv_subdev(priv)->intr = g84_therm_intr;
+
+ /* init the thresholds */
+ nvkm_therm_sensor_set_threshold_state(&priv->base.base,
+ NVKM_THERM_THRS_SHUTDOWN,
+ NVKM_THERM_THRS_LOWER);
+ nvkm_therm_sensor_set_threshold_state(&priv->base.base,
+ NVKM_THERM_THRS_FANBOOST,
+ NVKM_THERM_THRS_LOWER);
+ nvkm_therm_sensor_set_threshold_state(&priv->base.base,
+ NVKM_THERM_THRS_CRITICAL,
+ NVKM_THERM_THRS_LOWER);
+ nvkm_therm_sensor_set_threshold_state(&priv->base.base,
+ NVKM_THERM_THRS_DOWNCLOCK,
+ NVKM_THERM_THRS_LOWER);
+
+ return nvkm_therm_preinit(&priv->base.base);
+}
+
+int
+g84_therm_fini(struct nvkm_object *object, bool suspend)
+{
+ /* Disable PTherm IRQs */
+ nv_wr32(object, 0x20000, 0x00000000);
+
+ /* ACK all PTherm IRQs */
+ nv_wr32(object, 0x20100, 0xffffffff);
+ nv_wr32(object, 0x1100, 0x10000); /* PBUS */
+
+ return _nvkm_therm_fini(object, suspend);
+}
+
+struct nvkm_oclass
+g84_therm_oclass = {
+ .handle = NV_SUBDEV(THERM, 0x84),
+ .ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = g84_therm_ctor,
+ .dtor = _nvkm_therm_dtor,
+ .init = g84_therm_init,
+ .fini = g84_therm_fini,
+ },
+};
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/gf110.c b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/gf110.c
new file mode 100644
index 000000000000..46b7e656a752
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/gf110.c
@@ -0,0 +1,174 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "priv.h"
+
+#include <core/device.h>
+
+struct gf110_therm_priv {
+ struct nvkm_therm_priv base;
+};
+
+static int
+pwm_info(struct nvkm_therm *therm, int line)
+{
+ u32 gpio = nv_rd32(therm, 0x00d610 + (line * 0x04));
+
+ switch (gpio & 0x000000c0) {
+ case 0x00000000: /* normal mode, possibly pwm forced off by us */
+ case 0x00000040: /* nvio special */
+ switch (gpio & 0x0000001f) {
+ case 0x00: return 2;
+ case 0x19: return 1;
+ case 0x1c: return 0;
+ case 0x1e: return 2;
+ default:
+ break;
+ }
+ default:
+ break;
+ }
+
+ nv_error(therm, "GPIO %d unknown PWM: 0x%08x\n", line, gpio);
+ return -ENODEV;
+}
+
+static int
+gf110_fan_pwm_ctrl(struct nvkm_therm *therm, int line, bool enable)
+{
+ u32 data = enable ? 0x00000040 : 0x00000000;
+ int indx = pwm_info(therm, line);
+ if (indx < 0)
+ return indx;
+ else if (indx < 2)
+ nv_mask(therm, 0x00d610 + (line * 0x04), 0x000000c0, data);
+ /* nothing to do for indx == 2, it seems hardwired to PTHERM */
+ return 0;
+}
+
+static int
+gf110_fan_pwm_get(struct nvkm_therm *therm, int line, u32 *divs, u32 *duty)
+{
+ int indx = pwm_info(therm, line);
+ if (indx < 0)
+ return indx;
+ else if (indx < 2) {
+ if (nv_rd32(therm, 0x00d610 + (line * 0x04)) & 0x00000040) {
+ *divs = nv_rd32(therm, 0x00e114 + (indx * 8));
+ *duty = nv_rd32(therm, 0x00e118 + (indx * 8));
+ return 0;
+ }
+ } else if (indx == 2) {
+ *divs = nv_rd32(therm, 0x0200d8) & 0x1fff;
+ *duty = nv_rd32(therm, 0x0200dc) & 0x1fff;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int
+gf110_fan_pwm_set(struct nvkm_therm *therm, int line, u32 divs, u32 duty)
+{
+ int indx = pwm_info(therm, line);
+ if (indx < 0)
+ return indx;
+ else if (indx < 2) {
+ nv_wr32(therm, 0x00e114 + (indx * 8), divs);
+ nv_wr32(therm, 0x00e118 + (indx * 8), duty | 0x80000000);
+ } else if (indx == 2) {
+ nv_mask(therm, 0x0200d8, 0x1fff, divs); /* keep the high bits */
+ nv_wr32(therm, 0x0200dc, duty | 0x40000000);
+ }
+ return 0;
+}
+
+static int
+gf110_fan_pwm_clock(struct nvkm_therm *therm, int line)
+{
+ int indx = pwm_info(therm, line);
+ if (indx < 0)
+ return 0;
+ else if (indx < 2)
+ return (nv_device(therm)->crystal * 1000) / 20;
+ else
+ return nv_device(therm)->crystal * 1000 / 10;
+}
+
+int
+gf110_therm_init(struct nvkm_object *object)
+{
+ struct gf110_therm_priv *priv = (void *)object;
+ int ret;
+
+ ret = nvkm_therm_init(&priv->base.base);
+ if (ret)
+ return ret;
+
+ /* enable fan tach, count revolutions per-second */
+ nv_mask(priv, 0x00e720, 0x00000003, 0x00000002);
+ if (priv->base.fan->tach.func != DCB_GPIO_UNUSED) {
+ nv_mask(priv, 0x00d79c, 0x000000ff, priv->base.fan->tach.line);
+ nv_wr32(priv, 0x00e724, nv_device(priv)->crystal * 1000);
+ nv_mask(priv, 0x00e720, 0x00000001, 0x00000001);
+ }
+ nv_mask(priv, 0x00e720, 0x00000002, 0x00000000);
+
+ return 0;
+}
+
+static int
+gf110_therm_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 size,
+ struct nvkm_object **pobject)
+{
+ struct gf110_therm_priv *priv;
+ int ret;
+
+ ret = nvkm_therm_create(parent, engine, oclass, &priv);
+ *pobject = nv_object(priv);
+ if (ret)
+ return ret;
+
+ g84_sensor_setup(&priv->base.base);
+
+ priv->base.base.pwm_ctrl = gf110_fan_pwm_ctrl;
+ priv->base.base.pwm_get = gf110_fan_pwm_get;
+ priv->base.base.pwm_set = gf110_fan_pwm_set;
+ priv->base.base.pwm_clock = gf110_fan_pwm_clock;
+ priv->base.base.temp_get = g84_temp_get;
+ priv->base.base.fan_sense = gt215_therm_fan_sense;
+ priv->base.sensor.program_alarms = nvkm_therm_program_alarms_polling;
+ return nvkm_therm_preinit(&priv->base.base);
+}
+
+struct nvkm_oclass
+gf110_therm_oclass = {
+ .handle = NV_SUBDEV(THERM, 0xd0),
+ .ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = gf110_therm_ctor,
+ .dtor = _nvkm_therm_dtor,
+ .init = gf110_therm_init,
+ .fini = g84_therm_fini,
+ },
+};
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/gm107.c b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/gm107.c
new file mode 100644
index 000000000000..2fd110f09878
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/gm107.c
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2014 Martin Peres
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Martin Peres
+ */
+#include "priv.h"
+
+#include <core/device.h>
+
+struct gm107_therm_priv {
+ struct nvkm_therm_priv base;
+};
+
+static int
+gm107_fan_pwm_ctrl(struct nvkm_therm *therm, int line, bool enable)
+{
+ /* nothing to do, it seems hardwired */
+ return 0;
+}
+
+static int
+gm107_fan_pwm_get(struct nvkm_therm *therm, int line, u32 *divs, u32 *duty)
+{
+ *divs = nv_rd32(therm, 0x10eb20) & 0x1fff;
+ *duty = nv_rd32(therm, 0x10eb24) & 0x1fff;
+ return 0;
+}
+
+static int
+gm107_fan_pwm_set(struct nvkm_therm *therm, int line, u32 divs, u32 duty)
+{
+ nv_mask(therm, 0x10eb10, 0x1fff, divs); /* keep the high bits */
+ nv_wr32(therm, 0x10eb14, duty | 0x80000000);
+ return 0;
+}
+
+static int
+gm107_fan_pwm_clock(struct nvkm_therm *therm, int line)
+{
+ return nv_device(therm)->crystal * 1000;
+}
+
+static int
+gm107_therm_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 size,
+ struct nvkm_object **pobject)
+{
+ struct gm107_therm_priv *priv;
+ int ret;
+
+ ret = nvkm_therm_create(parent, engine, oclass, &priv);
+ *pobject = nv_object(priv);
+ if (ret)
+ return ret;
+
+ priv->base.base.pwm_ctrl = gm107_fan_pwm_ctrl;
+ priv->base.base.pwm_get = gm107_fan_pwm_get;
+ priv->base.base.pwm_set = gm107_fan_pwm_set;
+ priv->base.base.pwm_clock = gm107_fan_pwm_clock;
+ priv->base.base.temp_get = g84_temp_get;
+ priv->base.base.fan_sense = gt215_therm_fan_sense;
+ priv->base.sensor.program_alarms = nvkm_therm_program_alarms_polling;
+ return nvkm_therm_preinit(&priv->base.base);
+}
+
+struct nvkm_oclass
+gm107_therm_oclass = {
+ .handle = NV_SUBDEV(THERM, 0x117),
+ .ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = gm107_therm_ctor,
+ .dtor = _nvkm_therm_dtor,
+ .init = gf110_therm_init,
+ .fini = g84_therm_fini,
+ },
+};
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/gt215.c b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/gt215.c
new file mode 100644
index 000000000000..e99be20332f2
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/gt215.c
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "priv.h"
+
+#include <core/device.h>
+#include <subdev/gpio.h>
+
+struct gt215_therm_priv {
+ struct nvkm_therm_priv base;
+};
+
+int
+gt215_therm_fan_sense(struct nvkm_therm *therm)
+{
+ u32 tach = nv_rd32(therm, 0x00e728) & 0x0000ffff;
+ u32 ctrl = nv_rd32(therm, 0x00e720);
+ if (ctrl & 0x00000001)
+ return tach * 60 / 2;
+ return -ENODEV;
+}
+
+static int
+gt215_therm_init(struct nvkm_object *object)
+{
+ struct gt215_therm_priv *priv = (void *)object;
+ struct dcb_gpio_func *tach = &priv->base.fan->tach;
+ int ret;
+
+ ret = nvkm_therm_init(&priv->base.base);
+ if (ret)
+ return ret;
+
+ g84_sensor_setup(&priv->base.base);
+
+ /* enable fan tach, count revolutions per-second */
+ nv_mask(priv, 0x00e720, 0x00000003, 0x00000002);
+ if (tach->func != DCB_GPIO_UNUSED) {
+ nv_wr32(priv, 0x00e724, nv_device(priv)->crystal * 1000);
+ nv_mask(priv, 0x00e720, 0x001f0000, tach->line << 16);
+ nv_mask(priv, 0x00e720, 0x00000001, 0x00000001);
+ }
+ nv_mask(priv, 0x00e720, 0x00000002, 0x00000000);
+
+ return 0;
+}
+
+static int
+gt215_therm_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 size,
+ struct nvkm_object **pobject)
+{
+ struct gt215_therm_priv *priv;
+ int ret;
+
+ ret = nvkm_therm_create(parent, engine, oclass, &priv);
+ *pobject = nv_object(priv);
+ if (ret)
+ return ret;
+
+ priv->base.base.pwm_ctrl = nv50_fan_pwm_ctrl;
+ priv->base.base.pwm_get = nv50_fan_pwm_get;
+ priv->base.base.pwm_set = nv50_fan_pwm_set;
+ priv->base.base.pwm_clock = nv50_fan_pwm_clock;
+ priv->base.base.temp_get = g84_temp_get;
+ priv->base.base.fan_sense = gt215_therm_fan_sense;
+ priv->base.sensor.program_alarms = nvkm_therm_program_alarms_polling;
+ return nvkm_therm_preinit(&priv->base.base);
+}
+
+struct nvkm_oclass
+gt215_therm_oclass = {
+ .handle = NV_SUBDEV(THERM, 0xa3),
+ .ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = gt215_therm_ctor,
+ .dtor = _nvkm_therm_dtor,
+ .init = gt215_therm_init,
+ .fini = g84_therm_fini,
+ },
+};
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/ic.c b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/ic.c
new file mode 100644
index 000000000000..09fc4605e853
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/ic.c
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2012 Nouveau community
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Martin Peres
+ */
+#include "priv.h"
+
+#include <subdev/bios/extdev.h>
+#include <subdev/i2c.h>
+
+static bool
+probe_monitoring_device(struct nvkm_i2c_port *i2c,
+ struct i2c_board_info *info, void *data)
+{
+ struct nvkm_therm_priv *priv = data;
+ struct nvbios_therm_sensor *sensor = &priv->bios_sensor;
+ struct i2c_client *client;
+
+ request_module("%s%s", I2C_MODULE_PREFIX, info->type);
+
+ client = i2c_new_device(&i2c->adapter, info);
+ if (!client)
+ return false;
+
+ if (!client->dev.driver ||
+ to_i2c_driver(client->dev.driver)->detect(client, info)) {
+ i2c_unregister_device(client);
+ return false;
+ }
+
+ nv_info(priv,
+ "Found an %s at address 0x%x (controlled by lm_sensors, "
+ "temp offset %+i C)\n",
+ info->type, info->addr, sensor->offset_constant);
+ priv->ic = client;
+ return true;
+}
+
+static struct nvkm_i2c_board_info
+nv_board_infos[] = {
+ { { I2C_BOARD_INFO("w83l785ts", 0x2d) }, 0 },
+ { { I2C_BOARD_INFO("w83781d", 0x2d) }, 0 },
+ { { I2C_BOARD_INFO("adt7473", 0x2e) }, 40 },
+ { { I2C_BOARD_INFO("adt7473", 0x2d) }, 40 },
+ { { I2C_BOARD_INFO("adt7473", 0x2c) }, 40 },
+ { { I2C_BOARD_INFO("f75375", 0x2e) }, 0 },
+ { { I2C_BOARD_INFO("lm99", 0x4c) }, 0 },
+ { { I2C_BOARD_INFO("lm90", 0x4c) }, 0 },
+ { { I2C_BOARD_INFO("lm90", 0x4d) }, 0 },
+ { { I2C_BOARD_INFO("adm1021", 0x18) }, 0 },
+ { { I2C_BOARD_INFO("adm1021", 0x19) }, 0 },
+ { { I2C_BOARD_INFO("adm1021", 0x1a) }, 0 },
+ { { I2C_BOARD_INFO("adm1021", 0x29) }, 0 },
+ { { I2C_BOARD_INFO("adm1021", 0x2a) }, 0 },
+ { { I2C_BOARD_INFO("adm1021", 0x2b) }, 0 },
+ { { I2C_BOARD_INFO("adm1021", 0x4c) }, 0 },
+ { { I2C_BOARD_INFO("adm1021", 0x4d) }, 0 },
+ { { I2C_BOARD_INFO("adm1021", 0x4e) }, 0 },
+ { { I2C_BOARD_INFO("lm63", 0x18) }, 0 },
+ { { I2C_BOARD_INFO("lm63", 0x4e) }, 0 },
+ { }
+};
+
+void
+nvkm_therm_ic_ctor(struct nvkm_therm *therm)
+{
+ struct nvkm_therm_priv *priv = (void *)therm;
+ struct nvkm_bios *bios = nvkm_bios(therm);
+ struct nvkm_i2c *i2c = nvkm_i2c(therm);
+ struct nvbios_extdev_func extdev_entry;
+
+ if (!nvbios_extdev_find(bios, NVBIOS_EXTDEV_LM89, &extdev_entry)) {
+ struct nvkm_i2c_board_info board[] = {
+ { { I2C_BOARD_INFO("lm90", extdev_entry.addr >> 1) }, 0},
+ { }
+ };
+
+ i2c->identify(i2c, NV_I2C_DEFAULT(0), "monitoring device",
+ board, probe_monitoring_device, therm);
+ if (priv->ic)
+ return;
+ }
+
+ if (!nvbios_extdev_find(bios, NVBIOS_EXTDEV_ADT7473, &extdev_entry)) {
+ struct nvkm_i2c_board_info board[] = {
+ { { I2C_BOARD_INFO("adt7473", extdev_entry.addr >> 1) }, 20 },
+ { }
+ };
+
+ i2c->identify(i2c, NV_I2C_DEFAULT(0), "monitoring device",
+ board, probe_monitoring_device, therm);
+ if (priv->ic)
+ return;
+ }
+
+ /* The vbios doesn't provide the address of an exisiting monitoring
+ device. Let's try our static list.
+ */
+ i2c->identify(i2c, NV_I2C_DEFAULT(0), "monitoring device",
+ nv_board_infos, probe_monitoring_device, therm);
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/nv40.c b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/nv40.c
new file mode 100644
index 000000000000..8496fffd4688
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/nv40.c
@@ -0,0 +1,225 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ * Martin Peres
+ */
+#include "priv.h"
+
+#include <core/device.h>
+
+struct nv40_therm_priv {
+ struct nvkm_therm_priv base;
+};
+
+enum nv40_sensor_style { INVALID_STYLE = -1, OLD_STYLE = 0, NEW_STYLE = 1 };
+
+static enum nv40_sensor_style
+nv40_sensor_style(struct nvkm_therm *therm)
+{
+ struct nvkm_device *device = nv_device(therm);
+
+ switch (device->chipset) {
+ case 0x43:
+ case 0x44:
+ case 0x4a:
+ case 0x47:
+ return OLD_STYLE;
+
+ case 0x46:
+ case 0x49:
+ case 0x4b:
+ case 0x4e:
+ case 0x4c:
+ case 0x67:
+ case 0x68:
+ case 0x63:
+ return NEW_STYLE;
+ default:
+ return INVALID_STYLE;
+ }
+}
+
+static int
+nv40_sensor_setup(struct nvkm_therm *therm)
+{
+ enum nv40_sensor_style style = nv40_sensor_style(therm);
+
+ /* enable ADC readout and disable the ALARM threshold */
+ if (style == NEW_STYLE) {
+ nv_mask(therm, 0x15b8, 0x80000000, 0);
+ nv_wr32(therm, 0x15b0, 0x80003fff);
+ mdelay(20); /* wait for the temperature to stabilize */
+ return nv_rd32(therm, 0x15b4) & 0x3fff;
+ } else if (style == OLD_STYLE) {
+ nv_wr32(therm, 0x15b0, 0xff);
+ mdelay(20); /* wait for the temperature to stabilize */
+ return nv_rd32(therm, 0x15b4) & 0xff;
+ } else
+ return -ENODEV;
+}
+
+static int
+nv40_temp_get(struct nvkm_therm *therm)
+{
+ struct nvkm_therm_priv *priv = (void *)therm;
+ struct nvbios_therm_sensor *sensor = &priv->bios_sensor;
+ enum nv40_sensor_style style = nv40_sensor_style(therm);
+ int core_temp;
+
+ if (style == NEW_STYLE) {
+ nv_wr32(therm, 0x15b0, 0x80003fff);
+ core_temp = nv_rd32(therm, 0x15b4) & 0x3fff;
+ } else if (style == OLD_STYLE) {
+ nv_wr32(therm, 0x15b0, 0xff);
+ core_temp = nv_rd32(therm, 0x15b4) & 0xff;
+ } else
+ return -ENODEV;
+
+ /* if the slope or the offset is unset, do no use the sensor */
+ if (!sensor->slope_div || !sensor->slope_mult ||
+ !sensor->offset_num || !sensor->offset_den)
+ return -ENODEV;
+
+ core_temp = core_temp * sensor->slope_mult / sensor->slope_div;
+ core_temp = core_temp + sensor->offset_num / sensor->offset_den;
+ core_temp = core_temp + sensor->offset_constant - 8;
+
+ /* reserve negative temperatures for errors */
+ if (core_temp < 0)
+ core_temp = 0;
+
+ return core_temp;
+}
+
+static int
+nv40_fan_pwm_ctrl(struct nvkm_therm *therm, int line, bool enable)
+{
+ u32 mask = enable ? 0x80000000 : 0x0000000;
+ if (line == 2) nv_mask(therm, 0x0010f0, 0x80000000, mask);
+ else if (line == 9) nv_mask(therm, 0x0015f4, 0x80000000, mask);
+ else {
+ nv_error(therm, "unknown pwm ctrl for gpio %d\n", line);
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static int
+nv40_fan_pwm_get(struct nvkm_therm *therm, int line, u32 *divs, u32 *duty)
+{
+ if (line == 2) {
+ u32 reg = nv_rd32(therm, 0x0010f0);
+ if (reg & 0x80000000) {
+ *duty = (reg & 0x7fff0000) >> 16;
+ *divs = (reg & 0x00007fff);
+ return 0;
+ }
+ } else
+ if (line == 9) {
+ u32 reg = nv_rd32(therm, 0x0015f4);
+ if (reg & 0x80000000) {
+ *divs = nv_rd32(therm, 0x0015f8);
+ *duty = (reg & 0x7fffffff);
+ return 0;
+ }
+ } else {
+ nv_error(therm, "unknown pwm ctrl for gpio %d\n", line);
+ return -ENODEV;
+ }
+
+ return -EINVAL;
+}
+
+static int
+nv40_fan_pwm_set(struct nvkm_therm *therm, int line, u32 divs, u32 duty)
+{
+ if (line == 2) {
+ nv_mask(therm, 0x0010f0, 0x7fff7fff, (duty << 16) | divs);
+ } else
+ if (line == 9) {
+ nv_wr32(therm, 0x0015f8, divs);
+ nv_mask(therm, 0x0015f4, 0x7fffffff, duty);
+ } else {
+ nv_error(therm, "unknown pwm ctrl for gpio %d\n", line);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+void
+nv40_therm_intr(struct nvkm_subdev *subdev)
+{
+ struct nvkm_therm *therm = nvkm_therm(subdev);
+ uint32_t stat = nv_rd32(therm, 0x1100);
+
+ /* traitement */
+
+ /* ack all IRQs */
+ nv_wr32(therm, 0x1100, 0x70000);
+
+ nv_error(therm, "THERM received an IRQ: stat = %x\n", stat);
+}
+
+static int
+nv40_therm_ctor(struct nvkm_object *parent,
+ struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 size,
+ struct nvkm_object **pobject)
+{
+ struct nv40_therm_priv *priv;
+ int ret;
+
+ ret = nvkm_therm_create(parent, engine, oclass, &priv);
+ *pobject = nv_object(priv);
+ if (ret)
+ return ret;
+
+ priv->base.base.pwm_ctrl = nv40_fan_pwm_ctrl;
+ priv->base.base.pwm_get = nv40_fan_pwm_get;
+ priv->base.base.pwm_set = nv40_fan_pwm_set;
+ priv->base.base.temp_get = nv40_temp_get;
+ priv->base.sensor.program_alarms = nvkm_therm_program_alarms_polling;
+ nv_subdev(priv)->intr = nv40_therm_intr;
+ return nvkm_therm_preinit(&priv->base.base);
+}
+
+static int
+nv40_therm_init(struct nvkm_object *object)
+{
+ struct nvkm_therm *therm = (void *)object;
+
+ nv40_sensor_setup(therm);
+
+ return _nvkm_therm_init(object);
+}
+
+struct nvkm_oclass
+nv40_therm_oclass = {
+ .handle = NV_SUBDEV(THERM, 0x40),
+ .ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv40_therm_ctor,
+ .dtor = _nvkm_therm_dtor,
+ .init = nv40_therm_init,
+ .fini = _nvkm_therm_fini,
+ },
+};
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/nv50.c b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/nv50.c
new file mode 100644
index 000000000000..1ef59e8922d4
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/nv50.c
@@ -0,0 +1,198 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ * Martin Peres
+ */
+#include "priv.h"
+
+#include <core/device.h>
+
+struct nv50_therm_priv {
+ struct nvkm_therm_priv base;
+};
+
+static int
+pwm_info(struct nvkm_therm *therm, int *line, int *ctrl, int *indx)
+{
+ if (*line == 0x04) {
+ *ctrl = 0x00e100;
+ *line = 4;
+ *indx = 0;
+ } else
+ if (*line == 0x09) {
+ *ctrl = 0x00e100;
+ *line = 9;
+ *indx = 1;
+ } else
+ if (*line == 0x10) {
+ *ctrl = 0x00e28c;
+ *line = 0;
+ *indx = 0;
+ } else {
+ nv_error(therm, "unknown pwm ctrl for gpio %d\n", *line);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+int
+nv50_fan_pwm_ctrl(struct nvkm_therm *therm, int line, bool enable)
+{
+ u32 data = enable ? 0x00000001 : 0x00000000;
+ int ctrl, id, ret = pwm_info(therm, &line, &ctrl, &id);
+ if (ret == 0)
+ nv_mask(therm, ctrl, 0x00010001 << line, data << line);
+ return ret;
+}
+
+int
+nv50_fan_pwm_get(struct nvkm_therm *therm, int line, u32 *divs, u32 *duty)
+{
+ int ctrl, id, ret = pwm_info(therm, &line, &ctrl, &id);
+ if (ret)
+ return ret;
+
+ if (nv_rd32(therm, ctrl) & (1 << line)) {
+ *divs = nv_rd32(therm, 0x00e114 + (id * 8));
+ *duty = nv_rd32(therm, 0x00e118 + (id * 8));
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+int
+nv50_fan_pwm_set(struct nvkm_therm *therm, int line, u32 divs, u32 duty)
+{
+ int ctrl, id, ret = pwm_info(therm, &line, &ctrl, &id);
+ if (ret)
+ return ret;
+
+ nv_wr32(therm, 0x00e114 + (id * 8), divs);
+ nv_wr32(therm, 0x00e118 + (id * 8), duty | 0x80000000);
+ return 0;
+}
+
+int
+nv50_fan_pwm_clock(struct nvkm_therm *therm, int line)
+{
+ int chipset = nv_device(therm)->chipset;
+ int crystal = nv_device(therm)->crystal;
+ int pwm_clock;
+
+ /* determine the PWM source clock */
+ if (chipset > 0x50 && chipset < 0x94) {
+ u8 pwm_div = nv_rd32(therm, 0x410c);
+ if (nv_rd32(therm, 0xc040) & 0x800000) {
+ /* Use the HOST clock (100 MHz)
+ * Where does this constant(2.4) comes from? */
+ pwm_clock = (100000000 >> pwm_div) * 10 / 24;
+ } else {
+ /* Where does this constant(20) comes from? */
+ pwm_clock = (crystal * 1000) >> pwm_div;
+ pwm_clock /= 20;
+ }
+ } else {
+ pwm_clock = (crystal * 1000) / 20;
+ }
+
+ return pwm_clock;
+}
+
+static void
+nv50_sensor_setup(struct nvkm_therm *therm)
+{
+ nv_mask(therm, 0x20010, 0x40000000, 0x0);
+ mdelay(20); /* wait for the temperature to stabilize */
+}
+
+static int
+nv50_temp_get(struct nvkm_therm *therm)
+{
+ struct nvkm_therm_priv *priv = (void *)therm;
+ struct nvbios_therm_sensor *sensor = &priv->bios_sensor;
+ int core_temp;
+
+ core_temp = nv_rd32(therm, 0x20014) & 0x3fff;
+
+ /* if the slope or the offset is unset, do no use the sensor */
+ if (!sensor->slope_div || !sensor->slope_mult ||
+ !sensor->offset_num || !sensor->offset_den)
+ return -ENODEV;
+
+ core_temp = core_temp * sensor->slope_mult / sensor->slope_div;
+ core_temp = core_temp + sensor->offset_num / sensor->offset_den;
+ core_temp = core_temp + sensor->offset_constant - 8;
+
+ /* reserve negative temperatures for errors */
+ if (core_temp < 0)
+ core_temp = 0;
+
+ return core_temp;
+}
+
+static int
+nv50_therm_ctor(struct nvkm_object *parent,
+ struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 size,
+ struct nvkm_object **pobject)
+{
+ struct nv50_therm_priv *priv;
+ int ret;
+
+ ret = nvkm_therm_create(parent, engine, oclass, &priv);
+ *pobject = nv_object(priv);
+ if (ret)
+ return ret;
+
+ priv->base.base.pwm_ctrl = nv50_fan_pwm_ctrl;
+ priv->base.base.pwm_get = nv50_fan_pwm_get;
+ priv->base.base.pwm_set = nv50_fan_pwm_set;
+ priv->base.base.pwm_clock = nv50_fan_pwm_clock;
+ priv->base.base.temp_get = nv50_temp_get;
+ priv->base.sensor.program_alarms = nvkm_therm_program_alarms_polling;
+ nv_subdev(priv)->intr = nv40_therm_intr;
+
+ return nvkm_therm_preinit(&priv->base.base);
+}
+
+static int
+nv50_therm_init(struct nvkm_object *object)
+{
+ struct nvkm_therm *therm = (void *)object;
+
+ nv50_sensor_setup(therm);
+
+ return _nvkm_therm_init(object);
+}
+
+struct nvkm_oclass
+nv50_therm_oclass = {
+ .handle = NV_SUBDEV(THERM, 0x50),
+ .ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv50_therm_ctor,
+ .dtor = _nvkm_therm_dtor,
+ .init = nv50_therm_init,
+ .fini = _nvkm_therm_fini,
+ },
+};
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/priv.h b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/priv.h
new file mode 100644
index 000000000000..916a149efe6e
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/priv.h
@@ -0,0 +1,153 @@
+#ifndef __NVTHERM_PRIV_H__
+#define __NVTHERM_PRIV_H__
+/*
+ * Copyright 2012 The Nouveau community
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Martin Peres
+ */
+#include <subdev/therm.h>
+#include <subdev/bios.h>
+#include <subdev/bios/extdev.h>
+#include <subdev/bios/gpio.h>
+#include <subdev/bios/perf.h>
+#include <subdev/bios/therm.h>
+#include <subdev/timer.h>
+
+struct nvkm_fan {
+ struct nvkm_therm *parent;
+ const char *type;
+
+ struct nvbios_therm_fan bios;
+ struct nvbios_perf_fan perf;
+
+ struct nvkm_alarm alarm;
+ spinlock_t lock;
+ int percent;
+
+ int (*get)(struct nvkm_therm *);
+ int (*set)(struct nvkm_therm *, int percent);
+
+ struct dcb_gpio_func tach;
+};
+
+enum nvkm_therm_thrs_direction {
+ NVKM_THERM_THRS_FALLING = 0,
+ NVKM_THERM_THRS_RISING = 1
+};
+
+enum nvkm_therm_thrs_state {
+ NVKM_THERM_THRS_LOWER = 0,
+ NVKM_THERM_THRS_HIGHER = 1
+};
+
+enum nvkm_therm_thrs {
+ NVKM_THERM_THRS_FANBOOST = 0,
+ NVKM_THERM_THRS_DOWNCLOCK = 1,
+ NVKM_THERM_THRS_CRITICAL = 2,
+ NVKM_THERM_THRS_SHUTDOWN = 3,
+ NVKM_THERM_THRS_NR
+};
+
+struct nvkm_therm_priv {
+ struct nvkm_therm base;
+
+ /* automatic thermal management */
+ struct nvkm_alarm alarm;
+ spinlock_t lock;
+ struct nvbios_therm_trip_point *last_trip;
+ int mode;
+ int cstate;
+ int suspend;
+
+ /* bios */
+ struct nvbios_therm_sensor bios_sensor;
+
+ /* fan priv */
+ struct nvkm_fan *fan;
+
+ /* alarms priv */
+ struct {
+ spinlock_t alarm_program_lock;
+ struct nvkm_alarm therm_poll_alarm;
+ enum nvkm_therm_thrs_state alarm_state[NVKM_THERM_THRS_NR];
+ void (*program_alarms)(struct nvkm_therm *);
+ } sensor;
+
+ /* what should be done if the card overheats */
+ struct {
+ void (*downclock)(struct nvkm_therm *, bool active);
+ void (*pause)(struct nvkm_therm *, bool active);
+ } emergency;
+
+ /* ic */
+ struct i2c_client *ic;
+};
+
+int nvkm_therm_fan_mode(struct nvkm_therm *, int mode);
+int nvkm_therm_attr_get(struct nvkm_therm *, enum nvkm_therm_attr_type);
+int nvkm_therm_attr_set(struct nvkm_therm *, enum nvkm_therm_attr_type, int);
+
+void nvkm_therm_ic_ctor(struct nvkm_therm *);
+
+int nvkm_therm_sensor_ctor(struct nvkm_therm *);
+
+int nvkm_therm_fan_ctor(struct nvkm_therm *);
+int nvkm_therm_fan_init(struct nvkm_therm *);
+int nvkm_therm_fan_fini(struct nvkm_therm *, bool suspend);
+int nvkm_therm_fan_get(struct nvkm_therm *);
+int nvkm_therm_fan_set(struct nvkm_therm *, bool now, int percent);
+int nvkm_therm_fan_user_get(struct nvkm_therm *);
+int nvkm_therm_fan_user_set(struct nvkm_therm *, int percent);
+
+int nvkm_therm_fan_sense(struct nvkm_therm *);
+
+int nvkm_therm_preinit(struct nvkm_therm *);
+
+int nvkm_therm_sensor_init(struct nvkm_therm *);
+int nvkm_therm_sensor_fini(struct nvkm_therm *, bool suspend);
+void nvkm_therm_sensor_preinit(struct nvkm_therm *);
+void nvkm_therm_sensor_set_threshold_state(struct nvkm_therm *,
+ enum nvkm_therm_thrs,
+ enum nvkm_therm_thrs_state);
+enum nvkm_therm_thrs_state
+nvkm_therm_sensor_get_threshold_state(struct nvkm_therm *,
+ enum nvkm_therm_thrs);
+void nvkm_therm_sensor_event(struct nvkm_therm *, enum nvkm_therm_thrs,
+ enum nvkm_therm_thrs_direction);
+void nvkm_therm_program_alarms_polling(struct nvkm_therm *);
+
+void nv40_therm_intr(struct nvkm_subdev *);
+int nv50_fan_pwm_ctrl(struct nvkm_therm *, int, bool);
+int nv50_fan_pwm_get(struct nvkm_therm *, int, u32 *, u32 *);
+int nv50_fan_pwm_set(struct nvkm_therm *, int, u32, u32);
+int nv50_fan_pwm_clock(struct nvkm_therm *, int);
+int g84_temp_get(struct nvkm_therm *);
+void g84_sensor_setup(struct nvkm_therm *);
+int g84_therm_fini(struct nvkm_object *, bool suspend);
+
+int gt215_therm_fan_sense(struct nvkm_therm *);
+
+int gf110_therm_init(struct nvkm_object *);
+
+int nvkm_fanpwm_create(struct nvkm_therm *, struct dcb_gpio_func *);
+int nvkm_fantog_create(struct nvkm_therm *, struct dcb_gpio_func *);
+int nvkm_fannil_create(struct nvkm_therm *);
+#endif
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/temp.c b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/temp.c
new file mode 100644
index 000000000000..aa13744f3854
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/temp.c
@@ -0,0 +1,259 @@
+/*
+ * Copyright 2012 The Nouveau community
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Martin Peres
+ */
+#include "priv.h"
+
+static void
+nvkm_therm_temp_set_defaults(struct nvkm_therm *therm)
+{
+ struct nvkm_therm_priv *priv = (void *)therm;
+
+ priv->bios_sensor.offset_constant = 0;
+
+ priv->bios_sensor.thrs_fan_boost.temp = 90;
+ priv->bios_sensor.thrs_fan_boost.hysteresis = 3;
+
+ priv->bios_sensor.thrs_down_clock.temp = 95;
+ priv->bios_sensor.thrs_down_clock.hysteresis = 3;
+
+ priv->bios_sensor.thrs_critical.temp = 105;
+ priv->bios_sensor.thrs_critical.hysteresis = 5;
+
+ priv->bios_sensor.thrs_shutdown.temp = 135;
+ priv->bios_sensor.thrs_shutdown.hysteresis = 5; /*not that it matters */
+}
+
+
+static void
+nvkm_therm_temp_safety_checks(struct nvkm_therm *therm)
+{
+ struct nvkm_therm_priv *priv = (void *)therm;
+ struct nvbios_therm_sensor *s = &priv->bios_sensor;
+
+ /* enforce a minimum hysteresis on thresholds */
+ s->thrs_fan_boost.hysteresis = max_t(u8, s->thrs_fan_boost.hysteresis, 2);
+ s->thrs_down_clock.hysteresis = max_t(u8, s->thrs_down_clock.hysteresis, 2);
+ s->thrs_critical.hysteresis = max_t(u8, s->thrs_critical.hysteresis, 2);
+ s->thrs_shutdown.hysteresis = max_t(u8, s->thrs_shutdown.hysteresis, 2);
+}
+
+/* must be called with alarm_program_lock taken ! */
+void
+nvkm_therm_sensor_set_threshold_state(struct nvkm_therm *therm,
+ enum nvkm_therm_thrs thrs,
+ enum nvkm_therm_thrs_state st)
+{
+ struct nvkm_therm_priv *priv = (void *)therm;
+ priv->sensor.alarm_state[thrs] = st;
+}
+
+/* must be called with alarm_program_lock taken ! */
+enum nvkm_therm_thrs_state
+nvkm_therm_sensor_get_threshold_state(struct nvkm_therm *therm,
+ enum nvkm_therm_thrs thrs)
+{
+ struct nvkm_therm_priv *priv = (void *)therm;
+ return priv->sensor.alarm_state[thrs];
+}
+
+static void
+nv_poweroff_work(struct work_struct *work)
+{
+ orderly_poweroff(true);
+ kfree(work);
+}
+
+void
+nvkm_therm_sensor_event(struct nvkm_therm *therm, enum nvkm_therm_thrs thrs,
+ enum nvkm_therm_thrs_direction dir)
+{
+ struct nvkm_therm_priv *priv = (void *)therm;
+ bool active;
+ const char *thresolds[] = {
+ "fanboost", "downclock", "critical", "shutdown"
+ };
+ int temperature = therm->temp_get(therm);
+
+ if (thrs < 0 || thrs > 3)
+ return;
+
+ if (dir == NVKM_THERM_THRS_FALLING)
+ nv_info(therm, "temperature (%i C) went below the '%s' threshold\n",
+ temperature, thresolds[thrs]);
+ else
+ nv_info(therm, "temperature (%i C) hit the '%s' threshold\n",
+ temperature, thresolds[thrs]);
+
+ active = (dir == NVKM_THERM_THRS_RISING);
+ switch (thrs) {
+ case NVKM_THERM_THRS_FANBOOST:
+ if (active) {
+ nvkm_therm_fan_set(therm, true, 100);
+ nvkm_therm_fan_mode(therm, NVKM_THERM_CTRL_AUTO);
+ }
+ break;
+ case NVKM_THERM_THRS_DOWNCLOCK:
+ if (priv->emergency.downclock)
+ priv->emergency.downclock(therm, active);
+ break;
+ case NVKM_THERM_THRS_CRITICAL:
+ if (priv->emergency.pause)
+ priv->emergency.pause(therm, active);
+ break;
+ case NVKM_THERM_THRS_SHUTDOWN:
+ if (active) {
+ struct work_struct *work;
+
+ work = kmalloc(sizeof(*work), GFP_ATOMIC);
+ if (work) {
+ INIT_WORK(work, nv_poweroff_work);
+ schedule_work(work);
+ }
+ }
+ break;
+ case NVKM_THERM_THRS_NR:
+ break;
+ }
+
+}
+
+/* must be called with alarm_program_lock taken ! */
+static void
+nvkm_therm_threshold_hyst_polling(struct nvkm_therm *therm,
+ const struct nvbios_therm_threshold *thrs,
+ enum nvkm_therm_thrs thrs_name)
+{
+ enum nvkm_therm_thrs_direction direction;
+ enum nvkm_therm_thrs_state prev_state, new_state;
+ int temp = therm->temp_get(therm);
+
+ prev_state = nvkm_therm_sensor_get_threshold_state(therm, thrs_name);
+
+ if (temp >= thrs->temp && prev_state == NVKM_THERM_THRS_LOWER) {
+ direction = NVKM_THERM_THRS_RISING;
+ new_state = NVKM_THERM_THRS_HIGHER;
+ } else if (temp <= thrs->temp - thrs->hysteresis &&
+ prev_state == NVKM_THERM_THRS_HIGHER) {
+ direction = NVKM_THERM_THRS_FALLING;
+ new_state = NVKM_THERM_THRS_LOWER;
+ } else
+ return; /* nothing to do */
+
+ nvkm_therm_sensor_set_threshold_state(therm, thrs_name, new_state);
+ nvkm_therm_sensor_event(therm, thrs_name, direction);
+}
+
+static void
+alarm_timer_callback(struct nvkm_alarm *alarm)
+{
+ struct nvkm_therm_priv *priv =
+ container_of(alarm, struct nvkm_therm_priv, sensor.therm_poll_alarm);
+ struct nvbios_therm_sensor *sensor = &priv->bios_sensor;
+ struct nvkm_timer *ptimer = nvkm_timer(priv);
+ struct nvkm_therm *therm = &priv->base;
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->sensor.alarm_program_lock, flags);
+
+ nvkm_therm_threshold_hyst_polling(therm, &sensor->thrs_fan_boost,
+ NVKM_THERM_THRS_FANBOOST);
+
+ nvkm_therm_threshold_hyst_polling(therm, &sensor->thrs_down_clock,
+ NVKM_THERM_THRS_DOWNCLOCK);
+
+ nvkm_therm_threshold_hyst_polling(therm, &sensor->thrs_critical,
+ NVKM_THERM_THRS_CRITICAL);
+
+ nvkm_therm_threshold_hyst_polling(therm, &sensor->thrs_shutdown,
+ NVKM_THERM_THRS_SHUTDOWN);
+
+ spin_unlock_irqrestore(&priv->sensor.alarm_program_lock, flags);
+
+ /* schedule the next poll in one second */
+ if (therm->temp_get(therm) >= 0 && list_empty(&alarm->head))
+ ptimer->alarm(ptimer, 1000000000ULL, alarm);
+}
+
+void
+nvkm_therm_program_alarms_polling(struct nvkm_therm *therm)
+{
+ struct nvkm_therm_priv *priv = (void *)therm;
+ struct nvbios_therm_sensor *sensor = &priv->bios_sensor;
+
+ nv_debug(therm,
+ "programmed thresholds [ %d(%d), %d(%d), %d(%d), %d(%d) ]\n",
+ sensor->thrs_fan_boost.temp, sensor->thrs_fan_boost.hysteresis,
+ sensor->thrs_down_clock.temp,
+ sensor->thrs_down_clock.hysteresis,
+ sensor->thrs_critical.temp, sensor->thrs_critical.hysteresis,
+ sensor->thrs_shutdown.temp, sensor->thrs_shutdown.hysteresis);
+
+ alarm_timer_callback(&priv->sensor.therm_poll_alarm);
+}
+
+int
+nvkm_therm_sensor_init(struct nvkm_therm *therm)
+{
+ struct nvkm_therm_priv *priv = (void *)therm;
+ priv->sensor.program_alarms(therm);
+ return 0;
+}
+
+int
+nvkm_therm_sensor_fini(struct nvkm_therm *therm, bool suspend)
+{
+ struct nvkm_therm_priv *priv = (void *)therm;
+ struct nvkm_timer *ptimer = nvkm_timer(therm);
+
+ if (suspend)
+ ptimer->alarm_cancel(ptimer, &priv->sensor.therm_poll_alarm);
+ return 0;
+}
+
+void
+nvkm_therm_sensor_preinit(struct nvkm_therm *therm)
+{
+ const char *sensor_avail = "yes";
+
+ if (therm->temp_get(therm) < 0)
+ sensor_avail = "no";
+
+ nv_info(therm, "internal sensor: %s\n", sensor_avail);
+}
+
+int
+nvkm_therm_sensor_ctor(struct nvkm_therm *therm)
+{
+ struct nvkm_therm_priv *priv = (void *)therm;
+ struct nvkm_bios *bios = nvkm_bios(therm);
+
+ nvkm_alarm_init(&priv->sensor.therm_poll_alarm, alarm_timer_callback);
+
+ nvkm_therm_temp_set_defaults(therm);
+ if (nvbios_therm_sensor_parse(bios, NVBIOS_THERM_DOMAIN_CORE,
+ &priv->bios_sensor))
+ nv_error(therm, "nvbios_therm_sensor_parse failed\n");
+ nvkm_therm_temp_safety_checks(therm);
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/timer/Kbuild b/drivers/gpu/drm/nouveau/nvkm/subdev/timer/Kbuild
new file mode 100644
index 000000000000..d1d38b4ba30a
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/timer/Kbuild
@@ -0,0 +1,3 @@
+nvkm-y += nvkm/subdev/timer/base.o
+nvkm-y += nvkm/subdev/timer/nv04.o
+nvkm-y += nvkm/subdev/timer/gk20a.o
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/timer/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/timer/base.c
new file mode 100644
index 000000000000..d894061ced52
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/timer/base.c
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include <subdev/timer.h>
+
+bool
+nvkm_timer_wait_eq(void *obj, u64 nsec, u32 addr, u32 mask, u32 data)
+{
+ struct nvkm_timer *ptimer = nvkm_timer(obj);
+ u64 time0;
+
+ time0 = ptimer->read(ptimer);
+ do {
+ if (nv_iclass(obj, NV_SUBDEV_CLASS)) {
+ if ((nv_rd32(obj, addr) & mask) == data)
+ return true;
+ } else {
+ if ((nv_ro32(obj, addr) & mask) == data)
+ return true;
+ }
+ } while (ptimer->read(ptimer) - time0 < nsec);
+
+ return false;
+}
+
+bool
+nvkm_timer_wait_ne(void *obj, u64 nsec, u32 addr, u32 mask, u32 data)
+{
+ struct nvkm_timer *ptimer = nvkm_timer(obj);
+ u64 time0;
+
+ time0 = ptimer->read(ptimer);
+ do {
+ if (nv_iclass(obj, NV_SUBDEV_CLASS)) {
+ if ((nv_rd32(obj, addr) & mask) != data)
+ return true;
+ } else {
+ if ((nv_ro32(obj, addr) & mask) != data)
+ return true;
+ }
+ } while (ptimer->read(ptimer) - time0 < nsec);
+
+ return false;
+}
+
+bool
+nvkm_timer_wait_cb(void *obj, u64 nsec, bool (*func)(void *), void *data)
+{
+ struct nvkm_timer *ptimer = nvkm_timer(obj);
+ u64 time0;
+
+ time0 = ptimer->read(ptimer);
+ do {
+ if (func(data) == true)
+ return true;
+ } while (ptimer->read(ptimer) - time0 < nsec);
+
+ return false;
+}
+
+void
+nvkm_timer_alarm(void *obj, u32 nsec, struct nvkm_alarm *alarm)
+{
+ struct nvkm_timer *ptimer = nvkm_timer(obj);
+ ptimer->alarm(ptimer, nsec, alarm);
+}
+
+void
+nvkm_timer_alarm_cancel(void *obj, struct nvkm_alarm *alarm)
+{
+ struct nvkm_timer *ptimer = nvkm_timer(obj);
+ ptimer->alarm_cancel(ptimer, alarm);
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/timer/gk20a.c b/drivers/gpu/drm/nouveau/nvkm/subdev/timer/gk20a.c
new file mode 100644
index 000000000000..80e38063dd9b
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/timer/gk20a.c
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "nv04.h"
+
+static int
+gk20a_timer_init(struct nvkm_object *object)
+{
+ struct nv04_timer_priv *priv = (void *)object;
+ u32 hi = upper_32_bits(priv->suspend_time);
+ u32 lo = lower_32_bits(priv->suspend_time);
+ int ret;
+
+ ret = nvkm_timer_init(&priv->base);
+ if (ret)
+ return ret;
+
+ nv_debug(priv, "time low : 0x%08x\n", lo);
+ nv_debug(priv, "time high : 0x%08x\n", hi);
+
+ /* restore the time before suspend */
+ nv_wr32(priv, NV04_PTIMER_TIME_1, hi);
+ nv_wr32(priv, NV04_PTIMER_TIME_0, lo);
+ return 0;
+}
+
+struct nvkm_oclass
+gk20a_timer_oclass = {
+ .handle = NV_SUBDEV(TIMER, 0xff),
+ .ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv04_timer_ctor,
+ .dtor = nv04_timer_dtor,
+ .init = gk20a_timer_init,
+ .fini = nv04_timer_fini,
+ }
+};
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/timer/nv04.c b/drivers/gpu/drm/nouveau/nvkm/subdev/timer/nv04.c
new file mode 100644
index 000000000000..6b7facbe59a2
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/timer/nv04.c
@@ -0,0 +1,262 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "nv04.h"
+
+#include <core/device.h>
+
+static u64
+nv04_timer_read(struct nvkm_timer *ptimer)
+{
+ struct nv04_timer_priv *priv = (void *)ptimer;
+ u32 hi, lo;
+
+ do {
+ hi = nv_rd32(priv, NV04_PTIMER_TIME_1);
+ lo = nv_rd32(priv, NV04_PTIMER_TIME_0);
+ } while (hi != nv_rd32(priv, NV04_PTIMER_TIME_1));
+
+ return ((u64)hi << 32 | lo);
+}
+
+static void
+nv04_timer_alarm_trigger(struct nvkm_timer *ptimer)
+{
+ struct nv04_timer_priv *priv = (void *)ptimer;
+ struct nvkm_alarm *alarm, *atemp;
+ unsigned long flags;
+ LIST_HEAD(exec);
+
+ /* move any due alarms off the pending list */
+ spin_lock_irqsave(&priv->lock, flags);
+ list_for_each_entry_safe(alarm, atemp, &priv->alarms, head) {
+ if (alarm->timestamp <= ptimer->read(ptimer))
+ list_move_tail(&alarm->head, &exec);
+ }
+
+ /* reschedule interrupt for next alarm time */
+ if (!list_empty(&priv->alarms)) {
+ alarm = list_first_entry(&priv->alarms, typeof(*alarm), head);
+ nv_wr32(priv, NV04_PTIMER_ALARM_0, alarm->timestamp);
+ nv_wr32(priv, NV04_PTIMER_INTR_EN_0, 0x00000001);
+ } else {
+ nv_wr32(priv, NV04_PTIMER_INTR_EN_0, 0x00000000);
+ }
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ /* execute any pending alarm handlers */
+ list_for_each_entry_safe(alarm, atemp, &exec, head) {
+ list_del_init(&alarm->head);
+ alarm->func(alarm);
+ }
+}
+
+static void
+nv04_timer_alarm(struct nvkm_timer *ptimer, u64 time, struct nvkm_alarm *alarm)
+{
+ struct nv04_timer_priv *priv = (void *)ptimer;
+ struct nvkm_alarm *list;
+ unsigned long flags;
+
+ alarm->timestamp = ptimer->read(ptimer) + time;
+
+ /* append new alarm to list, in soonest-alarm-first order */
+ spin_lock_irqsave(&priv->lock, flags);
+ if (!time) {
+ if (!list_empty(&alarm->head))
+ list_del(&alarm->head);
+ } else {
+ list_for_each_entry(list, &priv->alarms, head) {
+ if (list->timestamp > alarm->timestamp)
+ break;
+ }
+ list_add_tail(&alarm->head, &list->head);
+ }
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ /* process pending alarms */
+ nv04_timer_alarm_trigger(ptimer);
+}
+
+static void
+nv04_timer_alarm_cancel(struct nvkm_timer *ptimer, struct nvkm_alarm *alarm)
+{
+ struct nv04_timer_priv *priv = (void *)ptimer;
+ unsigned long flags;
+ spin_lock_irqsave(&priv->lock, flags);
+ list_del_init(&alarm->head);
+ spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+static void
+nv04_timer_intr(struct nvkm_subdev *subdev)
+{
+ struct nv04_timer_priv *priv = (void *)subdev;
+ u32 stat = nv_rd32(priv, NV04_PTIMER_INTR_0);
+
+ if (stat & 0x00000001) {
+ nv04_timer_alarm_trigger(&priv->base);
+ nv_wr32(priv, NV04_PTIMER_INTR_0, 0x00000001);
+ stat &= ~0x00000001;
+ }
+
+ if (stat) {
+ nv_error(priv, "unknown stat 0x%08x\n", stat);
+ nv_wr32(priv, NV04_PTIMER_INTR_0, stat);
+ }
+}
+
+int
+nv04_timer_fini(struct nvkm_object *object, bool suspend)
+{
+ struct nv04_timer_priv *priv = (void *)object;
+ if (suspend)
+ priv->suspend_time = nv04_timer_read(&priv->base);
+ nv_wr32(priv, NV04_PTIMER_INTR_EN_0, 0x00000000);
+ return nvkm_timer_fini(&priv->base, suspend);
+}
+
+static int
+nv04_timer_init(struct nvkm_object *object)
+{
+ struct nvkm_device *device = nv_device(object);
+ struct nv04_timer_priv *priv = (void *)object;
+ u32 m = 1, f, n, d, lo, hi;
+ int ret;
+
+ ret = nvkm_timer_init(&priv->base);
+ if (ret)
+ return ret;
+
+ /* aim for 31.25MHz, which gives us nanosecond timestamps */
+ d = 1000000 / 32;
+
+ /* determine base clock for timer source */
+#if 0 /*XXX*/
+ if (device->chipset < 0x40) {
+ n = nvkm_hw_get_clock(device, PLL_CORE);
+ } else
+#endif
+ if (device->chipset <= 0x40) {
+ /*XXX: figure this out */
+ f = -1;
+ n = 0;
+ } else {
+ f = device->crystal;
+ n = f;
+ while (n < (d * 2)) {
+ n += (n / m);
+ m++;
+ }
+
+ nv_wr32(priv, 0x009220, m - 1);
+ }
+
+ if (!n) {
+ nv_warn(priv, "unknown input clock freq\n");
+ if (!nv_rd32(priv, NV04_PTIMER_NUMERATOR) ||
+ !nv_rd32(priv, NV04_PTIMER_DENOMINATOR)) {
+ nv_wr32(priv, NV04_PTIMER_NUMERATOR, 1);
+ nv_wr32(priv, NV04_PTIMER_DENOMINATOR, 1);
+ }
+ return 0;
+ }
+
+ /* reduce ratio to acceptable values */
+ while (((n % 5) == 0) && ((d % 5) == 0)) {
+ n /= 5;
+ d /= 5;
+ }
+
+ while (((n % 2) == 0) && ((d % 2) == 0)) {
+ n /= 2;
+ d /= 2;
+ }
+
+ while (n > 0xffff || d > 0xffff) {
+ n >>= 1;
+ d >>= 1;
+ }
+
+ /* restore the time before suspend */
+ lo = priv->suspend_time;
+ hi = (priv->suspend_time >> 32);
+
+ nv_debug(priv, "input frequency : %dHz\n", f);
+ nv_debug(priv, "input multiplier: %d\n", m);
+ nv_debug(priv, "numerator : 0x%08x\n", n);
+ nv_debug(priv, "denominator : 0x%08x\n", d);
+ nv_debug(priv, "timer frequency : %dHz\n", (f * m) * d / n);
+ nv_debug(priv, "time low : 0x%08x\n", lo);
+ nv_debug(priv, "time high : 0x%08x\n", hi);
+
+ nv_wr32(priv, NV04_PTIMER_NUMERATOR, n);
+ nv_wr32(priv, NV04_PTIMER_DENOMINATOR, d);
+ nv_wr32(priv, NV04_PTIMER_INTR_0, 0xffffffff);
+ nv_wr32(priv, NV04_PTIMER_INTR_EN_0, 0x00000000);
+ nv_wr32(priv, NV04_PTIMER_TIME_1, hi);
+ nv_wr32(priv, NV04_PTIMER_TIME_0, lo);
+ return 0;
+}
+
+void
+nv04_timer_dtor(struct nvkm_object *object)
+{
+ struct nv04_timer_priv *priv = (void *)object;
+ return nvkm_timer_destroy(&priv->base);
+}
+
+int
+nv04_timer_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 size,
+ struct nvkm_object **pobject)
+{
+ struct nv04_timer_priv *priv;
+ int ret;
+
+ ret = nvkm_timer_create(parent, engine, oclass, &priv);
+ *pobject = nv_object(priv);
+ if (ret)
+ return ret;
+
+ priv->base.base.intr = nv04_timer_intr;
+ priv->base.read = nv04_timer_read;
+ priv->base.alarm = nv04_timer_alarm;
+ priv->base.alarm_cancel = nv04_timer_alarm_cancel;
+ priv->suspend_time = 0;
+
+ INIT_LIST_HEAD(&priv->alarms);
+ spin_lock_init(&priv->lock);
+ return 0;
+}
+
+struct nvkm_oclass
+nv04_timer_oclass = {
+ .handle = NV_SUBDEV(TIMER, 0x04),
+ .ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv04_timer_ctor,
+ .dtor = nv04_timer_dtor,
+ .init = nv04_timer_init,
+ .fini = nv04_timer_fini,
+ }
+};
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/timer/nv04.h b/drivers/gpu/drm/nouveau/nvkm/subdev/timer/nv04.h
new file mode 100644
index 000000000000..89996a9826b1
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/timer/nv04.h
@@ -0,0 +1,25 @@
+#ifndef __NVKM_TIMER_NV04_H__
+#define __NVKM_TIMER_NV04_H__
+#include "priv.h"
+
+#define NV04_PTIMER_INTR_0 0x009100
+#define NV04_PTIMER_INTR_EN_0 0x009140
+#define NV04_PTIMER_NUMERATOR 0x009200
+#define NV04_PTIMER_DENOMINATOR 0x009210
+#define NV04_PTIMER_TIME_0 0x009400
+#define NV04_PTIMER_TIME_1 0x009410
+#define NV04_PTIMER_ALARM_0 0x009420
+
+struct nv04_timer_priv {
+ struct nvkm_timer base;
+ struct list_head alarms;
+ spinlock_t lock;
+ u64 suspend_time;
+};
+
+int nv04_timer_ctor(struct nvkm_object *, struct nvkm_object *,
+ struct nvkm_oclass *, void *, u32,
+ struct nvkm_object **);
+void nv04_timer_dtor(struct nvkm_object *);
+int nv04_timer_fini(struct nvkm_object *, bool);
+#endif
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/timer/priv.h b/drivers/gpu/drm/nouveau/nvkm/subdev/timer/priv.h
new file mode 100644
index 000000000000..08e29a3da188
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/timer/priv.h
@@ -0,0 +1,4 @@
+#ifndef __NVKM_TIMER_PRIV_H__
+#define __NVKM_TIMER_PRIV_H__
+#include <subdev/timer.h>
+#endif
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/volt/Kbuild b/drivers/gpu/drm/nouveau/nvkm/subdev/volt/Kbuild
new file mode 100644
index 000000000000..6b46ff4213a3
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/volt/Kbuild
@@ -0,0 +1,4 @@
+nvkm-y += nvkm/subdev/volt/base.o
+nvkm-y += nvkm/subdev/volt/gpio.o
+nvkm-y += nvkm/subdev/volt/nv40.o
+nvkm-y += nvkm/subdev/volt/gk20a.o
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/volt/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/volt/base.c
new file mode 100644
index 000000000000..39f15803f2d4
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/volt/base.c
@@ -0,0 +1,204 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include <subdev/volt.h>
+#include <subdev/bios.h>
+#include <subdev/bios/vmap.h>
+#include <subdev/bios/volt.h>
+
+static int
+nvkm_volt_get(struct nvkm_volt *volt)
+{
+ if (volt->vid_get) {
+ int ret = volt->vid_get(volt), i;
+ if (ret >= 0) {
+ for (i = 0; i < volt->vid_nr; i++) {
+ if (volt->vid[i].vid == ret)
+ return volt->vid[i].uv;
+ }
+ ret = -EINVAL;
+ }
+ return ret;
+ }
+ return -ENODEV;
+}
+
+static int
+nvkm_volt_set(struct nvkm_volt *volt, u32 uv)
+{
+ if (volt->vid_set) {
+ int i, ret = -EINVAL;
+ for (i = 0; i < volt->vid_nr; i++) {
+ if (volt->vid[i].uv == uv) {
+ ret = volt->vid_set(volt, volt->vid[i].vid);
+ nv_debug(volt, "set %duv: %d\n", uv, ret);
+ break;
+ }
+ }
+ return ret;
+ }
+ return -ENODEV;
+}
+
+static int
+nvkm_volt_map(struct nvkm_volt *volt, u8 id)
+{
+ struct nvkm_bios *bios = nvkm_bios(volt);
+ struct nvbios_vmap_entry info;
+ u8 ver, len;
+ u16 vmap;
+
+ vmap = nvbios_vmap_entry_parse(bios, id, &ver, &len, &info);
+ if (vmap) {
+ if (info.link != 0xff) {
+ int ret = nvkm_volt_map(volt, info.link);
+ if (ret < 0)
+ return ret;
+ info.min += ret;
+ }
+ return info.min;
+ }
+
+ return id ? id * 10000 : -ENODEV;
+}
+
+static int
+nvkm_volt_set_id(struct nvkm_volt *volt, u8 id, int condition)
+{
+ int ret = nvkm_volt_map(volt, id);
+ if (ret >= 0) {
+ int prev = nvkm_volt_get(volt);
+ if (!condition || prev < 0 ||
+ (condition < 0 && ret < prev) ||
+ (condition > 0 && ret > prev)) {
+ ret = nvkm_volt_set(volt, ret);
+ } else {
+ ret = 0;
+ }
+ }
+ return ret;
+}
+
+static void
+nvkm_volt_parse_bios(struct nvkm_bios *bios, struct nvkm_volt *volt)
+{
+ struct nvbios_volt_entry ivid;
+ struct nvbios_volt info;
+ u8 ver, hdr, cnt, len;
+ u16 data;
+ int i;
+
+ data = nvbios_volt_parse(bios, &ver, &hdr, &cnt, &len, &info);
+ if (data && info.vidmask && info.base && info.step) {
+ for (i = 0; i < info.vidmask + 1; i++) {
+ if (info.base >= info.min &&
+ info.base <= info.max) {
+ volt->vid[volt->vid_nr].uv = info.base;
+ volt->vid[volt->vid_nr].vid = i;
+ volt->vid_nr++;
+ }
+ info.base += info.step;
+ }
+ volt->vid_mask = info.vidmask;
+ } else if (data && info.vidmask) {
+ for (i = 0; i < cnt; i++) {
+ data = nvbios_volt_entry_parse(bios, i, &ver, &hdr,
+ &ivid);
+ if (data) {
+ volt->vid[volt->vid_nr].uv = ivid.voltage;
+ volt->vid[volt->vid_nr].vid = ivid.vid;
+ volt->vid_nr++;
+ }
+ }
+ volt->vid_mask = info.vidmask;
+ }
+}
+
+int
+_nvkm_volt_init(struct nvkm_object *object)
+{
+ struct nvkm_volt *volt = (void *)object;
+ int ret;
+
+ ret = nvkm_subdev_init(&volt->base);
+ if (ret)
+ return ret;
+
+ ret = volt->get(volt);
+ if (ret < 0) {
+ if (ret != -ENODEV)
+ nv_debug(volt, "current voltage unknown\n");
+ return 0;
+ }
+
+ nv_info(volt, "GPU voltage: %duv\n", ret);
+ return 0;
+}
+
+void
+_nvkm_volt_dtor(struct nvkm_object *object)
+{
+ struct nvkm_volt *volt = (void *)object;
+ nvkm_subdev_destroy(&volt->base);
+}
+
+int
+nvkm_volt_create_(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, int length, void **pobject)
+{
+ struct nvkm_bios *bios = nvkm_bios(parent);
+ struct nvkm_volt *volt;
+ int ret, i;
+
+ ret = nvkm_subdev_create_(parent, engine, oclass, 0, "VOLT",
+ "voltage", length, pobject);
+ volt = *pobject;
+ if (ret)
+ return ret;
+
+ volt->get = nvkm_volt_get;
+ volt->set = nvkm_volt_set;
+ volt->set_id = nvkm_volt_set_id;
+
+ /* Assuming the non-bios device should build the voltage table later */
+ if (bios)
+ nvkm_volt_parse_bios(bios, volt);
+
+ if (volt->vid_nr) {
+ for (i = 0; i < volt->vid_nr; i++) {
+ nv_debug(volt, "VID %02x: %duv\n",
+ volt->vid[i].vid, volt->vid[i].uv);
+ }
+
+ /*XXX: this is an assumption.. there probably exists boards
+ * out there with i2c-connected voltage controllers too..
+ */
+ ret = nvkm_voltgpio_init(volt);
+ if (ret == 0) {
+ volt->vid_get = nvkm_voltgpio_get;
+ volt->vid_set = nvkm_voltgpio_set;
+ }
+ }
+
+ return ret;
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/volt/gk20a.c b/drivers/gpu/drm/nouveau/nvkm/subdev/volt/gk20a.c
new file mode 100644
index 000000000000..871fd51011db
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/volt/gk20a.c
@@ -0,0 +1,197 @@
+/*
+ * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+#include <subdev/volt.h>
+#ifdef __KERNEL__
+#include <nouveau_platform.h>
+#endif
+
+struct cvb_coef {
+ int c0;
+ int c1;
+ int c2;
+ int c3;
+ int c4;
+ int c5;
+};
+
+struct gk20a_volt_priv {
+ struct nvkm_volt base;
+ struct regulator *vdd;
+};
+
+const struct cvb_coef gk20a_cvb_coef[] = {
+ /* MHz, c0, c1, c2, c3, c4, c5 */
+ /* 72 */ { 1209886, -36468, 515, 417, -13123, 203},
+ /* 108 */ { 1130804, -27659, 296, 298, -10834, 221},
+ /* 180 */ { 1162871, -27110, 247, 238, -10681, 268},
+ /* 252 */ { 1220458, -28654, 247, 179, -10376, 298},
+ /* 324 */ { 1280953, -30204, 247, 119, -9766, 304},
+ /* 396 */ { 1344547, -31777, 247, 119, -8545, 292},
+ /* 468 */ { 1420168, -34227, 269, 60, -7172, 256},
+ /* 540 */ { 1490757, -35955, 274, 60, -5188, 197},
+ /* 612 */ { 1599112, -42583, 398, 0, -1831, 119},
+ /* 648 */ { 1366986, -16459, -274, 0, -3204, 72},
+ /* 684 */ { 1391884, -17078, -274, -60, -1526, 30},
+ /* 708 */ { 1415522, -17497, -274, -60, -458, 0},
+ /* 756 */ { 1464061, -18331, -274, -119, 1831, -72},
+ /* 804 */ { 1524225, -20064, -254, -119, 4272, -155},
+ /* 852 */ { 1608418, -21643, -269, 0, 763, -48},
+};
+
+/**
+ * cvb_mv = ((c2 * speedo / s_scale + c1) * speedo / s_scale + c0)
+ */
+static inline int
+gk20a_volt_get_cvb_voltage(int speedo, int s_scale, const struct cvb_coef *coef)
+{
+ int mv;
+
+ mv = DIV_ROUND_CLOSEST(coef->c2 * speedo, s_scale);
+ mv = DIV_ROUND_CLOSEST((mv + coef->c1) * speedo, s_scale) + coef->c0;
+ return mv;
+}
+
+/**
+ * cvb_t_mv =
+ * ((c2 * speedo / s_scale + c1) * speedo / s_scale + c0) +
+ * ((c3 * speedo / s_scale + c4 + c5 * T / t_scale) * T / t_scale)
+ */
+static inline int
+gk20a_volt_get_cvb_t_voltage(int speedo, int temp, int s_scale, int t_scale,
+ const struct cvb_coef *coef)
+{
+ int cvb_mv, mv;
+
+ cvb_mv = gk20a_volt_get_cvb_voltage(speedo, s_scale, coef);
+
+ mv = DIV_ROUND_CLOSEST(coef->c3 * speedo, s_scale) + coef->c4 +
+ DIV_ROUND_CLOSEST(coef->c5 * temp, t_scale);
+ mv = DIV_ROUND_CLOSEST(mv * temp, t_scale) + cvb_mv;
+ return mv;
+}
+
+static int
+gk20a_volt_calc_voltage(const struct cvb_coef *coef, int speedo)
+{
+ int mv;
+
+ mv = gk20a_volt_get_cvb_t_voltage(speedo, -10, 100, 10, coef);
+ mv = DIV_ROUND_UP(mv, 1000);
+
+ return mv * 1000;
+}
+
+static int
+gk20a_volt_vid_get(struct nvkm_volt *volt)
+{
+ struct gk20a_volt_priv *priv = (void *)volt;
+ int i, uv;
+
+ uv = regulator_get_voltage(priv->vdd);
+
+ for (i = 0; i < volt->vid_nr; i++)
+ if (volt->vid[i].uv >= uv)
+ return i;
+
+ return -EINVAL;
+}
+
+static int
+gk20a_volt_vid_set(struct nvkm_volt *volt, u8 vid)
+{
+ struct gk20a_volt_priv *priv = (void *)volt;
+
+ nv_debug(volt, "set voltage as %duv\n", volt->vid[vid].uv);
+ return regulator_set_voltage(priv->vdd, volt->vid[vid].uv, 1200000);
+}
+
+static int
+gk20a_volt_set_id(struct nvkm_volt *volt, u8 id, int condition)
+{
+ struct gk20a_volt_priv *priv = (void *)volt;
+ int prev_uv = regulator_get_voltage(priv->vdd);
+ int target_uv = volt->vid[id].uv;
+ int ret;
+
+ nv_debug(volt, "prev=%d, target=%d, condition=%d\n",
+ prev_uv, target_uv, condition);
+ if (!condition ||
+ (condition < 0 && target_uv < prev_uv) ||
+ (condition > 0 && target_uv > prev_uv)) {
+ ret = gk20a_volt_vid_set(volt, volt->vid[id].vid);
+ } else {
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static int
+gk20a_volt_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 size,
+ struct nvkm_object **pobject)
+{
+ struct gk20a_volt_priv *priv;
+ struct nvkm_volt *volt;
+ struct nouveau_platform_device *plat;
+ int i, ret, uv;
+
+ ret = nvkm_volt_create(parent, engine, oclass, &priv);
+ *pobject = nv_object(priv);
+ if (ret)
+ return ret;
+
+ volt = &priv->base;
+
+ plat = nv_device_to_platform(nv_device(parent));
+
+ uv = regulator_get_voltage(plat->gpu->vdd);
+ nv_info(priv, "The default voltage is %duV\n", uv);
+
+ priv->vdd = plat->gpu->vdd;
+ priv->base.vid_get = gk20a_volt_vid_get;
+ priv->base.vid_set = gk20a_volt_vid_set;
+ priv->base.set_id = gk20a_volt_set_id;
+
+ volt->vid_nr = ARRAY_SIZE(gk20a_cvb_coef);
+ nv_debug(priv, "%s - vid_nr = %d\n", __func__, volt->vid_nr);
+ for (i = 0; i < volt->vid_nr; i++) {
+ volt->vid[i].vid = i;
+ volt->vid[i].uv = gk20a_volt_calc_voltage(&gk20a_cvb_coef[i],
+ plat->gpu_speedo);
+ nv_debug(priv, "%2d: vid=%d, uv=%d\n", i, volt->vid[i].vid,
+ volt->vid[i].uv);
+ }
+
+ return 0;
+}
+
+struct nvkm_oclass
+gk20a_volt_oclass = {
+ .handle = NV_SUBDEV(VOLT, 0xea),
+ .ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = gk20a_volt_ctor,
+ .dtor = _nvkm_volt_dtor,
+ .init = _nvkm_volt_init,
+ .fini = _nvkm_volt_fini,
+ },
+};
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/volt/gpio.c b/drivers/gpu/drm/nouveau/nvkm/subdev/volt/gpio.c
new file mode 100644
index 000000000000..b778deb32d93
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/volt/gpio.c
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include <subdev/volt.h>
+#include <subdev/bios.h>
+#include <subdev/bios/gpio.h>
+#include <subdev/gpio.h>
+
+static const u8 tags[] = {
+ DCB_GPIO_VID0, DCB_GPIO_VID1, DCB_GPIO_VID2, DCB_GPIO_VID3,
+ DCB_GPIO_VID4, DCB_GPIO_VID5, DCB_GPIO_VID6, DCB_GPIO_VID7,
+};
+
+int
+nvkm_voltgpio_get(struct nvkm_volt *volt)
+{
+ struct nvkm_gpio *gpio = nvkm_gpio(volt);
+ u8 vid = 0;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(tags); i++) {
+ if (volt->vid_mask & (1 << i)) {
+ int ret = gpio->get(gpio, 0, tags[i], 0xff);
+ if (ret < 0)
+ return ret;
+ vid |= ret << i;
+ }
+ }
+
+ return vid;
+}
+
+int
+nvkm_voltgpio_set(struct nvkm_volt *volt, u8 vid)
+{
+ struct nvkm_gpio *gpio = nvkm_gpio(volt);
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(tags); i++, vid >>= 1) {
+ if (volt->vid_mask & (1 << i)) {
+ int ret = gpio->set(gpio, 0, tags[i], 0xff, vid & 1);
+ if (ret < 0)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+int
+nvkm_voltgpio_init(struct nvkm_volt *volt)
+{
+ struct nvkm_gpio *gpio = nvkm_gpio(volt);
+ struct dcb_gpio_func func;
+ int i;
+
+ /* check we have gpio function info for each vid bit. on some
+ * boards (ie. nvs295) the vid mask has more bits than there
+ * are valid gpio functions... from traces, nvidia appear to
+ * just touch the existing ones, so let's mask off the invalid
+ * bits and continue with life
+ */
+ for (i = 0; i < ARRAY_SIZE(tags); i++) {
+ if (volt->vid_mask & (1 << i)) {
+ int ret = gpio->find(gpio, 0, tags[i], 0xff, &func);
+ if (ret) {
+ if (ret != -ENOENT)
+ return ret;
+ nv_debug(volt, "VID bit %d has no GPIO\n", i);
+ volt->vid_mask &= ~(1 << i);
+ }
+ }
+ }
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/volt/nv40.c b/drivers/gpu/drm/nouveau/nvkm/subdev/volt/nv40.c
new file mode 100644
index 000000000000..0ac5a3f8c9a8
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/volt/nv40.c
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include <subdev/volt.h>
+
+struct nv40_volt_priv {
+ struct nvkm_volt base;
+};
+
+static int
+nv40_volt_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
+ struct nvkm_oclass *oclass, void *data, u32 size,
+ struct nvkm_object **pobject)
+{
+ struct nv40_volt_priv *priv;
+ int ret;
+
+ ret = nvkm_volt_create(parent, engine, oclass, &priv);
+ *pobject = nv_object(priv);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+struct nvkm_oclass
+nv40_volt_oclass = {
+ .handle = NV_SUBDEV(VOLT, 0x40),
+ .ofuncs = &(struct nvkm_ofuncs) {
+ .ctor = nv40_volt_ctor,
+ .dtor = _nvkm_volt_dtor,
+ .init = _nvkm_volt_init,
+ .fini = _nvkm_volt_fini,
+ },
+};